Czytałem „Język programowania C ++” Stroustrupa, w którym mówi, że na dwa sposoby można dodać coś do zmiennej
x = x + a;
i
x += a;
Woli, +=
bo najprawdopodobniej jest lepiej zaimplementowany. Myślę, że ma na myśli, że to też działa szybciej.
Ale czy to naprawdę? Jeśli to zależy od kompilatora i innych rzeczy, jak mam to sprawdzić?
c++
performance
operators
Chiffa
źródło
źródło
Odpowiedzi:
Wszelkie warta jego sól kompilator wygeneruje dokładnie taką samą sekwencję maszynowego języka dla obu konstruktów dla dowolnego typu wbudowanego (
int
,float
, etc), o ile oświadczenie naprawdę jest tak proste, jakx = x + a;
i optymalizacji jest włączona . (Warto zauważyć, że GCC-O0
, który jest trybem domyślnym, wykonuje antyoptymalizacje , takie jak wstawianie całkowicie niepotrzebnych magazynów do pamięci, aby upewnić się, że debuggery zawsze mogą znaleźć wartości zmiennych).Jeśli jednak stwierdzenie jest bardziej skomplikowane, mogą być inne. Załóżmy
f
zatem, że jest to funkcja, która zwraca wskaźnikdzwoni
f
tylko raz, a tymczasemnazywa to dwukrotnie. Jeśli
f
ma skutki uboczne, jeden z dwóch będzie zły (prawdopodobnie ten drugi). Nawet jeślif
nie ma skutków ubocznych, kompilator może nie być w stanie wyeliminować drugiego wywołania, więc to drugie może być rzeczywiście wolniejsze.A ponieważ mówimy tutaj o C ++, sytuacja jest zupełnie inna w przypadku typów klas, które przeciążają
operator+
ioperator+=
. Jeślix
jest takim typem, to - przed optymalizacją -x += a
przekłada się nax.operator+=(a);
podczas gdy
x = x + a
przekłada się naauto TEMP(x.operator+(a)); x.operator=(TEMP);
Teraz, jeśli klasa jest poprawnie napisana, a optymalizator kompilatora jest wystarczająco dobry, oba kończą się generowaniem tego samego języka maszynowego, ale nie jest to takie pewne, jak w przypadku typów wbudowanych. Prawdopodobnie o tym myśli Stroustrup, zachęcając do używania
+=
.źródło
expr
dovar
isvar+=expr
i zapisywania go w inny sposób zmyli czytelników.*f() = *f() + a;
możesz dobrze przyjrzeć się temu, co naprawdę próbujesz osiągnąć ...1
jest stałą,a
może być zmienną, typem zdefiniowanym przez użytkownika lub czymkolwiek. Zupełnie inny. W rzeczywistości nie rozumiem, jak to się w ogóle zamknęło.Możesz to sprawdzić patrząc na demontaż, który będzie taki sam.
W przypadku podstawowych typów oba są równie szybkie.
Są to dane wyjściowe generowane przez kompilację debugowania (tj. Brak optymalizacji):
a += x; 010813BC mov eax,dword ptr [a] 010813BF add eax,dword ptr [x] 010813C2 mov dword ptr [a],eax a = a + x; 010813C5 mov eax,dword ptr [a] 010813C8 add eax,dword ptr [x] 010813CB mov dword ptr [a],eax
W przypadku typów zdefiniowanych przez użytkownika , w których można przeciążać
operator +
ioperator +=
, zależy to od ich odpowiednich implementacji.źródło
a
wynosi 1,x
tovolatile
kompilator może generowaćinc DWORD PTR [x]
. To jest powolne.operator +
aby nic nie robić ioperator +=
obliczyć 100000 liczbę pierwszą, a następnie zwrócić. Oczywiście byłoby to głupie, ale jest to możliwe.++x
itemp = x + 1; x = temp;
, wtedy najprawdopodobniej powinna być napisana montaż zamiast C ++ ...Tak! Pisanie jest szybsze, szybsze do czytania i szybsze do zrozumienia, jeśli chodzi o to drugie w przypadku, gdy
x
mogą mieć skutki uboczne. Więc ogólnie jest to szybsze dla ludzi. Ogólnie rzecz biorąc, czas ludzki kosztuje znacznie więcej niż czas komputera, więc pewnie o to pytałeś. Dobrze?źródło
To naprawdę zależy od typu x i a oraz implementacji +. Dla
kompilator musi utworzyć tymczasową wartość T, aby zawierała wartość x + a podczas jej oceny, którą następnie może przypisać do x. (Nie może używać x ani a jako obszaru roboczego podczas tej operacji).
Dla x + = a nie potrzeba tymczasowego.
W przypadku trywialnych typów nie ma różnicy.
źródło
Różnica między
x = x + a
ix += a
to ilość pracy, jaką musi wykonać maszyna - niektóre kompilatory mogą (i zwykle to robią) optymalizować ją, ale zazwyczaj, jeśli przez jakiś czas zignorujemy optymalizację, dzieje się tak, że w poprzednim fragmencie kodu maszyna musi sprawdzać wartośćx
dwukrotnie, podczas gdy w drugim przypadku to wyszukiwanie musi nastąpić tylko raz.Jednak, jak wspomniałem, obecnie większość kompilatorów jest wystarczająco inteligentna, aby przeanalizować instrukcje i zredukować wymagane instrukcje maszynowe.
PS: Pierwsza odpowiedź na temat przepełnienia stosu!
źródło
Ponieważ oznaczyłeś to C ++, nie ma sposobu, aby dowiedzieć się z dwóch opublikowanych instrukcji. Musisz wiedzieć, co to jest „x” (to trochę jak odpowiedź „42”). Jeśli
x
jest to POD, to tak naprawdę nie będzie miało większego znaczenia. Jeśli jednakx
jest to klasa, mogą występować przeciążenia metodoperator +
ioperator +=
, które mogą mieć różne zachowania, które prowadzą do bardzo różnych czasów wykonania.źródło
Jeśli powiesz
+=
, że znacznie ułatwiasz życie kompilatorowi. Aby kompilator rozpoznał, żex = x+a
jest to to samox += a
, co kompilator musiprzeanalizuj lewą stronę (
x
), aby upewnić się, że nie ma skutków ubocznych i zawsze odnosi się do tej samej wartości l. Na przykład mogłoby tak byćz[i]
i musi się upewnić, że jednoz
i drugiei
nie ulegnie zmianie.przeanalizuj prawą stronę (
x+a
) i upewnij się, że jest to sumowanie i że lewa strona występuje raz i tylko raz po prawej stronie, mimo że można ją przekształcić, jak wz[i] = a + *(z+2*0+i)
.Jeśli to, co masz na myśli to, aby dodać
a
dox
pisarz kompilator docenia go po prostu powiedzieć, co masz na myśli. W ten sposób nie ćwiczysz tej części kompilatora, z której jego autor ma nadzieję, że wyciągnął wszystkie błędy, a to w rzeczywistości nie ułatwia ci życia, chyba że szczerze nie możesz się wyrwać trybu Fortran.źródło
Dla konkretnego przykładu wyobraźmy sobie prosty typ liczb zespolonych:
struct complex { double x, y; complex(double _x, double _y) : x(_x), y(_y) { } complex& operator +=(const complex& b) { x += b.x; y += b.y; return *this; } complex operator +(const complex& b) { complex result(x+b.x, y+b.y); return result; } /* trivial assignment operator */ }
W przypadku a = a + b musi utworzyć dodatkową zmienną tymczasową, a następnie ją skopiować.
źródło
Zadajesz złe pytanie.
Jest mało prawdopodobne, aby wpłynęło to na wydajność aplikacji lub funkcji. Nawet gdyby tak było, sposobem na sprawdzenie jest profilowanie kodu i upewnienie się, jak wpływa on na Ciebie. Zamiast martwić się na tym poziomie, który jest szybszy, o wiele ważniejsze jest myślenie w kategoriach jasności, poprawności i czytelności.
Jest to szczególnie prawdziwe, gdy weźmiesz pod uwagę, że nawet jeśli jest to znaczący czynnik wydajności, kompilatory ewoluują w czasie. Ktoś może wymyślić nową optymalizację, a prawidłowa odpowiedź dzisiaj może okazać się błędna jutro. To klasyczny przypadek przedwczesnej optymalizacji.
Nie oznacza to, że wydajność w ogóle nie ma znaczenia ... Tylko, że jest to niewłaściwe podejście do osiągania celów perfekcyjnych. Właściwym podejściem jest użycie narzędzi do profilowania, aby dowiedzieć się, gdzie Twój kod faktycznie spędza czas, a tym samym na czym należy się skupić.
źródło
Myślę, że powinno to zależeć od maszyny i jej architektury. Jeśli jego architektura umożliwia pośrednie adresowanie pamięci, piszący kompilator MOŻE po prostu użyć tego kodu (do optymalizacji):
mov $[y],$ACC iadd $ACC, $[i] ; i += y. WHICH MIGHT ALSO STORE IT INTO "i"
Natomiast
i = i + y
może zostać przetłumaczony na (bez optymalizacji):To powiedziawszy,
i
należy również wziąć pod uwagę inne komplikacje, takie jak if jest funkcją zwracającą wskaźnik itp. Większość kompilatorów na poziomie produkcyjnym, w tym GCC, tworzy ten sam kod dla obu instrukcji (jeśli są to liczby całkowite).źródło
Nie, oba sposoby są takie same.
źródło