Как работают строки кэша? - PullRequest
       33

Как работают строки кэша?

144 голосов
/ 14 октября 2010

Я понимаю, что процессор вводит данные в кеш через строки кеша, которые - например, на моем процессоре Atom - за один раз выдают примерно 64 байта, независимо от размера считываемых данных.

Мой вопрос:

Представьте, что вам нужно прочитать один байт из памяти, какие 64 байта будут занесены в кеш?

Две возможности, которые я вижу, состоят в том, что либо 64 байта начинаются с ближайшей границы в 64 байта ниже интересующего байта, либо 64 байта распределяются вокруг байта некоторым заранее определенным способом (например, наполовину меньше, наполовину меньше). выше или все выше).

Что это?

Ответы [ 5 ]

109 голосов
/ 16 октября 2010

Если строка кэша, содержащая загружаемый вами байт или слово, еще не присутствует в кэше, ваш ЦП запросит 64 байта, которые начинаются на границе строки кэша (самый большой адрес ниже того, который вам нужен, кратный из 64).

Современные модули памяти ПК передают 64 бита (8 байт) за раз, в пакете из восьми передач , поэтому одна команда запускает чтение или запись полной строки кэша из памяти. (Размер пакетной передачи DDR1 / 2/3/4 SDRAM настраивается до 64 ББ; ЦП выбирают размер пакетной передачи в соответствии с размером строки кэша, но обычно используется 64 Б)

Как правило, если процессор не может предсказать доступ к памяти (и предварительно выбрать его), процесс поиска может занять ~ 90 наносекунд или ~ 250 тактовых циклов (от ЦП, зная адрес, до получения ЦП). данные).

Напротив, попадание в кэш L1 имеет задержку загрузки 3 или 4 такта, а перезагрузка магазина имеет задержку пересылки хранилища 4 или 5 циклов на современных процессорах x86. Вещи похожи на другие архитектуры.

Дальнейшее чтение: Ульрих Дреппер Что каждый программист должен знать о памяти . Рекомендации по программной предварительной выборке несколько устарели: современные программы предварительной выборки HW умнее, а гиперпоточность намного лучше, чем в дни P4 (поэтому поток предварительной выборки обычно является пустой тратой). Кроме того, вики-тег содержит множество ссылок на производительность для этой архитектуры.

20 голосов
/ 11 июня 2013

Если строки кэша имеют ширину 64 байта, то они соответствуют блокам памяти, которые начинаются с адресов, кратных 64. Наименее значимые 6 бит любого адреса являются смещением в строке кэша.

Таким образом, для любого данного байта можно найти строку кэша, которую необходимо извлечь, очистив шесть младших бит адреса, что соответствует округлению до ближайшего адреса, кратного 64.

Хотя это делается аппаратно, мы можем показать расчеты, используя некоторые эталонные определения макросов C:

#define CACHE_BLOCK_BITS 6
#define CACHE_BLOCK_SIZE (1U << CACHE_BLOCK_BITS)  /* 64 */
#define CACHE_BLOCK_MASK (CACHE_BLOCK_SIZE - 1)    /* 63, 0x3F */

/* Which byte offset in its cache block does this address reference? */
#define CACHE_BLOCK_OFFSET(ADDR) ((ADDR) & CACHE_BLOCK_MASK)

/* Address of 64 byte block brought into the cache when ADDR accessed */
#define CACHE_BLOCK_ALIGNED_ADDR(ADDR) ((ADDR) & ~CACHE_BLOCK_MASK)
14 голосов
/ 30 августа 2016

Прежде всего, доступ к основной памяти очень дорогой.В настоящее время процессор с частотой 2 ГГц (самый медленный один раз) имеет такты 2 Гц (циклов) в секунду.Процессор (виртуальное ядро ​​в настоящее время) может извлекать значение из своих регистров один раз за такт.Поскольку виртуальное ядро ​​состоит из нескольких процессорных блоков (ALU - блок арифметической логики, FPU и т. Д.), Оно может обрабатывать некоторые команды параллельно, если это возможно.

Доступ к основной памяти стоит от 70 нс до 100 нс (DDR4 стоитнемного быстрее).На этот раз в основном ищем кэш L1, L2 и L3 и затем попадаем в память (посылаем команду контроллеру памяти, которая отправляет его в банки памяти), ждем ответа и все готово.

100 нс означает около 200 тиков.Таким образом, в основном, если программа всегда будет пропускать кеши, к которым обращается каждая память, центральный процессор будет тратить около 99,5% своего времени (если он только читает память) в ожидании памяти.

Для ускорениявещи там есть кэши L1, L2, L3.Они используют память, непосредственно размещенную на чипе, и используют различные типы транзисторных схем для хранения данных битов.Это занимает больше места, больше энергии и является более дорогостоящим, чем основная память, поскольку ЦП обычно создается с использованием более продвинутой технологии, и производственный сбой в памяти L1, L2, L3 может сделать ЦП бесполезным (дефект), поэтомубольшие кэши L1, L2, L3 увеличивают частоту ошибок, что снижает выход, что напрямую снижает ROI.Таким образом, существует огромный компромисс, когда дело доходит до доступного размера кэша.

(в настоящее время создается больше кэшей L1, L2, L3, чтобы иметь возможность деактивировать определенные части для уменьшения вероятности фактического производственного бракаявляется областью кеш-памяти, отображающей дефект ЦП в целом).

