Библиотека сайта rus-linux.net
Что каждый программист должен знать о памяти.
Часть 2: Кэш-память процессора
Оригинал: "Memory part 2: CPU caches"Автор: Ulrich Drepper
Дата публикации: October 1, 2007
Перевод: Н.Ромоданов
Дата перевода: апрель 2012 г.
Назад | Оглавление | Вперед |
Процессоры сегодня гораздо сложнее, чем они были всего лишь 25 лет назад. В то время частота ядра процессора была равна приблизительно частоте шины памяти. Доступ к памяти был лишь чуть-чуть медленнее, чем доступ к регистрам. Но в начале 90-х годов это все резко изменилось, когда разработчики процессоров увеличили частоту ядра процессора, а частоту шины памяти и производительность чипов памяти не удалось увеличить пропорционально. Как рассказывается в предыдущем разделе, это не связано с тем, что нельзя создать более быструю оперативную память. Сделать это можно, но это не экономично. Оперативная память, работающая с той же скоростью, что ядра современных процессоров, будет на несколько порядков дороже, чем любая динамической память.
Если выбирать между машиной с очень маленькой и очень быстрой оперативной памятью и машиной с большим количеством не такой быстрой оперативной памятью, второй вариант всегда будет более выигрышным в случаях, когда размер рабочего набора данных будет превышать размер небольшой оперативной памяти и потребуются затраты на доступ к вторичным хранилищам данных, таким как жесткие диски. Проблема здесь заключается в скорости доступа к вторичным хранилищам данных, которыми, как правило, являются жесткие диски, и которые потребуется использовать для подкачки части рабочего набора. Доступ к таким дискам на порядки медленнее, чем даже доступ к памяти DRAM.
К счастью, это не тот случай, когда либо все, либо ничего. Компьютер в дополнение к большому объему памяти DRAM может иметь небольшое количество высокоскоростной памяти SRAM. Одной из возможных реализаций могло бы быть выделение определенной области адресного пространства процессора как содержащей память SRAM, а для памяти DRAM отвести все остальное. В этом случае задачей операционной могло бы быть оптимальное распределение данных, позволяющее использовать память SRAM. В принципе, память SRAM в подобной ситуации используется в качестве расширения набора регистров процессора.
Хотя такая реализация возможна, она нежизнеспособна. Если игнорировать проблему отображения физических ресурсов такой памяти SRAM в виртуальные адресные пространства процессов (что само по себе ужасно трудно), то при таком подходе потребуется, чтобы в каждом процессе с помощью программного обеспечения выполнялось администрирование распределения этой области памяти. Размер этой области памяти может варьироваться от процессора к процессору (поскольку процессоры имеют различное количество дорогой памяти SRAM). Каждый модуль, который входит в состав программы, будет претендовать на свою долю в быстрой памяти, из-за чего возникнут дополнительные расходы на синхронизацию запросов. В общем, выгоды, полученные от использования быстрой памяти, будут полностью съедены накладными расходами на управление ресурсами.
Поэтому вместо того, чтобы использовать память SRAM под управлением операционной системы или пользователя, она становится ресурсом, использование и администрирование которого осуществляется процессорами так, что это не видно ни операционной системе, ни пользователю. В этом режиме память SRAM используется для создания временных копий (другими словами, кэширования) данных, которые находятся в основной памяти, но которые, возможно, совсем скоро будут использоваться процессором. Это возможно, поскольку коду программы и данным свойственны пространственная и временная компактность. Это значит, что если рассматривать короткие промежутки времени, существует достаточно большая вероятность, что тот же самый код или данные будут использованы повторно. В случае кода, это означает, что в коде, скорее всего, есть много циклов, так что один и тот же код запускается на выполнение снова и снова (идеальный случай пространственной компактности - spatial locality). Доступ к данным также в идеале ограничен небольшим пространством. Даже если участки памяти, используемые в течение коротких периодов времени, расположены не рядом друг с другом, есть высокая вероятность того, что в скором времени будут повторно использованы те же самые данные (временная компактность - temporal locality). Для кода примером является цикл, в котором осуществляется вызов функции, а функция находится в другом месте адресного пространства. Эта функция может находиться в памяти далеко от вызова, но вызовы этой функции будут близки во времени. Для данных это означает, что некоторое количество памяти, используемое за один раз (размером в рабочий набор данных) в идеале ограничено, но участки этой используемой память из-за случайного характера доступа к оперативной памяти, могут располагаться далеко друг от друга. Реализация существования этой компактности является ключом к концепции кэш-памяти процессора в том виде, как мы используем ее сегодня.
Простой подсчет может показать, насколько теоретически может быть эффективна кэш-память. Предположим, что на доступ к оперативной памяти тратится 200 циклов, а на доступ к кэш-памяти - 15 циклов. Тогда, если в коде 100 раз используются 100 элементов данных, то в случае, если кэш-память отсутствует, на операции с памятью будет потрачено 2000000 циклов и, если данные находятся в кэше, на это будет потрачено только 168 500 циклов. Это улучшение составляет 91,5%.
Размер памяти SRAM, используемой для кэша, во много раз меньше, чем основная память. По опыту автора, имевшего дело рабочими станциями, имеющим кэш-память процессора, размер кэша всегда приблизительно равен 1/1000-ой от размера оперативной памяти (сегодня: 4 Мб кэш-памяти и 4 Гб оперативной памяти). Это само по себе не является проблемой. Если размер рабочего набора (набора данных, с которым в текущий момент осуществляется работа) меньше, чем размер кэш-памяти, то проблем нет. Но в компьютеры большое количество памяти не добавляют без всяких причин. Рабочий набор непременно больше, чем кэш-память. Это особенно актуально в системах, где запускаются несколько процессов и размер рабочего набора равен сумме размеров рабочих наборов всех отдельных процессов и ядра.
Все, что нужно, чтобы использовать кэш-память ограниченного размера, это набор хороших стратегий, которые определяют, что в любой заданный момент времени должно кешироваться. Поскольку не все данные рабочего набора используется одновременно, мы можем на время заменять некоторые данные, находящиеся в кэш-памяти, другими данными. И, возможно, это удастся сделать раньше, чем данные станут, на самом деле, необходимы. Такая предварительная загрузка позволит скрыть некоторые затраты на доступ к основной памяти, поскольку загрузка происходит асинхронно относительно выполнения программы. Все эти методы и многое другое можно использовать для того, чтобы кэш-память казалась больше, чем она есть на самом деле. Мы обсудим их в разделе 3.3. Использование всех этих методов позволит программисту помочь работе процессора. Как это сделать, мы обсудим в разделе 6.
Назад | Оглавление | Вперед |