Прежде всего, доступ к основной памяти очень дорогой.В настоящее время процессор с частотой 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) является разумно выбранным компромиссом между большими строками кэша, что делает маловероятным, что последний байт будет прочитан в ближайшем будущем, а также продолжительностью, необходимой для извлечения завершить строку кэша из памяти (и записать ее обратно), а также накладные расходы на организацию кэша и распараллеливание доступа к кешу и памяти.