"Оптимизация для PENTIUM процессора" - читать интересную книгу автора

на 32:

AGAIN: MOV EAX, [ESI]
MOV EBX, [ESI + 13*4096 + 4]
MOV ECX, [ESI + 20*4096 + 28]
DEC EDX
JNZ AGAIN

Все три адреса имеют одинаковую установочную величину, поскольку в усеченном
виде они кратны 4096. Этот цикл будет исполняться крайне медленно. Как только
мы попытеамся прочесть данные в ECX процессор обнаружит, что нет возможных
строк, для кеширования данных, следовательно он удалит из кеша наиболее давно
используемые данные, т.е. строку кеширующую данные, которые мы читали в EAX
и заполнит ее данными с [ESI + 20*4096] по [ESI +20*4096 + 31], а затем
прочтет данные в ECX. Затем, когда мы попытаемся прочесть данные в EAX,
процессор снова обнаружит, что в кеше нет строки для кеширования EAX и нет
места, что бы ее добавить, значит ему придется снова отвергнуть наиболее
старую строку (для EBX), затем проведет кеширование для EAX и, наконец,
прочтет данные. Теперь тоже самое произойдет с EBX, и т.д... Таким образом
мы имеем постоянные промахи кеша, и на каждый проход по циклу тратиться около
60 тактов. Но если мы заменим третью строку на:

MOV ECX, [ESI + 20*4096 + 32]

Все! Мы пересекли 32 байтную границу, значит третья строка будет иметь другую
установочную величину, все строки будут постоянно находиться в кеше, не будет
промахов. Время затрачиваемое на один проход по циклу сократится до 3 тактов
(за исключением первого прохода, конечно) - очень значительное улучшение!

Конечно, определить это весьма не просто, особенно если данные разбросаны по
разным сегментам. Лучший способ, которым вы можете решить эту проблему -
хранить данные, использующиеся в критической части вашей программы, в пределах
одного блока длиной 8Кб максимум, или двух блоков, по 4Кб максимум (например
один блок для статических переменных, а другой для стека). Это гарантирует,
что все линии кеша будут использованы оптимально.

Если критическая часть вашей программы работает с большими структурами
данных или работает с произвольными адресами, то не плохим решением будет
хранить все часто используемые переменные (счетчики, указатели, контрольные
переменные, и т.д.) в одном непрерывном блоке, до 4Кб длиной, тогда у вас
останется полный набор строк кеша, для доступа к произвольным данным.
Поскольку вам скорее всего потребуется память в стеке, для параметров
подпрограмм или адресов возврата, то лучше будет скопировать все часто
используемые статические данные в динамические переменные, в стеке, а затем,
если необходимо, копировать их обратно, за пределами критической части
программы.

Во время чтения данных, не находящихся в L1, строка кеша сначала заполняется
из L2, скорость доступа которой примерно 200 ns (что требует 20 тактов в
100 MHz системе), но первые байты из запрашиваемой строки обычно доступны