Samara Portal Technology, Computers

Самарский портал "Технологии, компьютеры"

Галопом по вычислительным Европам. Часть 3. Оптимизация.

Сага о голодных птенцах, или Наличные наше всё

Нет, дедушка еще не сошел с ума, просто состояние ядер современных высокопроизводительных процессоров в отношении данных больше всего напоминает состояние вечно голодных птенцов, а наличные — всего лишь буквальный перевод слова cash (cache). Собственно, о кэшировании я и хочу поговорить. Когда компьютеры только появились — все было хорошо. Процессоры (и микро- и еще до того, как они стали микро) работали на одной частоте с памятью и росли они в быстродействии совершенно параллельно. Пока процессор внутри себя десятки и сотни тактов пережевывал команды (помните про IPC/CPI i8086?) — недорогая динамическая PM/FPM память вполне успевала регенерировать заряд в ячейках и была готова к следующему запросу на чтение или запись.

Проблема (в мире PC) возникла с появлением высокопроизводительного (в 3-6 раз быстрее 8086) процессора i286, с раздельными[1] шинами адреса и данных, частота следования запросов к оперативной памяти от которого выросла пропорционально росту производительности и уже превышала возможности системной памяти к обработке запросов — что привело к тому, что исполнение команд новым мощным процессором вынужденно прерывалось на время доставки затребованных данных и выгрузки результатов. Т.е., у нас появляются простои ядра, обусловленные внешними по отношению к процессору причинами. Процессор же i386 с 32-разрядными операндами нанес следующий удар: еще раз удвоил потребление данных. Что делать? Известное дело: подсмотреть у «старших братьев».

Высокопроизводительные системы давным-давно использовали статическую память, вполне успевавшую работать с процессорами такт-в-такт. Но она была дорогой и горячей (подробнее в следующей главе), поэтому просто заменить DRAM на SRAM не получилось. Решение оказалось, как обычно, посередине: между основным объемом DRAM и процессором поместились несколько микросхем[2] SRAM, работавших в качестве промежуточного буфера, позволявшего мгновенное (в каждом такте) повторное чтение попавших в него данных, а при включении режима обратной (отложенной) записи и такую же моментальную запись результата вычислений. И все бы хорошо, но чисто для понимания: когда типичный объем оперативной памяти на PC был 1 мегабайт — кэш (если он был!) имел размер 64 килобайта, редко 128, для систем с процессором i386DX и 4 мегабайтами ОЗУ — 128-256 KB, и то 256 далеко не всегда. Дешевые же компьютеры часто продавались, вообще, с пустыми панельками под микросхемы кэша.

И это несмотря на то, что эффект от наличия кэша был, в некоторых случаях, огромным. Так, некоторая счетная задача, которая на i386/387DX без кэша тратила около пяти минут на итерацию — после установки 256 KB кэша с обратной записью стала делать итерацию расчета за 20-30 секунд, поначалу вызвав у программистов ощущение аварийного завершения (потому что «так быстро не бывает»).

Следующий шаг системам кэширования пришлось сделать с появлением процессоров i486 и Motorola 68040. Почему? Эти появившиеся практически одновременно конкуренты отличались от своих предшественников и большими частотами, и наличием описанного выше конвейерного исполнения команд. С точки зрения требований к пропускной способности и задержкам памяти это означает резкое увеличение нагрузки. Внешний SRAM-кэш начал вносить заметную задержку (в несколько тактов), не успевая исполнять запросы,  и чтобы избежать простоев ядра — эти процессоры получили внутренний сверхскоростной кэш, работающий рядом с ядром и на частоте ядра (кэширование, таким образом, стало многоуровневым[3] — оставшийся на плате стал кэшем второго уровня (L2), а внутренний — первого (L1)). Первоначально размер L1 и у Intel и у Motorola 68040*[4] составлял 8 KB, по 4 KB для данных и команд, а с появлением умножения частоты у процессоров Intel 486DX/2 и Motorola 68060 — размер накристалльного L1 вырос до 16 KB (по 8 для данных и команд). Одновременно для них ввели и отложенную запись, для ускорения исполнения таких запросов, чтобы не пропускать такт при выгрузке результатов. И все заверте[5]

