Вы действительно спрашиваете о предварительной загрузке, а не о логике управления циклом.
В общем, производительность цикла не будет зависеть от логики управления (то есть, приращение / уменьшение и условие, которое проверяется каждый раз через). Время, необходимое для выполнения этих вещей, не имеет значения, за исключением очень узких петель. Если вам это интересно, взгляните на ответ Джона Кнеллера , чтобы узнать подробности о регистре счетчиков 8086 и узнать, почему в прежние времена обратный отсчет был более эффективным. Как говорит Джон, предсказание ветвления (а также спекуляция) могут играть здесь роль в производительности, как и предварительная выборка команды .
Порядок итерации может значительно повлиять на производительность, когда он меняет порядок, в котором ваш цикл касается памяти. Порядок, в котором вы запрашиваете адреса памяти, может влиять на то, что выводится в ваш кеш , а также на то, что выталкивается из вашего кеша, когда больше нет места для извлечения новых строк кеша. Необходимость обращаться к памяти чаще, чем нужно, намного дороже, чем сравнивать, увеличивать или уменьшать. На современных процессорах от процессора к памяти может потребоваться тысячи циклов, и вашему процессору может потребоваться простаивать в течение некоторого или всего этого времени.
Вы, вероятно, знакомы с кешами , поэтому я не буду вдаваться во все эти детали здесь. Что вы можете не знать, так это то, что современные процессоры используют целую серию prefetchers , чтобы попытаться предсказать, какие данные вам понадобятся дальше на разных уровнях иерархии памяти. Как только они предсказывают, они пытаются извлечь эти данные из памяти или кэшей более низкого уровня, чтобы у вас было то, что вам нужно, когда вы приступаете к обработке. В зависимости от того, насколько хорошо они получают то, что вам нужно, ваша производительность может улучшиться или не улучшиться при их использовании.
Взгляните на Руководство Intel по оптимизации для аппаратных устройств предварительной выборки . В списке четыре предвыборщика; два для NetBurst чипов:
- Аппаратная предварительная выборка NetBurst может обнаруживать потоки обращений к памяти в прямом или обратном направлениях и будет пытаться загрузить данные из этих мест в кэш L2.
- NetBurst также имеет предварительный выборщик смежных строк кэша (ACL) , который автоматически загружает две соседние строки кэша при извлечении первой.
и два для Core :
- Core имеет немного более сложный аппаратный предварительный выборщик; он может обнаруживать пошаговый доступ в дополнение к потокам смежных ссылок, поэтому будет лучше, если вы будете проходить через массив через каждый второй элемент, каждый четвертый и т. д.
- В ядре также есть средство предварительной выборки ACL, например NetBurst.
Если вы перебираете массив вперед, вы сгенерируете несколько последовательных, обычно непрерывных ссылок на память. Предварительные извещатели ACL будут гораздо лучше работать с прямыми циклами (потому что вы в конечном итоге будете использовать эти последующие строки кэша), чем с обратными циклами, но вы можете нормально делать обратные ссылки на память, если предварительные извещатели могут это обнаружить (как в случае с оборудованием). предварительная выборка). Аппаратные средства предварительной выборки на Ядре могут обнаруживать шаги, что полезно для более сложных обходов массива.
Эти простые эвристики могут в некоторых случаях приводить к неприятностям. Например, Intel на самом деле рекомендует отключить предварительную выборку смежных строк кэша для серверов, поскольку они имеют тенденцию делать больше случайных ссылок на память, чем настольные пользовательские машины. Вероятность использования , а не при использовании смежной строки кэша на сервере выше, поэтому выборка данных, которые вы фактически не собираетесь использовать, приводит к загрязнению вашего кэша (заполнению его нежелательными данными), и производительность снижается. Для получения дополнительной информации о решении этой проблемы обратитесь к этой статье Supercomputing 2009 на , в которой используется машинное обучение для настройки средств предварительной выборки в крупных центрах обработки данных . Некоторые ребята из Google на этой бумаге; производительность - это то, что их очень беспокоит.
Простая эвристика не поможет вам с более сложными алгоритмами, и вам, возможно, придется задуматься о размерах кэшей L1, L2 и т. Д. Например, для обработки изображений часто требуется выполнить какую-либо операцию с подразделами 2D-изображения, но порядок, в котором вы пересекаете изображение, может влиять на то, насколько полезные его части остаются в вашем кэше, не будучи выселенными. Взгляните на обходы Z-порядка и циклическое разбиение , если вы заинтересованы в подобных вещах. Это довольно простой пример отображения двумерного местоположения данных изображения в одномерное расположение памяти для повышения производительности. Это также область, где компиляторы не всегда могут реструктурировать ваш код наилучшим образом, но реструктуризация кода C вручную может значительно повысить производительность кэша.
Надеюсь, это даст вам представление о том, как порядок итераций влияет на производительность памяти. Это зависит от конкретной архитектуры, но идеи являются общими. Вы должны уметь понимать предварительную выборку в AMD и Power, если вы понимаете это в Intel, и вам не обязательно знать сборку, чтобы структурировать свой код, чтобы использовать преимущества памяти. Вам просто нужно немного знать архитектуру компьютера.