как предварительно загрузить большой массив для кэширования параллельно?

Моя машина имеет архитектуру Intel IvyBride. Кэш L3 у меня 12Мб, 16-ассоциативный, размер строки кэша 64Б.

У меня есть очень большой длинный массив [12MB/sizeof(long)] в моей программе. Я хочу предварительно загрузить большой массив перед выполнением программы, чтобы ускорить процесс инициализации.

Один из способов, который я могу придумать для достижения этого, - это доступ ко всему массиву от индекса 0 до конца массива "последовательно". Однако время доступа ко всему массиву слишком велико. Этот подход использует одно ядро.

Другой способ, который я могу сделать, - это использовать несколько потоков для параллельного доступа ко всему массиву. Каждый поток обращается только к части массива. Поскольку эти потоки могут выполняться на нескольких ядрах, это может ускорить предварительную загрузку массива в общий кэш. Однако для этого подхода требуются многоядерные процессоры для запуска этих потоков.

У меня вопрос: есть ли какое-либо оборудование (например, DMA), которое я могу использовать, чтобы выполнить команду и заставить оборудование предварительно загрузить набор данных в общий кэш?


person Mike    schedule 22.04.2014    source источник
comment
Даже если есть способ (а я не думаю, что есть), процессор находится в лучшем положении, чем вы, чтобы решить, как использовать эти кеши.   -  person z̫͋    schedule 22.04.2014
comment
Обратите внимание, что ваш кеш L3, скорее всего, является унифицированным кешем, то есть он обрабатывает как данные, так и код и используется совместно с другими ядрами, и, если бы вы могли заставить его хранить ваш массив и ничего, кроме вашего массива, тогда не было бы ни одного вашего код в кеше. Любой код, который вы пытались запустить для предварительной загрузки вашего массива, также вытолкнет части вашего массива. Лучше просто позволить процессору выяснить, что там должно быть, возможно, с некоторыми подсказками предварительной выборки достаточно заранее, чтобы загрузить следующий фрагмент вашего массива до того, как он вам понадобится...   -  person twalberg    schedule 22.04.2014


Ответы (2)


Это возможно при некоторых условиях — проверьте, поддерживает ли ваш ЦП «DCA» (прямой доступ к кэшу) и можете ли вы активировать эту функцию. Это может быть полезно: https://www.myricom.com/software/myri10ge/790-how-do-i-enable-intel-direct-cache-access-dca-with-the-linux-myri10ge-driver.html

Я не думаю, что вам это действительно нужно, последовательное прохождение всего массива должно быть очень эффективным, поскольку оно будет легко распознано процессором как последовательный поток и вызовет предварительную выборку HW. Так как это IvyBridge, даже линейное пересечение страниц должно быть быстрым, поскольку он может выполнять предварительную выборку на следующую физическую страницу. Может быть небольшая оптимизация при параллельном доступе к нескольким страницам (в том числе с точки зрения задержки промаха TLB), но в конечном итоге все сводится к вопросу — можете ли вы насытить свою память Bandwidth. Одно ядро, вероятно, столкнется с узким местом на границе ядро/L3, поэтому оптимальным способом будет распределение работы путем запуска HW-потока на каждом ядре, каждый в своем сегменте (размер может составлять одну страницу 4k на итерацию, но более крупные фрагменты также будут пользоваться преимуществом локальности карты страниц в каждом ядре)

Однако у вас может быть более серьезная проблема, чем доступ к данным, и это состоит в том, чтобы убедить L3 сохранить их там. Говорят, что IvyBridge использует политику динамической замены в L3, а это означает, что он будет спрашивать себя, кто использует все эти данные, и, поскольку вы просто предварительно загружаете их один раз, ответ, вероятно, будет «никто». В этот момент L3 может принять решение полностью отказаться от кэширования этого массива или записать новые блоки поверх старых.

Точное поведение зависит от фактической реализации, которая не была опубликована, но чтобы «обмануть» ее, я полагаю, вам придется обращаться к каждой строке данных более одного раза, прежде чем она будет «выброшена». Обратите внимание, что простое обращение к нему два раза подряд не поможет, поскольку оно уже находится в верхних кэшах, вам придется обращаться к нему на некотором расстоянии — не слишком маленьком, чтобы снова получить доступ к L3, но и не слишком большом, чтобы избежать его получения. выброшенный. Конечно, для тонкой настройки потребуется провести некоторые эксперименты.

РЕДАКТИРОВАТЬ:

Вот сообщение в блоге, посвященное политике замены L3 IvyBridges, о которой вам следует беспокоиться:
http://blog.stuffedcow.net/2013/01/ivb-cache-replacement/

Фактический процесс, конечно, должен вести себя хорошо, поскольку он должен быть пойман как использующий преимущества кэширования L3, это просто фаза предварительной загрузки, которая может доставить вам проблемы. Если обработка занимает относительно много времени, то начальные холодные промахи могут не стоить усилий по предварительной загрузке — остерегайтесь преждевременной оптимизации.

person Leeor    schedule 22.04.2014

DMA может предварительно загрузить ваш массив в основную память, например, из диск, он не работает с кэшем. Кроме того, время загрузки 12 МБ из ОЗУ в кеш ничтожно по сравнению с затратами на загрузку с диска в ОЗУ.

Для достижения последнего вы можете использовать mmap/MAP_POPULATE. Это оставит механизм, с помощью которого ваши данные предварительно загружаются в ОЗУ, до реализации ядра, но в целом будет быстрее, чем делать это вручную. Ядро, скорее всего, будет использовать для этого DMA или аналогичный механизм.

Загрузка данных в кеши — гораздо более серьезная проблема, особенно если учесть, что вы не можете контролировать удаление кешей. Самое близкое, что вы можете получить, это инструкция предварительной выборки (gcc __builtin_prefetch(const void *addr, ...)), но она даже не гарантирует предварительную выборку, плюс вы должны вызывать ее для каждой строки кэша, что, вероятно, займет больше времени, чем промах кэша.

person Sergey L.    schedule 22.04.2014