Нет, серьезно, после появления даже того, крохотного по нынешним меркам, накристалльного кэша производительность ядра процессоров стала независимой от остальных систем настолько[6], что стало понятно: найден абсолютно верный путь, позволяющий строить все более и более быстрые процессоры, почти не замечая гири DRAM на ноге. С тех пор кэши на кристаллах только росли.

На самом деле, тут есть фундаментальный конфликт интересов. С одной стороны, чем больше кэш — тем вероятнее найти в нем нужные данные, но с другой стороны — чем он больше, тем медленнее[7] поиск в нем. Конечно, у разных компаний разные успехи в этом (Intel умеет делать очень быстрые, но относительно небольшие кэши, а вот AMD берет объемом, увеличивая процент попаданий), но в целом такое соотношение сохраняется.

Возникла, правда, проблема: чем больше кэш — тем дольше в нем поиск, но с другой стороны — выше вероятность попадания. Пока размеры в 1-4 килобайта это не в счет, а когда уже 16+16 и более — то над дальнейшим увеличением уже надо подумать, что важнее: темп доступа или вероятность попадания/промаха? Потому что поиск-то в кэшах выполняется последовательно, от меньших кэшей к большим. Сначала происходит проверка наименьшего и наибыстрейшего кэша первого уровня (L1), в случае попадания процессор продолжает работу на высокой скорости. Если меньший кэш дал промах (в нем нет нужных данных), проверяется следующий, чуть больший и более медленный кэш второго уровня (L2), и так далее, пока — потерпев две, три, а иногда и четыре неудачи последовательно не будет сформировано запроса к основной памяти.

По мере роста производительности процессоров росли и кэши. В эпоху i486 у PC[8] размер L2 варьировался от 128 KB у дешевых моделей до 512 KB у продвинутых рабочих станций, процессоры Pentium поддерживались уже кэшами размером 256-1024 KB (как правило[9], 512 KB), но время шло, и по мере увеличения коэффициента умножения, L2 работающий на частоте внешней шины справляться перестал (а у высокопроизводительных RISC станций и серверов проблема обозначилась еще раньше[10]). Потребовалось пересматривать всю системную архитектуру. Варианты были разные[11], но победил Intel.

Да, самым красивым стало решение Pentium PRO от Intel. В дополнение к 8+8 KB L1 в корпусе процессора разместился и кристалл с L2, работавший по отдельной шине на полной скорости ядра (соответственно, от 150 до 200 MHz) размер этого L2 варьировался от 256 до 1024 KB, а эффект от увеличения его скорости до частоты ядра был потрясающим: Pentium PRO 166 с полноскоростным L2 на 512 KB обгонял обычный Pentium 166 с 1024 KB внешнего L2 на частоте 66 MHz как стоячего. Чуть лучше выглядел Pentium 166 MMX (L1 у него был 16+16), но тоже[12] — не то! Pentium PRO же с частотой 200 MHz и 1024 KB полноскоростного L2 рвал всех[13]. И, в особенности, кошельки покупателей. На момент выпуска он стоил около 3000 еще тех, тяжелых, долларов за штуку. И с ростом частоты цена вопроса обещала расти уже совершенно непристойно. Надо было как-то выходить из положения.

Галопом по вычислительным Европам. Часть 4. Как накормить процессор. Статья Ильи Вайцмана. 25.01.2023 г.

И ответы тут разные компании нашли разные. Так компания Intel в своих процессорах Pentium II остановилась на варианте 16+16 L1, но быстро (+512 KB на плате процессора и на половинной частоте), а вот AMD в своих первых Athlon предпочла пожертвовать скоростью L1, увеличив вероятность попадания за счет размера 64+64 и эксклюзивности[14] L1 по отношению к L2 (L2 в серии Argon/Pluto/Orion так же, как у Intel был внешним, 512 KB и на половинной скорости ядра). Впоследствии, по мере развития технологических процессов оказалось, что вместо сборки из самого процессора и микросхем кэша на корпусе процессора (Pentium PRO) или на процессорной плате (Pentium II с разъемом Slot 1 и первые Athlon на Slot A) все можно разместить на самом кристалле. Получается хотя (на тот момент) и меньше — 256 KB против 512[15], но более чем вдвое быстрее. С тех пор прошло много лет, и после того, как на кристалл стал помещаться не только L2, но и несколько ядер возникла новая проблема: организация взаимодействия между различными потоками команд. И это было непросто…

