Увеличение быстродействия с помощью дополнительных устройств
Некоторые частые и медленные операции можно убыстрить за счёт аппаратного сбора и хранения дополнительной информации. Например, самые частые обращения в медленную память можно кешировать в быстрой памяти, а адрес следующей инструкции в условном переходе предсказывать (это особенно актуально при упреждающих вычислениях)
Кеш
Кеширование как идея
Базовая статья: Что такое кэш процессора, и как он работает
Кеш — механизм аккумуляции части данных медленного устройства большого объёма с помощью быстрого устройства малого объёма.
- Повышение быстродействия за счёт скорости обмена большинством данных
- Повышение эффективности за счёт дробления единиц обмена данными
- Пример: дисковый кеш
- быстродействие памяти на несколько порядков выше быстродействия диска
- обмен с диском поблочный, с памятью — побайтный
Для повышения быстродействия в кеше должны содержаться как можно более «популярные» данные
Временная локальность: доступ к некоторому ограниченному фрагменту «большой» памяти востребован много раз в течение небольшого промежутка времени (когда выполняется работающий с ним фрагмент программы)
- Адресная локальность: соседние ячейки памяти часто востребованы вместе (например, при работе с массивами)
Меньший размер и увеличение быстродействия иногда связаны напрямую:
- чтение из памяти: выставление адреса → доступ к ячейке по адресу → чтение данных → запись во внутренний блок
- чтение из регистра: непосредственная запись из регистра (определяется на стадии разбора инструкции) во внутренний блок
Методика заполнения и очистки кеша
Заполнение кеша
- По запросу: в кеш попадают только данные, к которым производился доступ
- Упреждающее чтение: в кеш также попадают данные, лежащие на кешируемом устройстве непосредственно после только что прочтённых
- Иногда получается автоматически (чтение блока с диска)
- Иногда можно сделать более эффективным (кеширование сразу файла или сразу многих команд до команды перехода)
- Чем больше упреждение, тем выше вероятность закешировать ненужное
- Анализ поведения программы (например, размер упреждения в зависимости от места в программе, кеширование данных, к которым она обратится, т. к. это записано в ещё не исполненном коде и т. п.)
Устаревание данных в кеше
- Случайные
- эффективная аппаратная реализация
- невысокая эффективность кеширования
- Редко используемые (LFU)
- требуется хранить счётчик использования
- требуется поиск самого низкого значения счётчика
плохо работает в чистом виде (только что добавленный элемент использовался один раз, но, согласно принципу временной локальности, его не надо тут же выбрасывать из кеша)
- Давно не используемые (LRU)
- в чистом виде требуется хранить возраст
- Очередь (FIFO)
- в чистом виде — эффективная аппаратная реализация
- невысокая эффективность кеширования
- FIFO+LRU
самый свежий элемент всегда ставится в начало очереди (или переставляется в начало, если он был в ней)
- удаляется самый последний элемент (он и есть самый старый)
Замечание: кеш эффективен статистически: всегда можно создать алгоритм или наткнуться на класс задач, сильно понижащий актуальность кешируемых данных (например, пересылка огромных объёмов данных в случайном порядке).
Терминология
Попадание (cache hit) — однократный доступ к закешированному элементу
Промах (cache miss) — однократный доступ к незакешированному элементу
Кеширование одного байта/ячейки невыгодно: слишком много метаинформации на один элемент кеша:
| 00 | 10 04 00 00 | a0 78 4f 95 |
- 8 битов — счётчик возраста
- 30 битов — адрес
- 32 бита — содержимое ячейки Уже тут не нужно 32 битов для адреса, т. к. адрес ячейки всегда кратен 4
Разовьём идею. Пользуясь адресной локальностью, можно эффективно кешировать непрерывные области памяти:
| 00 | 10 04 00 | a0 78 4f 95 … 34 45|
- 8 битов — счётчик возраста
24 бита — тег адреса
8192 бита — содержимое ячеек (строка кеша)
Тег адреса в кеше — часть адреса, используемая для идентификации кешируемой области Строка кеша — минимальная кешируемая область данных
Кеш прямого отображения
Принцип: каждая строка кеширует одну из нескольких фиксированных областей памяти:
Размер тега зависит от количества областей, кешируемых одной и той же строкой. В примере таких области 4 (количество линий, исходящих из каждой строки), значит, тег займёт всего 2 бита.
Замечание: алгоритм «привязки» областей памяти к той или иной строке кеша может быть любым, но чаще всего используется тот, что в примере: память представляется в виде последовательно расположенных разделов (того же объёма, что и кеш), и тегом является номер раздела. Каждый раздел, в свою очередь, представлен в виде областей, соответствующих строкам кеша.
- При обращении к памяти через кеш вычисляются:
- тег адреса — целая часть деления адреса на размер кеша
номер строки — целая часть деления смещения относительно начала раздела на длину строки
- Если тег адреса совпадает с записанным в строку кеша, засчитывается попадание, и данные передаются из кеша
- Если тег адреса не совпадает с записанным в строку кеша, засчитывается промах, и происходит непосредственное обращение к памяти с обязательным заполнением строки
Согласно принципу адресной локальности такое разбиение наиболее эффективно: подряд идущие данные не вытесняют друг друга из кеша, если помещаются в нем
Прямой кеш имеет очень эффективную аппаратную реализацию, но не справляется с нарушениями принципа адресной локальности
Ассоциативный кеш
Любая строка может кешировать любую область памяти соответствующего размера (с выравниванием адреса).
Тег в ассоциативном кеше больше тега в прямом кеше, он также зависит от числа возможных кешируемых областей, а их столько, сколько строк умещается в памяти
- При обращении к памяти через кеш вычисляется тег — целая часть деления адреса на размер строки
- Если строка с таким тегом содержится в кеше, засчитывается попадание, и данные передаются из кеша
- Если строки с таким тегом в кеше нет, засчитывается промах
- происходит непосредственное обращение к памяти
- выбирается строка-кандидат на вытеснение из кеша
- считанные данные помещаются в строку кеша
Ассоциативный кеш требует аппаратной реализации поиска тега, следовательно, его эффективность линейно падает с увеличением количества строк
С другой стороны, ассоциативный кеш устойчив к нарушению принципа адресной локальности, а алгоритм вытеснения из кеша позволяет повысить эффективность и за счёт локальности временной.
Множественно-ассоциативный кеш
В современных многозадачных ОС с разделением времени принципы локальности несколько видоизменились
- Временная локальность либо обеспечена только на один квант работы процесса, либо захватывает данные всех активных процессов систем
- Адресная локальность демонстрирует не один очаг «горячих» данных, а несколько, по количеству активных процессов системы
Таким образом, востребована архитектура кеша
- большего по объёму
- поддерживающего более одного очага локальности
Естественное решение: разбить монолитный кеш прямого доступа на несколько «банков» (в соответствии с количеством ожидаемых очагов локальности), а банки объединить ассоциативно
В таком кеше
- размер тега равен размеру тега в одном банке прямого доступа
- поиск тега среди строк разных банков сравнительно эффективен, т. к. линеен по количеству банков
- могут эффективно кешироваться несколько одновременно выполняющихся потоков команд и используемых ими областей данных
Кеш на запись
Всё это время разговор шёл о чтении из памяти
Кеширование записи очень заманчиво, т. к. согласно принципу локальности записей в память может быть много, и все они попадут в кеш, а синхронизацию с памятью можно сделать после или вообще по запросу на чтение.
Когерентность кеша — соответствие данных, находящихся в кеше, данным в памяти
- Запись не кешируется (сквозная запись, «wtite through»)
- если данные есть в кеше, их надо обновить
- если данных нет в кеше
- кеш не изменяется
- кеш изменяется, как если бы происходило чтение (заполнение по записи)
- заполнение по записи требует усложнения архитектуры, и не всегда эффективно
- Запись кешируется (write back), т. е. вычисления продолжаются непосредственно после записи данных в кеш, не дожидаясь актуальной записи в память
- Дополнительный признак: «горячая» строка (вариант: dirty) — строка, содержащая акутальные данные, но не синхронизованная с оперативной памятью
Чем дольше строка горячая, тем больше вероятность, что в неё ещё раз запишут => эффективность
- С другой стороны, чем больше горячих кешей, тем дороже их потеря (сброс питания, например) и тяжелее операция полной выгрузки
- Все операции с оперативной памятью (в частности, DMA) должны либо проходить через кеш (это неэффективно), либо отслеживаться кеш-контроллером
- ⇒ возникает дополнительный признак: «недостоверная» строка (invalid), означающий, что данные в строке неактуальны (например, память была обновлена по DMA)
- доступ на чтение в обход кеша также надо отслеживать и задерживать до синхронизации соответствующих горячих кешей
- Дополнительный признак: «горячая» строка (вариант: dirty) — строка, содержащая акутальные данные, но не синхронизованная с оперативной памятью
Всё это начинает крепко подгорать в многоядерных системах, которые имеют обыкновение
- Иметь отдельный кеш на каждом ядре
Лезть в память одновременно
(см. план лекции по многоядаерным системам)
Замечания и примеры
- Кеш данных и кеш инструкций удобно разделять:
- инструкции не кешируются на запись
- инструкции и данные могут образовывать два очага локальности
- для инструкций и для данных методы упреждения различны
- Кеш часто делают двухуровневым
- большой относительно медленный общий кеш L2 (скорее всего, прямого отображения + несколько ассоциативных банков)
- два маленьких совсем быстрых кеша (отдельно — инструкций L1I, отдельно — данных L1D; скорее всего, ассоциативные)
- В зависимости от архитектуры, L1 может быть подмножеством L2 или дополнением к нему. В последнем случае данные, вытесняемые из L1, помещаются в L2, а данные, закешированные в L1, вытесняются из L2
MARS
- Открыть в Mars «Tools → Data Cache Simulator»
- Открыть в Mars «Tools → Memory Reference Visualization»
- Изучить эффективность работы кешей (2×2=4 варианта)
- Организация: Direct Mapping (прямое отображение) / Fully Associative (ассоциативный)
- Вытеснение: LRU (давно не использованная строка) / Random (случайная строка)
Примеры:
- Гладкое чтение:
- Чтение вразбивку (с двойным вытеснением):
- Чтение с шагом (поэкспериментировать с размером шага):
- Чтение с небольшим вытеснением:
Предсказание перехода
Условные переходы отрицательно влияют на эффективность выполнения программы: невозможно точно сказать, какую инструкцию следующей выбирать из памяти — следующую или по адресу перехода
в MIPS это приводит либо к появлению в конвейере «пузырька»-nopа, либо к использованию слота задержки (конвейер организован таким образом, что уже на втором шаге стадии проблема снимается)
в архетиктурах с упреждающим выполнением это может привести к необходимости выполнять обе ветки вычислений (с двойным набором регистров и прочими сложностями)
Можно попробовать пособирать статистику: в каких случаях условный переход был, а в каких — не было
Количественную (сколько раз был переход, сколько — не было)
- Количественную с устареванием
С учётом значений используемых регистров (например, $ra и $sp)
- ...
Для этого нужно предусмотреть специальную таблицу, в которой хранятся адреса инструкций (последних? наиболее популярных? см. политики кеширования) условных переходов и статистика по их использованию.
При выполнении такой инструкции процессор будет (в терминах MIPS) заранее предсказывать адрес для стадии I, а в случае ошибочного предсказания оперативно заменять уже считанную инструкцию на «пузырёк»-nop (трюк со слотом задержки мы сейчас не рассматриваем).
В MARS реализована простейшая BHT (Branch History Table), собирающая статистику за один или два предыдущих вызова конкретного перехода. Даже в случае одного бита предсказания это должно повысить эффективность циклов (в которых множество переходов по одной ветке завершается единственным переходом по другой).
В случае двух битов предсказания можно ещё немного повысить эффективность классического цикла. Допустим, после серии переходов (цикл продолжался) произошёл не-переход (цикл закончился). Логично ожидать, что когда выполнение подойдёт к этому переходу в следующий раз, переход снова произойдёт (начнётся новый цикл), и однократной ошибки в предсказании недостаточно, чтобы «переменить мнение».
Вот как BHT отрабатывает наш третий пример
с размером истории 2 и политикой по умолчанию «NOT TAKE» (переход не произойдёт, если статистика отсутствует или 1:1):
Вопрос: каков метод попадания инструкции перехода в BHT MARS?
Стоит попробовать все варианты (размер 1/2 и политика TAKE/NOT TAKE). Заметим, что стиль написания программы влияет на эффективность предсказания (как?)
Более глубокая статистика (например, 3), позволяет нивелировать эту зависимость эффективности от стиля, но вычислительно дороже.