Optymalizacja pętli
Optymalizacja pętli

Najczęstszymi miejscami krytycznymi programów są pętle

Optymalizacja pojedynczej instrukcji daje duży zysk dopiero gdy ta instrukcja jest powtarzana wielokrotnie np. w pętli.

Redukcja narzutu pętli

Narzut pętli to instrukcje potrzebne do skoku do początku pętli i do sprawdzenia warunku stopu.

Typowa pętla

for (int i = 0; i < n; i++) {
 // (loop body)
}

Kod bez optymalizacji

  mov ecx, [n]
  xor eax, eax    ; i = 0
LoopTop:
  cmp eax, ecx    ; i < n
  jge LoopEnd     ; wyjście gdy i >= n
  ; (loop body)
  add eax, 1      ; i++
  jmp LoopTop     ; skok do początku
LoopEnd:
Nie należy używać inc i dec bo są mniej wydajne i wprowadzają zależności od CF.

Redukcja jednego skoku przez przesunięcie warunku na koniec

  mov ecx, [n]  
  test ecx, ecx
  jng LoopEnd    ; opuść gdy n <= 0
  xor eax, eax   ; i = 0
LoopTop:
  ; (loop body)
  add eax, 1     ; i++
  cmp eax, ecx   ; i < n
  jl LoopTop     ; kontynuuj jeżeli i < n
LoopEnd:

Jeżeli nie potrzebujemy indeksu wewnątrz pętli możemy pozbyć się cmp odliczając w dół

  mov ecx, [n]  
  test ecx, ecx
  jng LoopEnd    ; opuść gdy n <= 0
LoopTop:
  ; (loop body)
  sub ecx, 1     ; n--
  jnz LoopTop     ; kontynuuj jeżeli n!=0
LoopEnd:
Nie należy używać jecxz i loop bo są mniej wydajne.

Iterowanie od tyłu po tablicy jest nieoptymalne bo cache'owanie działa lepiej do przodu na a nie wstecz. Można to osiągnąć indeksując relatywnie do końca tablicy wartościami i = -n .. 1

  mov ecx, [n]     ; liczba elementów
  lea esi, [Array+4*ecx] ; wskaźnik na zaostatni element
  neg ecx           ; i = -n
  jnl LoopEnd  ; pomiń jeżeli (-n) >= 0
LoopTop:
  ; (Loop body) : dodajemy jeden do każdego elementu
  add dword [esi+4*ecx], 1
  add ecx, 1    ; i++
  js LoopTop    ; powtorz jezeli i < 0
LoopEnd:

Przykład

Strona 95.


Tomasz Kapela, Kraków 2011-2020