Смотрите: как обычно происходит обмен? Один процесс пишет что-то в оперативную память, а другой читает оттуда данные. Пока у вас одно ядро, всё более-менее просто, записал — передал управление, и пускай другой поток читает. Если у нас есть кэш (обычно, второго уровня) с отложенной записью, то оборот данных происходит достаточно быстро. Если же, предположим, у нас двух- и более процессорная система, то второй процесс получит доступ к данным только после того, как они «доедут» до основной оперативной памяти и прокрутятся через нее, а основная память, примерно на порядок медленнее, чем накристалльный кэш второго уровня. Пока частоты были небольшими (в эпоху тех же Pentium PRO и первых Pentium II), такой оборот не представлял большой проблемы, но когда процессы перешагнули за гигагерц, а число ядер на кристалле стало больше одного — пришлось думать, как быть.

И тут снова, разные компании подошли к вопросу по-разному. Компания Intel сначала (в процессорах архитектуры Core 2) увеличили L1, до размера 32I+32D и начали делать общий на два ядра кэш L2 (доведя его размер до 2, 3, 4 и даже 6 MB на два ядра и, соответственно, 4, 6, 8 и 12[16] MB на сборку из двух чиплетов, известную как Core 2 Quad). Но, как и ожидалось, большие кэши не позволяли производить быстрый поиск, ограничивая потенциал ускорения самих ядер, поэтому впоследствии, (в процессорах Core I[3...9], с заметно выросшим IPC) Intel решила использовать небольшой L2 (по 256 KB на ядро), а для межъядерного обмена и буферизации работы с основной памятью создать разделяемый между ядрами кэш третьего уровня.

Их же конкуренты из AMD предпочли оставить кэши отдельными, ограничившись L2, но довели их размер с 256 до 1024 KB на ядро. В принципе, индивидуальные кэши у AMD получались эффективнее, потому что больше (в одно и то же время между собой конкурировали процессоры Intel с двумя ядрами с кешем 256 KB на ядро и 2-3 мегабайтами общего L3 и четырехядерные процессоры AMD с организацией L2 4 x 1 MB), что для суммы однопоточных приложений выходило неплохо, но вот межпроцессный обмен получался хуже. Итог был, прямо скажем, неоднозначным.

Если рассмотреть процессоры AMD семейств Llano и Trinity, то можно увидеть, что при тех же четырех ядрах у Liano L2[17] организован как 4x1 MB, а у Trinity — 2x2 MB. Что частично решило проблему взаимодействия процессов (как у Core 2 Quad). Стало гораздо лучше, хотя бы два потока получили возможность для быстрого обмена данными. Однако, передача данных между потоками из разных «полупроцессоров» требовала (как и у процессоров Intel) оборота данных через оперативную память.

Компания AMD урок усвоила, и следующие поколения многоядерных процессоров AMD получили разделяемый (и доступный для встроенной графики, если она есть) кэш третьего уровня. И даже в тех случаях, когда по технологическим причинам этот кэш приходится разделять на части — то на каждый сегмент кэша третьего уровня приходится не меньше трех ядер (6 потоков), чего в большинстве случаев достаточно для приемлемой эффективности обмена (помните главу «Количество против качества»?). С тех пор трехуровневая модель кэширования — быстрый маленький L1, среднего размера L2 и большой разделяемый между ядрами L3 – стала, в целом, общепринятой.

Но не кэшем единым живы процессоры, без основной памяти и долговременного энергонезависимого накопителя информации тоже никуда не денешься, и тут тоже все сложно (но интересно).

Продолжение следует…

[1]У процессоров 8086/88 адрес и данные выставлялись на общую шину поочередно, а 286 мог делать это в одном такте, по раздельным шинам адреса (24 разряда) и данных (16 разрядов).

[2]Отличавшихся, в первую очередь, штатной скоростью (длиной цикла). На дорогих материнках при равном объеме кэш, как правило, был побыстрее, на дешевых — помедленнее.

[3]Внутренний кэш появился впервые даже чуть раньше, в 1985 году в процессоре MIPS R2000, и сразу 64I+32D, но у него не было внешнего кэша, так что это было такое неоднозначное с точки зрения производительности решение. В R3000 положение исправилось, но на рынки он уже реально вышел позже, чем i486.

[4]Неконвейерный процессор Motorola 68030 имел по 256 байт неуправляемого кэша команд и данных, которые можно считать, скорее, буферами предвыборки, а не полноценными кэшами. Такие же 256 байт кэша имел и микропроцессор Zilog Z280, например.

