Наши партнеры

UnixForum






Книги по Linux (с отзывами читателей)

Библиотека сайта rus-linux.net

На главную -> MyLDP -> Тематический каталог -> Аппаратное обеспечение

Что каждый программист должен знать о памяти. Часть 5.

Оригинал: What every programmer should know about memory. Memory part 5: What programmers can do.
Автор: Ulrich Drepper
Дата публикации: 23.10.2007
Перевод: Капустин С.В.
Дата перевода: 30.10.2009

6. Что могут делать программисты - оптимизация кэша

6.3 Предварительная загрузка

Цель предварительной загрузки состоит в том, чтобы спрятать задержку доступа к памяти. Конвейер команд и способность современных процессоров выполнять их не по порядку (OOO - out-of-order), позволяет прятать часть задержки, но, в лучшем случае, для тех доступов к памяти, которые находятся в кэше. Чтобы покрыть задержку доступа к основной памяти, очередь команд должна быть невероятно большой. Некоторые процессоры без OOO пытаются компенсировать это увеличением числа ядер, но это не равноценный обмен, если только весь используемый код не распараллелен.

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

6.3.1 Аппаратная предварительная загрузка

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

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

Модули, отвечающие за распознавание образцов, закреплены за соответствующими кэшами. Могут иметься модули предварительной загрузки для кэшей L1d и L1i. Очень большая вероятность того, что имеется модуль для кэшей L2 и выше. Этот модуль для L2 и выше разделяется между всеми ядрами и гиперпотоками, использующими общий кэш. Следовательно количество от восьми до шестнадцати отдельных потоков быстро уменьшается.

Предварительная загрузка имеет одну большую слабость - она ме может пересекать границы страницы. Причина должна быть очевидной, если вспомнить, что ЦПУ поддерживают выделение страниц по запросу. Если модуль предварительной загрузки сможет пересекать границы страницы, то такой доступ пожет породить событие операционной системы, делающее эту страницу доступной. Это само по себе плохо, особенно для производительности. Что еще хуже, это то, что модуль предварительной загрузки ничего не знает о семантике программы и самой операционной системы. Это может, следовательно, привести к предварительной загрузке страниц, которые в реальной жизни никогда бы не потребовались. Это означает, что модуль предварительной загрузки вышел бы край региона памяти, к которому процессор до этого имел доступ по узнаваемому образцу. Это не только возможно, так скорее всего и будет. Если процессор вызовет, как побочный эффект предварительной загрузки, запрос такой страницы, операционная система может быть совсем сбита с толку, если такой запрос в противном случая никогда бы не состоялся.

Следовательно важно понимать, что независимо от того, насколько хорош модуль предварительной загрузки в предсказании по образцу, программа будет получать промахи кэша на границе страницы, если она явно не делает предварительную загрузку или не читает из новой страницы. Это ещё одна причина оптимизировать размещение данных, как описано в разделе 6.2, чтобы минимизировать загрязнение кэша, не помещая туда несвязанные данные.

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

Если аппаратная предварительная загрузка запускается не в нужный момент, то можно сделать немного. Одна возможность - это попытаться выявить эту проблему и немного изменить расположение данных и/или кода. С этим придется, наверное, повозиться. Могут быть специалные локализованные решения, как например использование инструкции ud2 {или не-инструкции. Это рекомендованный неопределенный машинный код.} на процессорах x86 и x86-64. Эта инструкция, которая сама по себе не выполняется, используется после неявной инструкции перехода. Она используется как сигнал загрузчику инструкций, что процессор не должен тратить усилия на декодирование последующей памяти, так как выполнение продолжится с другого места. Однако это очень особая ситуация. В большинстве случаев приходится жить с этой проблемой.

Возможно полное или частичное отключение аппаратной предварительной загрузки для всего процессора. На процессорах Intel для этого используется регистр MSR - Model Specific Register (IA32_MISC_ENABLE, бит 9 на многих процессорах; бит 19 отключает только предварительную загрузку смежных строк кэша). Это, в большинстве случаев, должно происходить в ядре, так как это привилегированная операция. Если профилирование показывает, что важное приложение, работающее в системе, страдает от нехватки полосы пропускания и преждевременного исключения из кэша, возникающего из-за аппаратной предварительной загрузки, то использование MSR возможно.


Назад Оглавление Вперед