leo > Assembly/Compiler Coding Rule 27. (H impact, L generality) Always put code and data on separate pages. Avoid self-modifying code wherever possible. If code is to be modified, try to do it all at once and make sure the code that performs the modifications and the code being modified are on separate 4 KB pages or on separate aligned 1 KB subpages.
Теоретически незнаю, а практически разнос кода и данных с выравниванием на 1Кб-4Кб ничего не улучшил, более точный замер (160 тиков против 162) получил, если кроме выравнивания основного цикла next: на 16, ещё выровнять testloop: на 4. Даже не 160, а 153 если выкинуть 14 nop'ов (для выравнивания на 16 и участвующие в тестировании), они идут 2 за такт
Вот интересный тест (инструкция "mov eax,[mem]" при первом проходе), показывающий, что расположение данных в отдельной секции, иногда может стоить более миллиона тактов! (тестил на w2ksp4) В зависимости от того, где расположен mem, эта инструкция может выполнятся за 1 такт, за несколько сотен тактов, за несколько десятков тысяч, а иногда за много сот тысяч тактов (даже за миллион), и это при том, что mem выровнен. Код (Text): ---------------------------------------------------- "1.exe" "100.exe" "10000.exe" "100000.exe" ---------------------------------------------------- 0000000001 0000000165 0000026265 0021191825 0000000001 0000000001 0000000001 0000000001 0000000001 0000000001 0000000001 0000000001 0000000001 0000000001 0000000001 0000000001 0000000001 0000000001 0000000001 0000000001 0000000001 0000000001 0000000001 0000000001 0000000001 0000000001 0000000028 0000000031 0000000001 0000000001 0000000001 0000000001 0000000001 0000000001 0000000001 0000000001 0000000001 0000000001 0000000001 0000000001 ---------------------------------------------------- OK OK OK OK ---------------------------------------------------- 518746522__memtest.zip
S_T_A_S_ Вот, вот, я тоже в конце концов набрел на этот рул. И чуть дальше тоже четко сказано: "Software should avoid writing to a code page in the same 1 KB subpage of that is being executed". bogrus > "практически разнос кода и данных с выравниванием на 1Кб-4Кб ничего не улучшил" Это видимо на P2,P3 (P6 family). А для P4 речь идет не столько о выравнивании, сколько именно о разносе изменяемых данных и кода. В рассматриваемом случае исполняемый кусок кода не должен производить запись в пределах текущего килобайта адресного пространства - иначе огромные штрафы и пенальти. Интел как всегда не все договаривает, но после копания в мануалах я понял примерно следующее. И в P6 family и в P4 осуществляется контроль записи в кэш на предмет модификации кода. При обнаружении такой записи соответвующие строки кэша помечаются как модифицированные и инвалидные. А вот дальнейшие действия различаются в зависимости от семейства процессора и "дальности" от\до исполняемого кода. В P6 family инструкции выбираются 16-байтными блоками из одной или двух (при перекрытии) 32-байтных строк кэша и декодируются "на лету". Если происходит модификация внутри текущего блока или ближайших, готовящихся к загрузке, то получаем печальные последствия в смысле задержки (сброс конвейера и invalidate cache line). Но в нашем случае микса данных и кода ничего страшного не происходит, если в предах 32-64 байт кода не происходит запись в эти же линии кэша. В P4 ситуация иная, т.к. помимо кэширования данных и кода в L2, в L1 кэшируются декодированные микрооперации и интелы при записи по адресам уже декодированного кода не придумали ничего лучшего, как делать invalidate всего trace cache ("a write or a snoop of an instruction in a code segment, where the target instruction is already decoded and resident in the trace cache, invalidates the entire trace cache"). Звучит как приговор окончательный, но потом в optimization находим смягчающие обстоятельства - упомянутое правило №27: оказывается достаточно не модифицировать данные или код в одном килобайтном блоке с исполняемым кодом. Что собственно и подтверждается практикой.