[5]Существовали забавные мутанты: процессоры Cyrix/Ti 486SLC/DLC, по сути i386SX/DX но с 1 KB кэша L1, предназначенные для модернизации старых компьютеров. Сколько людей было обмануто жуликами, продававшими им такие системы за цену полноценных i486DX!! Нет, они были существенно быстрее i386, особенно в сочетании с Cyrix FasMath 83D87, но… А еще были 486SRX2 и 486DRX2, де-факто i386 SX и DX с частотой 66 MHz. Ощущение от работы на 486DRX2 было странным. Вроде бы, иногда даже почти как 486, но как с гирей на ноге. А вот смысла в 486SRX2 я совсем не понял. Все упиралось в 16-разрядную внешнюю шину.

[6]Не на 100%, конечно, но внутренние кэши в достаточно большой степени развязали руки разработчикам CPU.

[7]Чисто для примера: в процессоре Phenom II x6 1055T, имеющего размеры кэша первого, второго и третьего уровня в 64I+64D, 512 и 6144 килобайта — задержка выборки составляет 3, 13 и 48 тактов соответственно.

[8]В мире дорогих серверов и рабочих станций (MIPS или PA-RISC, например) размеры кэша на материнских платах уже в середине-конце 80 достигали 4-16 мегабайт. Но по таким ценам, я вам скажу…

[9]Чипсеты VIA Apollo поддерживали до 2048 KB L2, что иногда оказывалось полезным. Но, в целом, для того периода такая величина кэша была избыточной.

[10]Серверы на процессорах Alpha 21064 получили до 16(!) MB внешнего кэша L2, но помогало это тоже частично.

[11]Процессоры HP PA-RISC, например, оснащались огромным кэшем L1 (до 4 MB (2D+2I) на плате процессора), что позволяло им добиться высокой производительности на некоторых алгоритмах. Но — не на всех. Это решение оказалось не универсальным.

[12]На платах ASUSTEK можно было вместо шины 66,6 и коэффициента умножения 2,5 сделать 83х2 — и в таком случае (если все работало) Pentium 166 MMX с 1024 L2 обретал почти крылья. Потому что ускорялось не только ядро, но и L2 с оперативной памятью. Получалось быстрее, чем стандартный (3х66,6) Pentium 200 MMX.

[13]В чуть более поздние времена было на практике доказано, что серверная система из двух PPro 200 с полноскоростным L2 в 1024 KB даже со старой EDO памятью, уверенно опережала на ряде задач сервер на двух Pentium II 300 с более совершенной SDRAM — но с кэшами по 512 KB половинной скорости..

[14]Адресное пространство кэшей, как правило, инклюзивно — содержимое и адресное пространство кэша является частью кэша следующего уровня, но эксклюзивные кэши процессоров AMD того поколения не дублировались, а складывались, поэтому объем кэшей у них получался 128+512=640 KB.

[15]Соответственно, у Intel общий объем кэшей на кристалле вышел 256 KB, а у AMD целых 384. Но медленнее.

[16]Процессор Q9650, к примеру, с четырьмя ядрами частотой 3 GHz и 12 MB (2x6) кэша L2 — очень достойный по производительности экземпляр. На материнских платах с DDR3 он вполне актуален даже сегодня. Не то что круть неимоверная (как 15 лет назад), но вполне для работы годно.

[17]У семейства Trinity очень странная организация L1. Кэш инструкций по 16 KB индивидуальный для каждого ядра, а L1D (кэш данных) размером 64 KB — разделяемый, на два ядра. Решение интересное, но спорное.

----

Галопом по вычислительным Европам. Часть 2. Пути повышения IPC.

Галопом по вычислительным Европам. Часть 3. Оптимизация.

Галопом по вычислительным Европам. Часть 3. Оптимизация. Статья Ильи Вайцмана. 20.01.2023 г.

Галопом по вычислительным Европам. Часть 4. Как накормить процессор.

Галопом по вычислительным Европам. Часть 4. Как накормить процессор. Статья Ильи Вайцмана. 25.01.2023 г.

Blood, Sweat & Tears, или Кровь, пот и слёзы – часть третья, объединительная

Про верных русланов и их потомков

Про верных русланов и их потомков. Статья Владислава Боярова. 11.05.2022 г..