Optymalizacja operacji arytmetycznych
Nie raz juz na forum spotkalem sie z pewnymi niejanosciami jesli chodzi o zastepowanie pewnych operacji arytmetycznych operacjami bitowymi dlatego postaram sie to tutaj krotko wyjasnic kiedy je mozna stosowac:
zapis 2^x jakby ktos nie wiedzial jest to nic innego jak 2 do potegi x
Ponizsze dzialania mozna stosowac wylacznie dla liczb dodatnich
1. Mnozenie(*) i dzielenie(/)
a) dzialanie a * b mozna zastapic poprzez szybsza operacje przesuniecia bitowego w lewo: a << x jezeli b = 2^x.
Wiadomo ze mnozenie jest przemienne takze jezeli a = 2^x dzialanie mozna zastapic przez: b << x
Przykladowo:
poniewaz liczbe 32 mozna zapisac jako 2^5 powyzsza operacje mozna zapisac uzywajac operacji bitowej <<:
b) dzialanie a / b mozna zastapic poprzez szybsza operacje przesuniecia bitowego w prawo: a >> x jezeli b = 2^x
Przykladowo:
poniewaz liczbe 8 mozna zapisac jako 2^3 powyzsza operacje mozna zapisac uzywajac operacji bitowej >>:
2. Operacja modulo(%)
dzialanie a % b mozna zastapic przez operacje bitowa AND: a & (b-1) jezeli b = 2^x
Przykladowo:
poniewaz 4 mozna zapisac jako 2^2 powyzsze dzialanie mozna zapisac uzywajac operacji bitowej &:
Na koniec moze nie dzialanie arytmetyczne ale znana kazdemu a jak nie na pewno sie przyda optymalizacja tzw. operacji swap z pol. zamiana uzywajac operacji bitowej XOR(^)
zamiast:
w C:
w C++:
mozna to wykonac bez dodatkowej zmiennej uzywajac wlasciwosci operatora XOR:
w C:
w C++:
zapis 2^x jakby ktos nie wiedzial jest to nic innego jak 2 do potegi x
Ponizsze dzialania mozna stosowac wylacznie dla liczb dodatnich
1. Mnozenie(*) i dzielenie(/)
a) dzialanie a * b mozna zastapic poprzez szybsza operacje przesuniecia bitowego w lewo: a << x jezeli b = 2^x.
Wiadomo ze mnozenie jest przemienne takze jezeli a = 2^x dzialanie mozna zastapic przez: b << x
Przykladowo:
w = a * 32;
poniewaz liczbe 32 mozna zapisac jako 2^5 powyzsza operacje mozna zapisac uzywajac operacji bitowej <<:
w = a << 5;
b) dzialanie a / b mozna zastapic poprzez szybsza operacje przesuniecia bitowego w prawo: a >> x jezeli b = 2^x
Przykladowo:
w = a / 8;
poniewaz liczbe 8 mozna zapisac jako 2^3 powyzsza operacje mozna zapisac uzywajac operacji bitowej >>:
w = a >> 3;
2. Operacja modulo(%)
dzialanie a % b mozna zastapic przez operacje bitowa AND: a & (b-1) jezeli b = 2^x
Przykladowo:
w = a % 4;
poniewaz 4 mozna zapisac jako 2^2 powyzsze dzialanie mozna zapisac uzywajac operacji bitowej &:
w = a & 2;
Na koniec moze nie dzialanie arytmetyczne ale znana kazdemu a jak nie na pewno sie przyda optymalizacja tzw. operacji swap z pol. zamiana uzywajac operacji bitowej XOR(^)
zamiast:
w C:
w C++:
mozna to wykonac bez dodatkowej zmiennej uzywajac wlasciwosci operatora XOR:
w C:
w C++:



Przestałem gdy mój qsort nie chciał działać przez zastosowanie super metody z xor. Problem w tym, że jeżeli a == b ; to taka operacja wcale nie zamienia miejscami tych wartości!!
Wtedy też testowałem efektywność i wyszło na to, że motyw z operacją xor jest dużo wolniejszy.
Co do innych metod: klasyczne (a + b) >> 1; Ile razy autor pomylił się pisząc (a+b)>>2; ?? co ciekawe to zadziała (tylko) dla liczb dodatnich (sic!). Co jeszcze ciekawsze kompilator sam to ( (a+b) / 2 ) zoptymalizuje o ile będzie miał pewność, że wynik jest ok (nie ma głupot z liczbami ujemnymi).
Pytanie: co robi ta funkcja:
void foo(int& x, int& y) {
x = x + y;
y = x - y;
x = x - y;
}
Ciekawe ile osób (nieznających wcześniej tej głupoty jaką jest foo) od razu widzi co foo robi.
Myślę, że autor właśnie napisał jak _nie_ należy programować.
Warto to zaznaczyć.
Miałeś chyba na myśli liczby całkowite - zapewniam cię że na ujemnych też można.
Powyższe optymalizacje (mnozenie, dzielenie, modulo) mają sens TYLKO jeśli b jest zmienne i kompilator nie może w żaden sposób wydedukować, że and będzie działać.
Aczkolwiek wtedy kod traci na czytelności... nie widzę zastosowania takich mikrooptymalizacji, ew. w jednej najczęściej wykonywanej pętli w całym programie... (ale to i tak tak dużo na zmieni, na dzisiejszych prockach...)
Modulo - tam chyba miało być a & 3 ...
Swap - wersja z xorem będzie zazwyczaj wolniejsza - optymalizacje tego typu najlepiej zostawić kompilatorowi.
Wbrew pozorom (?) największy wpływ na szybkość programu (poza nielicznymi wyjątkami - jakieś aplikacje numeryczne) ma fragmentacja pamięci (wykorzystanie cache) a nie powolność dzielenia.
a tak na serio to RESPEKT
Co do operacji % i & tak tutaj foflik ma racje ale to nie tylko te operacje bitowego przesuniecia rowniez zapomnialem po prostu nadmienic ale dla liczb ujemnych nie mozna tego zastosowac po prostu wyniki zawsze beda sie roznily
serio?