[C][C++] Dywagacje o i++ a ++i i optymalizacjach
- Santiago A
- Użytkownik
- Posty: 248
- Rejestracja: 22 sty 2016, o 20:56
- Płeć: Mężczyzna
- Lokalizacja: Zaragoza
- Podziękował: 9 razy
- Pomógł: 51 razy
[C][C++] Dywagacje o i++ a ++i i optymalizacjach
W którym współczesnym kompilatorze można odczuć różnicę między ++i oraz i++?
-
- Użytkownik
- Posty: 1931
- Rejestracja: 29 maja 2009, o 11:58
- Płeć: Mężczyzna
- Lokalizacja: Warszawa
- Podziękował: 145 razy
- Pomógł: 320 razy
[error] - runge kutty felberg C++
Co do samego ++i oraz i++ to w sumie w żadnym (chyba, że będzie takich działań baaardzo dużo i będzie wyłączona optymalizacja - wtedy w każdym). Natomiast co do reszty przypadków:
Dopiero jak przejdzie ci zastąpić to iteratorem lub większą liczbą lub obiektem np streampos ... eam/tellg/ wtedy kompilator nawet nie ma prawa nic optymalizować i będziesz mieć bezsensowne "wycieki czasu".
-
- Użytkownik
- Posty: 576
- Rejestracja: 2 lut 2012, o 21:42
- Płeć: Mężczyzna
- Lokalizacja: Radom
- Podziękował: 1 raz
- Pomógł: 64 razy
[error] - runge kutty felberg C++
Daj przykład kodu do sprawdzenia, bo inaczej uznaję to za prowokację. Stwierdzenie "kompilator nawet nie ma prawa nic optymalizować" jest niezgodne z używanymi przeze mnie kompilatorami, a przynajmniej nie można łatwo całkowicie zablokować optymalizacji. Mam wrażenie, że ten cytowany tekst zdezaktualizował się kilka lat temu.
- Santiago A
- Użytkownik
- Posty: 248
- Rejestracja: 22 sty 2016, o 20:56
- Płeć: Mężczyzna
- Lokalizacja: Zaragoza
- Podziękował: 9 razy
- Pomógł: 51 razy
[error] - runge kutty felberg C++
Pozwolę sobie zacytować komentarz z pewnej strony poświęconej takim zagadnieniom.
Mark Harrison pisze:I used to be a compiler writer, and had the opportunity to test this code on many CPUs, operating systems, and compilers. The only compiler I found that didn't optimize the i++ case (in fact, the compiler that brought this to my attention professionally) was the Software Toolworks C80 compiler by Walt Bilofsky. That compiler was for Intel 8080 CP/M systems. It's safe to say that any compiler which does not include this optimization is not one meant for general use.
-
- Użytkownik
- Posty: 1931
- Rejestracja: 29 maja 2009, o 11:58
- Płeć: Mężczyzna
- Lokalizacja: Warszawa
- Podziękował: 145 razy
- Pomógł: 320 razy
[error] - runge kutty felberg C++
Kod: Zaznacz cały
#include <iostream>
#include <vector>
#include <ctime>
int main()
{
clock_t t1, t2, t3, t4;
t1 = clock();
std::vector<long long int> vec1;
for (long long int i = 0; i < 999999999L; ++i)
vec1.push_back(i);
t2 = clock();
float diff1 ((float)t2-(float)t1);
float seconds1 = diff1 / CLOCKS_PER_SEC;
std::cout << "++i: " << seconds1 << " s" << std::endl;
t3 = clock();
std::vector<long long int> vec2;
for (long long int i = 0; i < 999999999L; i++)
vec2.push_back(i);
t4 = clock();
float diff2 ((float)t4-(float)t3);
float seconds2 = diff2 / CLOCKS_PER_SEC;
std::cout << "i++: " << seconds2 << " s" << std::endl;
return 0;
}
Kod: Zaznacz cały
++i: 38.7001 s
i++: 42.5566 s
Słyszałeś o typie zmiennychathame pisze:Daj przykład kodu do sprawdzenia, bo inaczej uznaję to za prowokację. Stwierdzenie "kompilator nawet nie ma prawa nic optymalizować" jest niezgodne z używanymi przeze mnie kompilatorami, a przynajmniej nie można łatwo całkowicie zablokować optymalizacji. Mam wrażenie, że ten cytowany tekst zdezaktualizował się kilka lat temu.
volatile
?-
- Użytkownik
- Posty: 576
- Rejestracja: 2 lut 2012, o 21:42
- Płeć: Mężczyzna
- Lokalizacja: Radom
- Podziękował: 1 raz
- Pomógł: 64 razy
[error] - runge kutty felberg C++
U mnie (Arch Linux 64-bit, GCC 6.1.1) taki wynik programu:
Po zmniejszeniu iteracji do \(\displaystyle{ 199999999L}\) taki wynik:
O
Kod: Zaznacz cały
terminate called after throwing an instance of 'std::bad_alloc'
what(): std::bad_alloc
Przerwane (zrzut pamięci)
Kod: Zaznacz cały
++i: 3.53009 s
i++: 3.47582 s
volatile
słyszałem, ale nie zajmuję się na co dzień programowaniem, więc nie mam z tym styczności.-
- Użytkownik
- Posty: 1931
- Rejestracja: 29 maja 2009, o 11:58
- Płeć: Mężczyzna
- Lokalizacja: Warszawa
- Podziękował: 145 razy
- Pomógł: 320 razy
[error] - runge kutty felberg C++
Hmm rzeczywiście, dla mniejszej ilości iteracji też mi czasem tak pokazuje.
Ale jak wyłączy się optymalizacje w GCC (czyli
Aczkolwiek to było na gcc 4.2.1 i w tym momencie kompiluje mi się 6.2.0 (no i tu jest wykorzystanie procka 95%, także wynik jeszcze się wstrzymam chwilę jeśli chodzi o pewność)
Ale jak wyłączy się optymalizacje w GCC (czyli
-O0
):Kod: Zaznacz cały
Bez optymalizacji:
MBP-Dominik:desktop user$ g++ Untitled.cpp -O0 -o test
MBP-Dominik:desktop user$ ./test
++i: 8.84344 s
i++: 9.26068 s
Z optymalizacją:
MBP-Dominik:desktop user$ g++ Untitled.cpp -o test1
MBP-Dominik:desktop user$ ./test1
++i: 9.35894 s
i++: 9.05727 s
volatile
to jest taki typ, którego kompilator nie ma prawa optymalizować (to oczywiście nie jest jego główna cecha, ale to nieistotne).-
- Użytkownik
- Posty: 576
- Rejestracja: 2 lut 2012, o 21:42
- Płeć: Mężczyzna
- Lokalizacja: Radom
- Podziękował: 1 raz
- Pomógł: 64 razy
[error] - runge kutty felberg C++
volatile
ma sens tylko jeśli wymagana jest ściśle określona kontrola zachowania pamięci. W praktyce mikrokontrolery, a nie komputery uniwersalne. Jeśli już chcemy znaleźć odpowiednik javowego volatile
w c++ dla procesora Intel x86 (także 64 bit) lub ARM, to będzie std::atomic
.Po wyłączeniu optymalizacji:
Kod: Zaznacz cały
++i: 3.56654 s
i++: 3.51086 s
-
- Użytkownik
- Posty: 1931
- Rejestracja: 29 maja 2009, o 11:58
- Płeć: Mężczyzna
- Lokalizacja: Warszawa
- Podziękował: 145 razy
- Pomógł: 320 razy
[error] - runge kutty felberg C++
Naprawdę ciekawe, że takie wyniki otrzymujesz przy tylu iteracjach. Po zaktualizowaniu gcc do 6.2.0 takie wartości dostałem:
Dla \(\displaystyle{ 999999999L}\):
Dla \(\displaystyle{ 199999999L}\):
Dla \(\displaystyle{ 55999999L}\):
Dla \(\displaystyle{ 999999L}\):
Wniosek: jestem mocno przekonany, że jakbyś zwiększył rozmiar iteracji (bo, zakładając, że u Ciebie LLI waży 8 bajtów, to wtedy \(\displaystyle{ 999999999L}\) LLI zajmie koło 7,4GB RAMu - więc rzeczywiście może wywalić błąd o przepełnieniu stosu) to wtedy byś dostał wynik podobny do mojego (tj. coraz widoczniejsza będzie przewaga preinkrementacji nad postinkrementacją)
I jeszcze tak dla porównania GCC 4.2.1 (\(\displaystyle{ 999999999L}\)):
Fajnie widać efekty pracy nad optymalizacją na poziomie kompilatora
Dla \(\displaystyle{ 999999999L}\):
Ukryta treść:
Ukryta treść:
Ukryta treść:
Ukryta treść:
I jeszcze tak dla porównania GCC 4.2.1 (\(\displaystyle{ 999999999L}\)):
Ukryta treść:
Ostatnio zmieniony 29 sie 2016, o 02:04 przez kalwi, łącznie zmieniany 1 raz.
-
- Użytkownik
- Posty: 576
- Rejestracja: 2 lut 2012, o 21:42
- Płeć: Mężczyzna
- Lokalizacja: Radom
- Podziękował: 1 raz
- Pomógł: 64 razy
[error] - runge kutty felberg C++
Różnica jest, ale u mnie przy wszystkich dopuszczalnych wartościach (ograniczenia rozmiaru pamięci) jest dokładnie odwrotnie niż postulowałeś od początku:
i jeszcze w ramach uzupełnienia:
Sprzęt:
Kod: Zaznacz cały
tmp : cat speed.cpp | grep for
for (long long int i = 0; i < 199999999L; ++i)
for (long long int i = 0; i < 199999999L; i++)
tmp : g++ -O0 speed.cpp -o speed
tmp : g++ speed.cpp -o speedO
tmp : ./speed
++i: 3.46913 s
i++: 3.39606 s
tmp : ./speedO
++i: 3.46237 s
i++: 3.39336 s
Kod: Zaznacz cały
tmp : g++ -O3 speed.cpp -o speed
tmp : ./speed
++i: 1.09391 s
i++: 1.00993 s
Kod: Zaznacz cały
tmp : cat /proc/cpuinfo | grep name | uniq && free -h
model name : Intel(R) Core(TM) i7-3612QM CPU @ 2.10GHz
razem użyte wolne dzielone buf/cache dostępne
Pamięć: 7,7G 951M 5,5G 446M 1,3G 6,1G
Wymiana: 511M 0B 511M
-
- Moderator
- Posty: 2828
- Rejestracja: 15 cze 2008, o 15:45
- Płeć: Mężczyzna
- Lokalizacja: Seattle, WA
- Podziękował: 3 razy
- Pomógł: 356 razy
[error] - runge kutty felberg C++
Przecież ten kod nawet nie bada tego, o co się spieracie. Jak chcecie wnioskować o szybkości pojedynczej instrukcji, skoro większość pracy program wykona w alokacji pamięci? O takich „szczegółach”, jak niejednakowe środowisko, wykonanie testów tysiące razy, porównanie kodu maszynowego, czy optymalizacje na poziomie procesora już nie wspomnę.kalwi pisze:Kod: Zaznacz cały
#include <iostream> #include <vector> #include <ctime> int main() { clock_t t1, t2, t3, t4; t1 = clock(); std::vector<long long int> vec1; for (long long int i = 0; i < 999999999L; ++i) vec1.push_back(i); t2 = clock(); float diff1 ((float)t2-(float)t1); float seconds1 = diff1 / CLOCKS_PER_SEC; std::cout << "++i: " << seconds1 << " s" << std::endl; t3 = clock(); std::vector<long long int> vec2; for (long long int i = 0; i < 999999999L; i++) vec2.push_back(i); t4 = clock(); float diff2 ((float)t4-(float)t3); float seconds2 = diff2 / CLOCKS_PER_SEC; std::cout << "i++: " << seconds2 << " s" << std::endl; return 0; }
Jest jak najbardziej zgodne, boathame pisze:Stwierdzenie "kompilator nawet nie ma prawa nic optymalizować" jest niezgodne z używanymi przeze mnie kompilatorami, a przynajmniej nie można łatwo całkowicie zablokować optymalizacji. Mam wrażenie, że ten cytowany tekst zdezaktualizował się kilka lat temu.
i++
i ++i
są semantycznie różnymi operacjami i ich optymalizowanie nie może zmienić zachowania. Jeżeli i++
ma jakiekolwiek efekty zewnętrzne (a w cytowanym poście chodziło o obiekty, dla których te operatory są jawnie przeciążone, więc takie efekty jak najbardziej mogą występować), to nie można ot tak zamienić tych operacji, bo zmieniłoby to logikę programu.volatile nie zablokuje wszystkich optymalizacji, zmieni jedynie sposób ich przeprowadzenia, najczęściej przez wstawienie barier w pamięci, co nie oznacza, żekalwi pisze:Słyszałeś o typie zmiennychvolatile
?
i++
nie zostanie zamienione na ++i
lub na odwrót. W szczególności jakiekolwiek teoretyzowanie o optymalizacjach jest niewiarygodne bez solidnych testów, bo poza optymalizacjami w trakcie kompilacji jest masa przyspieszeń, które wykonuje procesor. Nawet fakt, że kompilator wygeneruje kod wykonujący fizycznie i++
nie oznacza, że taki kod zostanie wykonany przez jednostkę obliczeniową.Polecam poczytać o modelach pamięci i dlaczego double-checked lock działa w Javie, ale już niekoniecznie w C++. Tutaj krótka prezentacja pokazująca, co właściwie różne procesory mogą zrobić nawet z najprostszymi kodami:
Kod: Zaznacz cały
https://vimeo.com/171942746