| pekfos | » 2010-12-27 16:16:39 proste sztuczki? np to: for( int a = 0; a < 100000; a++ ) for( int b = 0; b < 10; ++b ) { int x = a * b / 12; } jest mniej efektywne od tego: for( int a = 0, b, x; a < 100000; a++ ) for( b = 0; b < 10; ++b ) { x = a * b / 12; } dlatego że deklarowanie zmiennej w pętli oznacza ze zmienna zostanie utworzona i zniszczona n razy. co oczywiście trochę trwa. czasem nawyki z C się przydają. deklaruj zmienne tymczasowe używanie w pętlach wcześniej a kod będzie szybszy. a dokładniej szybsze o 100000 tworzenia i niszczenia lokalnego b i 1000000 tworzenia i niszczenia lokalnego x | 
|  | 
| jsc Temat założony przez niniejszego użytkownika | » 2010-12-27 16:45:25 Jak wy tak piszecie to otworzyło mi się w głowie i:b++ szybsze niż b+1
 a & b szybsze niż a==b
 | 
|  | 
| ison | » 2010-12-27 16:50:37 nie możesz porównać tych dwóch rzeczy... jak już to b++; oraz b+=1; b++ to również przypisanie jeśli w danym momencie nie mają znaczenia priorytety operatorów to staraj się używać preinkrementacji (++b) - nie tworzysz wtedy obiektu tymczasowego to nie to samo | 
|  | 
| jsc Temat założony przez niniejszego użytkownika | » 2010-12-27 16:59:33 Czym różni się iloczyn logiczny od porównania. W tablicy prawdy dla bitów wychodzi to samo.
 A błąd logiczny.
 
 Masz rację.
 | 
|  | 
| Elaine |   » 2010-12-27 17:03:48 @ison: no jasne, kompilatory nie potrafią przeprowadzać strength reduction...
 Nie, zaraz, potrafią. W dodatku lepiej, niż większość ludzi.
 a * 0.5  wcale nie będzie szybsze, niż  a / 2 , co najwyżej wyjdzie na to samo, a i to tylko, jeśli a  jest typu zmiennoprzecinkowego:
 Co widać? Przede wszystkim,  a / 2  pozwoliło kompilatorowi na wygenerowanie porządnego kodu, bez żadnego dzielenia. Tylko... dlaczego w przypadku int  nie jest to po prostu  sar eax, 1 ? Z prostego powodu, dzielenie zaokrągla do zera, przesunięcie bitowe zaokrągla do minus nieskończoności - właśnie dlatego są dodatkowe (tanie) instrukcje.
 a * 0.5  natomiast wymusiło na kompilatorze wygenerowanie konwersji do liczby zmiennoprzecinkowej, mnożenia zmiennoprzecinkowego i konwersji z powrotem do liczby całkowitej - co jest niepotrzebnie skomplikowane (a więc i wolne), skoro można to zrobić bez konwersji.
 Oczywiście kompilator da sobie radę również w przypadku, gdy zamiast dwójki będzie inna jej potęga. Ba - nawet w przypadku, gdy dzielnik nie będzie potęgą dwójki, kompilator zwykle i tak nie będzie używał instrukcji do dzielenia, zamiast tego pomnoży przez stałą (plus ewentualnie kilka przesunięć bitowych, dodawań i odejmowań - czyli tanich operacji) w przypadku liczb całkowitych, a w przypadku liczb zmiennoprzecinkowych pomnoży przez odwrotność.
 Oczywiście te kody wyżej są x86-specific, ale na innych architekturach jest podobnie.
 @pekfos: to jest prawdą tylko dla typów, których konstrukcja/destrukcja faktycznie coś zajmuje (czyli tych, które mają konstruktor/destruktor, który wykonuje jakąś konkretniejszą robotę). Dla typów POD wyjdzie dokładnie na to samo - miejsce w ramce stosu i tak jest alokowane tylko raz, przy wejściu do funkcji, kosztów konstrukcji i destrukcji nie ma wcale. | 
|  | 
| ison | » 2010-12-27 17:08:08 @Iname ok, a co z przesunięciem bitowym zamiast dzielenia/mnożenia przez potęgi dwójki? W ogóle tego nie stosować bo kompilatory i tak sobie to zamienią jak będą chciały?
 Co jest w takim razie lepszym rozwiązaniem
 i*2 czy i/0.5?
 | 
|  | 
| jsc Temat założony przez niniejszego użytkownika | » 2010-12-27 17:16:40 a << 1 | 
|  | 
| Elaine |   » 2010-12-27 17:18:52 Jeśli chodzi o dzielenie to masz wyżej, dla innych potęg dwójki zmienią się tylko stałe i sekwencja dla inta trochę się zmieni (dojdzie jeden and, zamiast odejmowania będzie dodawanie - wciąż jednak to będą same tanie instrukcje, tylko cztery zamiast trzech - lepiej się tego zrobić nie da, bez naruszania zaokrąglania do zera).
 Jeśli chodzi o mnożenie:
 Dla liczb całkowitych  a * X , gdzie X  jest potęgą dwójki, to będzie jedno przesunięcie w lewo (wyjątek: jeśli mnożymy przez 2, wtedy to będzie dodanie wartości do samej siebie), teraz już bez żadnych cyrków z bitem znaku. Podobnie też jak wyżej, jeśli X  nie będzie potęgą dwójki, to kod niekoniecznie też będzie zawierał mnożenie - kompilator wykorzysta przesunięcia bitowe, dodawanie, odejmowanie i instrukcję  lea  (oczywiście nie zawsze - mnożenie jest stosunkowo szybkie, czasami ciąg wspomnianych instrukcji będzie wolniejszy niż  imul , wtedy po prostu kompilator zrobi ten  imul ). Dla liczb zmiennoprzecinkowych to z kolei będzie pojedyncza instrukcja mnożenia.
 a /( reciprocal of X )  z kolei będzie podobnym cyrkiem, jak wyżej  a * 0.5  - zbędne konwersje dla liczb całkowitych, jedno mnożenie dla zmiennoprzecinkowych.
 Takie mikrooptymalizacje nie mają sensu - jak chcesz dzielić/mnożyć, to użyj w kodzie dzielenia/mnożenia, kompilator już zadba o to, by wygenerowany kod robił to w jak najszybszy sposób.
 Swoją drogą -  a << 1  kompiluje się właśnie do pojedynczego dodawania. Nie spodziewaliście się tego, nie? ;> | 
|  | 
| 1 « 2 »  3 4 5 6 7 8 |