Кэш предназначен для уменьшения количества раз, которое ЦП будет останавливать в ожидании выполнения запроса памяти (избегая задержки памяти ), и в качестве второго эффекта, возможно, для уменьшения общего объема данных, которые должны быть переданы (сохранение пропускная способность памяти ).
Методы, позволяющие избежать страданий из-за задержек при извлечении памяти, обычно являются первыми, на которые стоит обратить внимание, а иногда и помогают. Ограниченная пропускная способность памяти также является ограничивающим фактором, особенно для многоядерных и многопоточных приложений, где многие потоки хотят использовать шину памяти. Другой набор методов помогает решить последнюю проблему.
Улучшение пространственной локальности означает, что вы гарантируете, что каждая строка кэша используется полностью после ее сопоставления с кэшем. Когда мы рассмотрели различные стандартные тесты, мы увидели, что удивительно большая часть из них не использует 100% извлеченных строк кэша до того, как строки кэша будут удалены.
Улучшение использования строк кэша помогает в трех отношениях:
- Он имеет тенденцию помещать более полезные данные в кеш, существенно увеличивая эффективный размер кеша.
- Он имеет тенденцию помещать более полезные данные в одну и ту же строку кэша, увеличивая вероятность того, что запрашиваемые данные могут быть найдены в кэше.
- Это уменьшает требования к пропускной способности памяти, поскольку будет меньше выборок.
Общие методы:
- Используйте меньшие типы данных
- Организуйте свои данные, чтобы избежать дыр в выравнивании (сортировка членов структуры по уменьшению размера - один из способов)
- Остерегайтесь стандартного распределителя динамической памяти, который может создавать дыры и распространять ваши данные в памяти при нагревании.
- Убедитесь, что все смежные данные действительно используются в горячих циклах. В противном случае рассмотрите возможность разбиения структур данных на горячие и холодные компоненты, чтобы в горячих циклах использовались горячие данные.
- избегайте алгоритмов и структур данных, которые демонстрируют нерегулярные шаблоны доступа, и предпочитайте линейные структуры данных.
Также следует отметить, что существуют другие способы скрыть задержку памяти, кроме использования кешей.
Современные процессоры часто имеют один или несколько аппаратных предварительных загрузчиков . Они тренируются по промахам в тайнике и пытаются выявить закономерности. Например, после нескольких пропусков в последующих строках кеша, средство предварительной выборки hw начнет извлекать строки кеша в кеш, предвидя потребности приложения. Если у вас есть обычный шаблон доступа, аппаратный предварительный выборщик обычно делает очень хорошую работу. И если ваша программа не отображает обычные шаблоны доступа, вы можете улучшить ситуацию, добавив инструкции предварительной выборки самостоятельно.
Перегруппировав инструкции таким образом, чтобы те, которые всегда пропускали в кеше, находились близко друг к другу, ЦП иногда может перекрывать эти выборки, так что приложение выдерживает только одно попадание задержки ( параллелизм на уровне памяти ).
Чтобы уменьшить общее давление на шину памяти, вы должны начать работу с так называемой временной локализацией . Это означает, что вы должны повторно использовать данные, пока они еще не были удалены из кэша.
Объединение циклов, которые касаются одних и тех же данных (объединение циклов ), и использование методов перезаписи, известных как разбиение на листы или блокировка всех, стремятся избежать этих дополнительных выборок памяти.
Несмотря на то, что для этого упражнения по переписыванию есть несколько практических правил, вам, как правило, необходимо тщательно учитывать зависимости данных, переносимых в цикле, чтобы не влиять на семантику программы.
Это то, что действительно окупается в многоядерном мире, где вы, как правило, не увидите значительных улучшений пропускной способности после добавления второго потока.