Чтобы дать представление о времени (источник: затраты на доступ к кешам и памяти )

  • Кэш-память первого уровня: от 1 до 2 нс (2-4 такта)
  • Кэш-память второго уровня: от 3 нс до 5 нс (6-10 циклов)
  • Кэш-память третьего уровня: от 12 нс до 20 нс (24-40 циклов)
  • ОЗУ: 60 нс (120 циклов)

Поскольку мы смешиваем разные типы процессоров, это всего лишь оценки, но они дают хорошее представление о том, что происходит на самом деле, когда выбирается значение памяти, и мы можем получить ударили промах в определенном слое кеша.

Таким образом, кеш в основном значительно ускоряет доступ к памяти (60 нс против 1 нс).

Получение значения, сохранение его в кеше для возможности перечитыванияэто хорошо для переменных, к которым часто обращаются, но для памятиОперации копирования ory это все равно будет медленным, так как каждый просто читает значение, записывает значение куда-то и никогда не считывает значение снова ... нет попаданий в кэш, очень медленно (кроме этого может происходить параллельно, так как у нас нет выполнения заказа).

Эта копия памяти настолько важна, что есть разные способы ускорить ее.В первые дни память часто могла копировать память вне процессора.Он был обработан контроллером памяти напрямую, поэтому операция копирования памяти не загрязняла кэши.

Но помимо простой копии памяти, другой последовательный доступ к памяти был довольно распространенным явлением.Примером является анализ ряда информации.Наличие массива целых чисел и вычисление суммы, среднего, среднего или даже более простого нахождения определенного значения (фильтр / поиск) были еще одним очень важным классом алгоритмов, запускаемых каждый раз на любом ЦП общего назначения.

Таким образом, анализируяВ схеме доступа к памяти было очевидно, что данные читаются последовательно очень часто.Была высокая вероятность того, что если программа читает значение по индексу i, программа также будет читать значение i + 1.Эта вероятность немного выше, чем вероятность того, что та же программа также прочитает значение i + 2 и так далее.

Таким образом, учитывая адрес памяти, было (и остается) хорошей идеей читать вперед и извлекать дополнительные значения.Это причина, почему есть режим повышения.

Доступ к памяти в режиме повышения означает, что адрес отправляется, а несколько значений отправляются последовательно. Каждая дополнительная отправка значения занимает всего около 10 нс (или даже ниже).

Еще одной проблемой был адрес. Отправка адреса занимает время. Чтобы адресовать большую часть памяти, необходимо отправить большие адреса. В первые дни это означало, что адресная шина была недостаточно велика для отправки адреса за один цикл (тик), и для отправки адреса требовалось более одного цикла, добавляя больше задержки.

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

Другая проблема, которую решает строка кэша (помимо чтения вперед и сохранения / освобождения шести битов на адресной шине), заключается в том, как организован кэш. Например, если кэш будет разделен на 8-байтовые (64-битные) блоки (ячейки), необходимо хранить адрес ячейки памяти, для которой эта ячейка кэша содержит значение вместе с ней. Если адрес также будет 64-битным, это означает, что половина размера кэша используется адресом, что приводит к дополнительным расходам в 100%.

Поскольку строка кэша имеет размер 64 байта и процессор может использовать 64 бита - 6 бит = 58 бит (нет необходимости хранить нулевые биты слишком правильно), это означает, что мы можем кэшировать 64 байта или 512 бит с объемом служебной информации 58 бит (11% дополнительной нагрузки). В действительности хранимые адреса даже меньше этого, но есть информация о состоянии (например, строка кэша верна и точна, грязна и требует обратной записи в оперативной памяти и т. Д.).

Другим аспектом является то, что у нас есть сет-ассоциативный кеш. Не каждая ячейка кеша может хранить определенный адрес, но только его подмножество. Это делает необходимые биты сохраненных адресов еще меньше, обеспечивает параллельный доступ к кешу (к каждому подмножеству можно получить доступ один раз, но независимо от других подмножеств).

Особенно важно, когда речь идет о синхронизации доступа к кэш-памяти / памяти между различными виртуальными ядрами, их независимыми несколькими процессорами на ядро ​​и, наконец, несколькими процессорами на одной материнской плате (в которых есть платы, содержащие до 48 процессоров и более). .

Это в основном текущая идея, почему у нас есть строки кэша. Преимущество от чтения вперед очень велико, и наихудший случай чтения одного байта из строки кэша и повторного чтения остальных очень мал, поскольку вероятность очень мала.

Размер строки кэша (64) является разумно выбранным компромиссом между большими строками кэша, что делает маловероятным, что последний байт будет прочитан в ближайшем будущем, а также продолжительностью, необходимой для извлечения завершить строку кэша из памяти (и записать ее обратно), а также накладные расходы на организацию кэша и распараллеливание доступа к кешу и памяти.

6 голосов
/ 16 октября 2010

Процессоры могут иметь многоуровневые кэши (L1, L2, L3), и они различаются по размеру и скорости.

Тем не менее, чтобы понять, что именно входит в каждый кэш, вам нужно изучить веткупредиктор, используемый этим конкретным процессором, и то, как инструкции / данные вашей программы ведут себя против него.

Прочитайте о предикторе ветвления , кэш процессора и заменеПолитика .

Это не простая задача.Если в конце дня вам нужен только тест производительности, вы можете использовать такой инструмент, как Cachegrind .Однако, поскольку это симуляция, ее результат может в некоторой степени отличаться.

4 голосов
/ 16 октября 2010

Не могу сказать наверняка, поскольку каждое оборудование отличается, но обычно это «64 байта начинаются с ближайшей границы 64 байта ниже», поскольку это очень быстрая и простая операция для ЦП.

...