Строка, помеченная как "New" введена, для создания переменной цикла I. Строка MOV EBX, [EBP-$10] копирует переменную I в регистр EBX. Следующая строка копирует переменную Stop в регистр ECX. Затем в строке CMP EBX, ECX они сравниваются, и инструкцией JBE @LOOPSTART управление передается в начало цикла, если I меньше или равно Stop. Поскольку мы используем регистр EBX и он не разрешен для свободного использования, поэтому мы его сохраняем его в стеке.
Мы решили проверять окончания цикла в начале цикла. Данный тест разделен компилятором на две части. Перед входом в цикл проверяется, что цикл может выполниться как минимум один раз и реальное окончание цикла проверяется в конце. Такая техника оптимизации называется инверсия цикла. Теперь мы сменим цикл так, что бы такую оптимизацию. Потом мы увидим преимущества от этой оптимизации.
Code: |
function ForLoopBASM4(Start, Stop : Integer) : Integer; asm push ebp push ebx mov ebp,esp add esp,-$14 mov [ebp-$08],edx mov [ebp-$04],eax //Result := 0; xor eax,eax mov [ebp-$0c],eax //for I := Start to Stop do mov eax,[ebp-$04] mov edx,[ebp-$08] //sub edx,eax //jl @LoopEnd mov [ebp-$10],eax //begin @LoopStart : mov ebx, [ebp-$10] mov ecx, [ebp-$08] cmp ebx, ecx ja @LoopEnd //Result := Result + I; mov eax,[ebp-$10] add [ebp-$0c],eax inc dword ptr [ebp-$10] //end; //mov ebx, [ebp-$10] //mov ecx, [ebp-$08] //cmp ebx, ecx //jbe @LoopStart jmp @LoopStart @LoopEnd : mov eax,[ebp-$0c] mov esp,ebp pop ebx pop ebp end; |
Проверка на окончания цикла была перемещена в начало и тест был инвертирован. На месте старой проверки теперь находится безусловный переход. Этот переход единственное, что сделано по отношению к инверсной оптимизации. В не оптимизированном цикле было два перехода, оптимизированным один. Проверка вверху, то что проверяется всегда. Start был на Stop и теперь лишнее и поэтому удалено. Перед проведением измерений по эффекту от двух оптимизаций, хорошей идеей будет оптимизировать часть или все, что возможно, стек в регистры, регистры в стек. Данный процесс называется – размещение в регистрах и это одна из самых важных оптимизаций на всех архитектурах, но это особенно важно для архитектуры Intel, поскольку в ней малое количество доступных регистров. Если нет места для всех переменных в регистрах, то важно определить какие переменные поместить в регистры. Инструкции MOV в теле цикла наиболее важные кандидаты на это. Они выполняются большое количество раз. Инструкции за пределами цикла выполняются только раз. Переменные внутри цикла первыми должны быть размещены в регистрах. Это переменные I, Stop и Result. Теперь рассмотрим использование регистров для временных переменных. Если переменная всегда копируется в тот же самый временный регистр, то ее желательно разместить в этом регистре. Переменная Stop в регистре EDX при входе в функцию и также используется как временный регистр, во всех строках, кроме двух строк. Здесь есть две строки в цикле, которые мы добавили, изменим их
mov ecx, [ebp-$08]
cmp ebx, ecx
на
mov edx, [ebp-$08]
cmp ebx, edx
Регистр EAX используется для Start вверху функции и как Result в остальной части функции. Если нет перекрытия по использованию, то мы можем использовать EAX для Result, как только Start прекратит его использования. После того, как Start назначен переменной I (MOV [EBP-$10], EAX), он больше нигде не используется и регистр EAX свободен для использования для Result, кроме тех строк, где EAX используется как временное хранилище для I.
mov eax,[ebp-$10]
add [ebp-$0c],eax
inc dword ptr [ebp-$10]
после того, как ECX прекращает использоваться, мы можем его использовать как временное хранилище для I, вместо EAX.
mov ecx,[ebp-$10]
add [ebp-$0c],ecx
inc dword ptr [ebp-$10]
Подведем итог по первой части оптимизации по использованию регистров: Result в EAX, I в ECX и Stop в EDX.
В начале заменим все строки со Stop. [EBP-$08] на использование EDX.
Code: |
function ForLoopBASM6(Start, Stop : Integer) : Integer; asm push ebp push ebx mov ebp,esp add esp,-$14 //mov [ebp-$08],edx mov edx,edx mov [ebp-$04],eax //Result := 0; xor eax,eax mov [ebp-$0c],eax //for I := Start to Stop do mov eax,[ebp-$04] //mov edx,[ebp-$08] mov edx,edx mov [ebp-$10],eax //begin @LoopStart : mov ebx, [ebp-$10] //mov edx, [ebp-$08] mov edx, edx cmp ebx, edx ja @LoopEnd //Result := Result + I; mov ecx,[ebp-$10] add [ebp-$0c],ecx inc dword ptr [ebp-$10] //end; jmp @LoopStart @LoopEnd : mov eax,[ebp-$0c] mov esp,ebp pop ebx pop ebp end; |
Затем распределим ECX для I, заменив [EBP-$10] на ECX.
Code: |
function ForLoopBASM7(Start, Stop : Integer) : Integer; asm push ebp push ebx mov ebp,esp add esp,-$14 mov edx,edx mov [ebp-$04],eax //Result := 0; xor eax,eax mov [ebp-$0c],eax //for I := Start to Stop do mov eax,[ebp-$04] mov edx,edx //mov [ebp-$10],eax mov ecx,eax //begin @LoopStart : //mov ebx, [ebp-$10] mov ebx, ecx mov edx, edx cmp ebx, edx ja @LoopEnd //Result := Result + I; //mov ecx,[ebp-$10] mov ecx,ecx add [ebp-$0c],ecx //inc dword ptr [ebp-$10] inc ecx //end; jmp @LoopStart @LoopEnd : mov eax,[ebp-$0c] mov esp,ebp pop ebx pop ebp end; |
И на конец используем EAX для Result. Поскольку EAX также используется вверху функции для Start и как временный регистр для инициализации Result нулем, то мы должны добавить несколько строк для копирования Result в EAX после как EAX более нигде не будет использоваться для других целей.
Code: |
function ForLoopBASM8(Start, Stop : Integer) : Integer; asm push ebp push ebx mov ebp,esp add esp,-$14 mov edx,edx mov [ebp-$04],eax //Result := 0; xor eax,eax mov [ebp-$0c],eax //for I := Start to Stop do mov eax,[ebp-$04] mov edx,edx mov ecx,eax mov eax, [ebp-$0c] //New //begin @LoopStart : mov ebx, ecx mov edx, edx cmp ebx, edx ja @LoopEnd //Result := Result + I; mov ecx,ecx //add [ebp-$0c],ecx add eax,ecx inc ecx //end; jmp @LoopStart @LoopEnd : //mov eax,[ebp-$0c] mov eax,eax mov esp,ebp pop ebx pop ebp end; |
Просьба писать ваши замечания, наблюдения и все остальное,
что поможет улучшить предоставляемую информацию на этом сайте.
ВСЕ КОММЕНТАРИИ МОДЕРИРУЮТСЯ ВРУЧНУЮ, ТАК ЧТО СПАМИТЬ БЕСПОЛЕЗНО!