Здравствуйте! Когда появится возможность сдать экзамен на сертификат? |
Кэш-память
Назначение и способы организации кэш-памяти; стратегии замещения; принцип локальности; особенности организации кэш-памяти в многоядерных процессорах на примере Intel Core i7.
НАЗНАЧЕНИЕ И СПОСОБЫ ОРГАНИЗАЦИИ КЭШ-ПАМЯТИ
Как следует из принципов архитектуры фон Неймана, память ЭВМ неоднородна и имеется иерархия запоминающих устройств. Одним из элементов этой иерархии в современных компьютерах является кэш-память процессора.
Кэш-память процессора (Cache) - это особый вид быстрой памяти, которая находится в процессоре и предназначается для хранения данных, активно используемых при выполнении данной программы в текущий момент времени, что позволяет существенно ускорить работу и снизить нагрузку на оперативную память и системную шину.
Размер кэш-памяти существенно меньше и самой оперативной памяти, и той её части, которая выделяется для работы одной программы. Поскольку эта память находится внутри процессора, то ограничения на размеры кремниевой пластины, на которой находится процессор, и различные другие факторы не позволяют сделать эту память достаточно большой. Поэтому по мере выполнения программы данные в кэш-памяти постоянно обновляются (эта процедура называется замещением и будет рассмотрена ниже).
Фактически, современные процессоры не работают напрямую с данными в оперативной памяти, но предварительно загружают их в кэш-память. При этом исполняемая процессором программа не контролирует работу с кэш-памятью и не знает о том, помещены ли туда необходимые данные туда, то есть для неё кэш-память является прозрачной. Cache в переводе с английского означает "тайник", что как раз отражает прозрачность этого вида памяти для прикладной программы.
Обмен данными между оперативной памятью и кэш-памятью осуществляется блоками фиксированного размера. Такой блок будем называть строкой кэш-памяти. Размер одной строки и общий объём являются основными характеристиками кэш-памяти, которые отличаются для разных моделей процессоров. Например, в процессорах Intel Core i7 размер кэш-памяти варьируется от 4 до 12 Mb, а размер строки кэш-памяти составляет 64 байта. При одинаковом общем размере, кэш-память, состоящая из большего количества строк, эффективнее, но сложнее в реализации.
Современные процессоры имеют многоуровневую кэш-память. Можно сказать, в этом случае принцип иерархических запоминающих устройств фон Неймана реализуется следующим образом: уровни с меньшими номерами кэшируют наиболее часто используемые данные в уровнях с бо?льшими номерами, и при этом каждый следующий уровень имеет больший размер и меньшее быстродействие, чем предыдущий.
Чтобы для исполняемой программы кэш-память была прозрачной, используется механизм отображения кэш-памяти. Этот механизм поддерживает соответствие между данными в кэш-памяти и данными в оперативной памяти. Последнее важно, так как в программном коде, в операндах команд, которые работают не с регистрами, а с оперативной памятью (например, команда сложения вида регистр/память), указаны адреса в оперативной памяти, а не в кэш-памяти, и обращения по этим адресам нужно заменить на обращения в кэш-память. Эту подмену и реализует механизм отображения.
Механизм отображения может быть ассоциативным, прямым или гибридным.
- Ассоциативный механизм отображения обеспечивает возможность связать каждую строку кэш-памяти с любым блоком оперативной памяти (см. рис.11.1, а ). Это наиболее гибкий механизм, но его аппаратная реализация является дорогостоящей и потребляет много энергии. Это происходит из-за того, что при обращении процессора в оперативную памяти по определённому адресу все строки кэш-памяти должны опрашиваться на предмет того, не содержат ли они копию соответствующего блока оперативной памяти. Также при выделении новой или освобождении занятой строки кэш-памяти процессор должен поддерживать перечень свободных строк, который нужен для того, чтобы при необходимости выделить очередную строку и быстро найти свободную, а не проверять все имеющиеся строки.
- Прямой механизм отображения позволяет связать с каждым блоком памяти лишь с одну из строк кэш-памяти (см. рис 11.1 б ). Прямое отображение существенно дешевле в реализации, чем ассоциативное, но оно одновременно и менее эффективно.
- Во многих современных процессорах, в том числе в процессорах семейства Intel x86, используется механизм гибридного отображения: на один и тот же блок оперативной памяти может отображаться не единственная (как в прямом отображении) и не произвольная (как в ассоциативном) строка кэш-памяти, а несколько строк, часто - 4 или 8, в зависимости от модели процессора. Такой механизм значительно более гибок, чем прямое отображение, но при этом не столь сложен в реализации, как механизм ассоциативного отображения.
СТРАТЕГИИ ЗАМЕЩЕНИЯ И ПРИНЦИП ЛОКАЛЬНОСТИ
Поскольку объём кэш-памяти процессора существенно меньше объёма оперативной памяти, уже после непродолжительной работы программы кэш-память оказывается полностью занятой. При необходимости обратиться к блоку оперативной памяти, который не был помещён в кэш-память, он перемещается туда. Но для этого нужно выбрать строку в кэш-памяти, в которую пометить данный блок. Для этого процессор выполняет операцию замещения, состоящую из следующих шагов:
- выбор подходящей строки кэш-памяти для замещения;
- сохранение в оперативной памяти актуальных данные из этой строки;
- копирование в рассматриваемую строку соответствующего блока оперативной памяти.
Способ, которым в кэш-памяти выбирается строка для замещения, называется стратегией замещения. При замещении обычно используется принцип Белэди: следует заместить строку, содержащую данные, которые не понадобятся процессору дольше всего. Но однозначно выбрать такую строку нельзя, поэтому используются различные эвристические подходы. Самым распространенным является стратегия LRU (Least Recently Used), которая фиксирует время последнего обращения к каждой строке кэш-памяти и замещает ту из них, к которой процессор дольше всего не обращался. Поясним, как эта стратегия может быть реализована в зависимости от используемого механизма отображения.
- Механизм ассоциативного отображения позволяет реализовать стратегию LRU непосредственно в том виде, в котором она описана выше.
- Для механизма прямого отображения выбор строки для замещения является тривиальным, так как с каждым блоком оперативной памяти может быть связана лишь одна строка кэш-памяти, которую и следует заместить.
- Для механизма гибридного отображения стратегию LRU можно реализовать, выбирая из тех строк, которые могут быть связаны с данным блоком оперативной памяти.
Несмотря на то, что программа на C/C++, Java, Python и т.д. не может управлять кэш-памятью процессора, при разработке программ иногда её надо учитывать. Например, при работе с данными, превышающими размер кэш-памяти, следует соблюдать принцип локальности.
Принцип локальности. Данные, обрабатываемые в одном фрагменте программы, должны быть расположены в оперативной памяти рядом.
Приведём пример программы на языке C для процессора Intel Core i7 (как было указано выше, кэш-память этого процессоров составляет от 4 до 12 Mb в зависимости от его модели):
int my_array[10000][10000]; ... for(int i = 0; i < 10000; ++i) for (int j = 0; j < 10000; ++j) my_array[i][j] *= 2;
В этом фрагменте объявлен двумерный массив (матрица) целых числе my_array размером 10000*10000. Целое число (int) в современных компиляторах языка C представляется четырьмя байтами. Соответственно, для массива my_array требуется . Очевидно, что этот массив не помещается в кэш-память процессора целиком. Следовательно, по мере того как программа будет работать с этим массивом, в кэш-память процессора будут подгружаются из оперативной памяти все новые и новые фрагменты данного массива. Далее, двумерные массивы языка С хранятся в оперативной памяти построчно: my_array[0][0], ... my_array [0][9999], my_array [1][0], ... my_array [1][9999], ... Отметим, что именно в этом порядке элементы массива обрабатываются в цикле for в программе, представленной выше: то есть, когда в кэш-память из оперативной памяти загружается строка, программа полностью её обрабатывает и больше не обращается к этим данным. Затем в кэш-память загружается следующая строка, и так далее. Очевидно, что в этой программе принцип локальности соблюдается.
Изменим эту программу так: заменим оператор my_array [i][j] *= 2 на my_array [j][i] *= 2. Теперь последовательно обрабатываемые элементы массива расположены в оперативной памяти далеко друг от друга, и программа вынуждена постоянно "прыгать" по массиву, что влечет многократную загрузку/выгрузку одних и тех же строк кэш-памяти. В такой программе принцип локальности не соблюдается, и она будет работать медленнее, чем предыдущая.