Влияние размера исполняемого кода на производительность
В целом, кодовый кэш устроен практически точно также, как и кэш данных. Даже проще, ведь машинные команды, в отличии от данных, не требуют поддержки операций записи. (Попытка модификации исполняемого кода приводит к непосредственному обновлению кэшируемой памяти и перезагрузке соответствующей стоки кодового кэша, поэтому во избежание падения производительности прибегать к самомодифицирующемуся коду в глубоко вложенных циклах категорически не следует).
Если не углубляться в детали, можно сказать, что влияние размера исполняемого кода на производительность подчиняется тем же законам, что и размер читаемого (не модифицируемого!) блока данных.
Давайте проследим, как меняется скорость исполнения блока кода при увеличении его размеров. Это не такая простая задачка! Ведь, в отличии от обработки данных, мы не можем не прибегая к самомодифицирующемуся коду менять размер исполняемого блока по своему желанию. Тем не менее, выход есть и довольно элегантный. Достаточно лишь слезть с высокоуровневых языков и обратится к макроассемблеру, развитые препроцессорные средства которого позволят нам генерировать блоки исполняемого кода произвольного размера.
Для предотвращения возможных побочных эффектов мы будем оформлять каждый блок кода отдельной программой и, чтобы не задавать параметры трансляции каждый раз вручную, воспользуемся пакетным файлом.
В результате, у нас получится следующее:
; CODE_SIZE EQU ? ; // Макрос CODE_SIZE автоматически генерируется
; // пакетным файлом-транслятором
; /*--------------------------------------------------------------------------
; *
; * МАКРОС, ГЕНЕРИРУЩИЙ N машинных команд "NOP
; *
; * (команда NOP буквально обозначает "нет операции"
; * и занимает ровно один байт, т.е. N команд NOP
; * дают N байт исполняемого кода)
; *
; -------------------------------------------------------------------------*/
NOPING MACRO N ; // НАЧАЛО МАКРОСА //
_N = N ; _N := N (получаем переданный макросу аргумент)
_A = 0 ; _A -- переменная-счетчик цикла
WHILE _A NE _N ; while(_A <= _N){
NOP ; вставляем в исходный текст "NOP"
_A = _A + 1 ; _A++;
ENDM ; }
ENDM ; // КОНЕЦ МАКРОСА //
; /*--------------------------------------------------------------------------
; *
; * ВЫЗОВ МАКРОСА ДЛЯ СОЗДАНИЯ БЛОКА ИЗ CODE_SIZE KB команд NOP
; *
; --------------------------------------------------------------------------*/
NOPING CODE_SIZE*1024
Листинг 2 [Cache/ code.cache.size.xm] Ассемблерный листинг программы, генерирующей произвольное количество машинных операций NOP
#include
"code.cache.size.h" ; ßэтот файл формируется пакетным транслятором
; и содержит определение CODE_SIZE
#include <DoCPU.h>
main()
{
int a;
A_BEGIN(1); ; ß начало замера времени выполнения
DoCPU(&a); ; выполняем блок из CODE_SIZE команд NOP
A_END(1); ; ß конец замера времени выполнения
// вывод результатов замера на экран
printf("%03d\t %d\n", CODE_SIZE, Ax_GET(1));
}
Листинг 3 [Cache/code.cache.size.c] Пример, демонстрирующий последствия выхода за кэш перового (второго) уровня
@ECHO OFF
IF #%1#==#MAKE_FOR# GOTO make_it
REM MAKE ALL
ECHO
= = = СБОРКА ПРИМЕРА, ДЕМОНСТРУЮЩЕГО ОПРЕДЕЛЕНИЕ РАЗМЕРА КОДОВОГО КЭША = = =
ECHO
Утилита к книге \"Техника оптимизации программ" /* название рабочее */
ECHO @ECHO OFF > CODE.CACHE.SIZE.RUN.BAT
ECHO ECHO
= = демонстрация определения размера кэша = = >> CODE.CACHE.SIZE.RUN.BAT
ECHO ECHO
Утилита к книге "Техника оптимизации программ" >> CODE.CACHE.SIZE.RUN.BAT
ECHO ECHO N NOP ...CLOCK... >> CODE.CACHE.SIZE.RUN.BAT
ECHO ECHO ------------------------------------------------- >> CODE.CACHE.SIZE.RUN.BAT
FOR %%A IN (2,4,8,16,32,64,128,256,512,1024,2048) DO CALL %0 MAKE_FOR %%A
ECHO DEL %%0 >> CODE.CACHE.SIZE.RUN.BAT
GOTO end
:make_it
ECHO /%0/%1/%2 *
SHIFT
ECHO CODE_SIZE EQU %1 > CODE.CACHE.SIZE.MOD
ECHO #define CODE_SIZE %1 > CODE.CACHE.SIZE.H
TYPE CODE.CACHE.SIZE.XM >> CODE.CACHE.SIZE.MOD
CALL CLOCK.MAKE.BAT CODE.CACHE.SIZE.C > NUL
DEL CODE.CACHE.SIZE.MOD
DEL CODE.CACHE.SIZE.H
IF NOT EXIST CODE.CACHE.SIZE.EXE GOTO err
IF EXIST CODE.CACHE.SIZE.%1.EXE DEL CODE.CACHE.SIZE.%1.EXE
REN CODE.CACHE.SIZE.EXE CODE.CACHE.SIZE.%1.EXE
ECHO CODE.CACHE.SIZE.%1.EXE >> CODE.CACHE.SIZE.RUN.BAT
ECHO DEL CODE.CACHE.SIZE.%1.EXE >> CODE.CACHE.SIZE.RUN.BAT
GOTO end
:err
ECHO -ERR ошибка
компиляции! подробнее см. CODE.CACHE.SIZE.ERR
TYPE CODE.CACHE.SIZE.ERR
EXIT
:end
Листинг 4 [Cache/code.cache.size.make.bat] Пакетный файл, выполняющий трансляцию тестовой программы