Во втором фрагменте изменение j
на каждой итерации создает шаблон с низкой пространственной локальностью. Помните, что за кулисами ссылка на массив вычисляется:
( ((y) * (row->width)) + (x) )
Рассмотрим упрощенный кэш L1, в котором достаточно места только для 50 строк нашего массива. За первые 50 итераций вы неизбежно заплатите за 50 промахов кэша, но что потом? Для каждой итерации с 50 по 99 вы все равно будете промахиваться в кэше и должны получать из L2 (и/или ОЗУ и т. д.). Затем x
изменяется на 1, а y
начинается заново, что приводит к еще одному промаху кеша, потому что первая строка вашего массива была вытеснена из кеша, и так далее.
В первом фрагменте такой проблемы нет. Он обращается к массиву в порядке строк, что обеспечивает лучшую локальность — вам нужно платить только за промахи в кэше не более одного раза (если строка вашего массива отсутствует в кэше в момент цикл начинается) за строку.
При этом этот вопрос очень зависит от архитектуры, поэтому вам нужно будет принять во внимание особенности (размер кэша L1, размер строки кэша и т. д.), чтобы сделать вывод. Вы также должны измерять оба способа и отслеживать аппаратные события, чтобы иметь конкретные данные, на основе которых можно делать выводы.
person
Michael Foukarakis
schedule
27.03.2012