Asembler x86/Optymalizacja

Historia

edytuj

W przypadku starszych procesorów X86 (szczególnie tych bez kolejkowania instrukcji oraz przewidywania skoku) duże znaczenie miały optymalizacje na poziomie pojedynczych instrukcji. Wśród procesorów 286 popularne było zastępowanie zerowania rejestru (MOV $0, %AX) wykonaniem na nim operacji XOR (XOR %AX, %AX), gdyż operacje arytmetyczne były o kilka taktów szybsze. Obecnie takie postępowanie jest niezalecane i spowalnia kod. Często zdarzało się również zastępowanie operacji zmniejszania/zwiększania o pewną małą wartość (1-2) taką samą liczbą instrukcji INC/DEC, co umożliwiało zyskanie jednego bajtu pamięci i małe przyspieszenie kodu. Obecnie tego typu optymalizacje mają sens jedynie w przypadku bardzo często wyknywanych fragmentów kodu.

Należy rozsądnie stosować optymalizację, gdyż w większości przypadków zmniejsza czytelność i estetykę kodu lub zwiększa jego rozmiar. Gdy kod jest bardzo rzadko wykonywany, nie ma sensu go optymalizować, gdyż może to zwiększyć prawdopodobieństwo popełnienia błędu.

Metody

edytuj

Wyróżniamy dwa rodzaje optymalizacji: optymalizację czasową i pamięciową.

Optymalizacja czasowa

edytuj

Optymalizacja mająca na celu zmniejszenie czasu wykonywania się programu.

Najważniejsze techniki optymalizacyjne:

  • Zastępowanie mnożenia/dzielenia przez potęgi dwójki operacjami przesunięcia bitowego. Czasami również da się zastąpić operacje mnożenia przez 3, 5 i 9 operacjami postaci: LEA (EAX, EAX, 2), EAX dla mnożenia przez 3, LEA (EAX, EAX, 4), EAX dla mnożenia przez 5 i LEA (EAX, EAX, 8), EAX dla mnożenia przez 9.
  • Wklejenie kodu funkcji w żądane miejsce zamiast jej wywoływania (np. przez makro).
  • Rozwijanie krótkich pętli do postaci kilku powtórzeń - metoda ta umożliwia zaoszczędzenie na instrukcjach sprawdzenia warunku oraz instrukcjach skoku. Ułatwia także procesorom równoległe obliczenie niektórych wartości, gdyż nie muszą one wtedy przewidywać kierunku skoku.
  • Wykorzystanie nowoczesnych zestawów instrukcji: MMX i SSE*.
  • Używanie rejestrów zamiast pamięci - co prawda nakłada to na programistę obowiązek pamiętania która zmienna jest w którym rejestrze, ale odwołanie do rejestru jest dużo szybsze od odwołania do pamięci (a nawet do cache procesora).


Pomijam tu oczywiste metody, takie jak wyczyszczenie kodu z niepotrzebnych instrukcji.

Optymalizacja pamięciowa

edytuj

Optymalizacja mająca na celu zmniejszenie wymagań pamięciowych programu.

  • Używanie rejestrów zamiast pamięci
  • Rozsądne zmniejszenie rozmiaru danych, w krytycznych strukturach zalecane jest zapisywanie zmiennych za pomocą pól bitowych (przykład zapisu za pomocą pól bitowych: rejestry statusu kontrolera przerwań, DMA, klawiatury).