Czy jest różnica w wydajności między i++ I ++i w C?

Czy istnieje różnica wydajności między i++ i ++i, jeśli wynikowa wartość nie jest używana?

Author: Mark Harrison, 2008-08-24

13 answers

Streszczenie: nie.

i++ potencjalnie może być wolniejszy niż ++i, ponieważ stara wartość i być może trzeba będzie zapisać do późniejszego wykorzystania, ale w praktyce wszystkie nowoczesne Kompilatory to zoptymalizują.

Możemy to zademonstrować, patrząc na kod tej funkcji, zarówno z ++i, jak i i++.

$ cat i++.c
extern void g(int i);
void f()
{
    int i;

    for (i = 0; i < 100; i++)
        g(i);

}

Pliki są takie same, z wyjątkiem ++i i i++:

$ diff i++.c ++i.c
6c6
<     for (i = 0; i < 100; i++)
---
>     for (i = 0; i < 100; ++i)

Skompilujemy je, a także uzyskamy wygenerowane asembler:

$ gcc -c i++.c ++i.c
$ gcc -S i++.c ++i.c

I widzimy, że zarówno wygenerowany obiekt, jak i pliki asemblera są takie same.

$ md5 i++.s ++i.s
MD5 (i++.s) = 90f620dda862cd0205cd5db1f2c8c06e
MD5 (++i.s) = 90f620dda862cd0205cd5db1f2c8c06e

$ md5 *.o
MD5 (++i.o) = dd3ef1408d3a9e4287facccec53f7d22
MD5 (i++.o) = dd3ef1408d3a9e4287facccec53f7d22
 335
Author: Mark Harrison,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-06 19:08:24

Z efektywność a intencja Andrew Koenig:

Po pierwsze, nie jest oczywiste, że ++i jest bardziej wydajny niż i++, przynajmniej jeśli chodzi o zmienne całkowite.

I:

Więc pytanie, które należy zadać, nie jest to, która z tych dwóch operacji jest szybsza, ale która z tych dwóch operacji wyraża dokładniej to, co próbujesz osiągnąć. Oświadczam, że jeśli nie używasz wartości wyrażenia, nigdy nie ma powodu, aby używać i++ zamiast ++i, ponieważ nigdy nie ma powodu, aby skopiować wartość zmiennej, zwiększyć zmienną, a następnie wyrzucić kopię.

Więc, jeśli wynikowa wartość nie jest używana, użyłbym ++i. Ale nie dlatego, że jest bardziej wydajny: ponieważ prawidłowo stwierdza mój zamiar.

 92
Author: Sébastien RoccaSerra,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-06 19:20:01

Lepszą odpowiedzią jest to, że ++i czasami będzie szybciej, ale nigdy wolniej.

Każdy wydaje się zakładać, że i jest regularnym typem wbudowanym, takim jak int. W tym przypadku nie będzie mierzalnej różnicy.

Jeśli jednak i jest typem złożonym, to można znaleźć mierzalną różnicę. Dla i++ musisz zrobić kopię swojej klasy przed jej zwiększeniem. W zależności od tego, co jest związane z kopią, może to być naprawdę wolniejsze, ponieważ z ++it możesz po prostu powrócić wartość końcowa.

Foo Foo::operator++()
{
  Foo oldFoo = *this; // copy existing value - could be slow
  // yadda yadda, do increment
  return oldFoo;
}

Inną różnicą jest to, że z ++i Masz możliwość zwracania referencji zamiast wartości. Ponownie, w zależności od tego, co jest związane z tworzeniem kopii obiektu, może to być wolniejsze.

Prawdziwym przykładem tego, gdzie może to nastąpić, jest użycie iteratorów. Kopiowanie iteratora jest mało prawdopodobne w Twojej aplikacji, ale nadal dobrą praktyką jest przyzwyczajenie się do używania {[1] } zamiast i++, gdzie wynik jest Nie dotyczy.

 42
Author: Andrew Grant,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-06 19:25:20

Oto dodatkowa uwaga, jeśli martwisz się o mikro optymalizację. Pętle dekrementacyjne mogą być "prawdopodobnie" bardziej wydajne niż pętle inkrementacyjne (w zależności od architektury zestawu instrukcji, np. ARM), biorąc pod uwagę:

for (i = 0; i < 100; i++)

Na każdej pętli będziesz miał po jednej instrukcji dla:

  1. dodanie 1 do i.
  2. Porównaj czy i jest mniejsze niż 100.
  3. oddział warunkowy, jeśli i jest mniejszy niż 100.

Pętla zmniejszająca:

for (i = 100; i != 0; i--)

Pętla będzie miała instrukcję dla każdego z:

  1. Decrement i, Ustawianie znacznika statusu rejestru procesora.
  2. oddział warunkowy zależny od statusu rejestru procesora (Z==0).

Oczywiście działa to tylko wtedy, gdy zmniejsza się do zera!

Pamiętany z podręcznika arm system Developer ' s Guide.

 16
Author: tonylo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-06 18:59:26

Biorąc liść od Scotta Meyersa, bardziej skuteczne c++ Pozycja 6: rozróżnić formy prefiksu i postfiksu operacji przyrostowych i przyrostowych.

Wersja prefiksu jest zawsze preferowana nad postfixem w odniesieniu do obiektów, szczególnie w odniesieniu do iteratorów.

Powód tego, jeśli spojrzeć na wzorzec wywołania operatorów.

// Prefix
Integer& Integer::operator++()
{
    *this += 1;
    return *this;
}

// Postfix
const Integer Integer::operator++(int)
{
    Integer oldValue = *this;
    ++(*this);
    return oldValue;
}

Patrząc na ten przykład łatwo zauważyć, że operator prefiksu zawsze będzie bardziej wydajny niż postfix. Ze względu na potrzebę użycia obiektu tymczasowego w użyciu postfixa.

Dlatego, gdy widzisz przykłady używające iteratorów, zawsze używają prefiksu w wersji.

Ale jak zauważyłeś dla int-ów nie ma żadnej różnicy ze względu na optymalizację kompilatora, która może mieć miejsce.

 14
Author: JProgrammer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-25 04:29:02

krótka odpowiedź:

Nigdy nie ma różnicy między i++ i ++i pod względem prędkości. Dobry kompilator nie powinien generować innego kodu w tych dwóch przypadkach.

długa odpowiedź:

Każda inna odpowiedź nie wspomina o tym, że różnica między ++i A i++ ma sens tylko w znalezionym wyrażeniu.

W przypadku for(i=0; i<n; i++), i++ jest sam w swoim własnym wyrażeniu: istnieje punkt sekwencji przed i++ i jest jeden po nim. Tak więc jedynym wygenerowanym kodem maszynowym jest "increase i by 1" i jest dobrze zdefiniowane jak jest to uporządkowane w stosunku do reszty programu. Jeśli więc zmienisz go na prefiks ++, nie ma to najmniejszego znaczenia, nadal otrzymujesz kod maszynowy " increase i by 1".

Różnice między ++i i i++ mają znaczenie tylko w wyrażeniach takich jak array[i++] = x; versus array[++i] = x;. Niektórzy mogą argumentować i twierdzić, że postfix będzie wolniejszy w takich operacjach, ponieważ rejestr, w którym znajduje się i, musi zostać przeładowany później. Ale zauważ, że kompilator może dowolnie zamawiać instrukcje w dowolny sposób, o ile nie" łamie zachowania maszyny abstrakcyjnej", jak to określa standard C.

Można więc założyć, że array[i++] = x; zostanie przetłumaczone na kod maszynowy jako:

  • przechowuj wartość i w rejestrze A.
  • przechowuj adres tablicy w rejestrze B.
  • Dodaj A i B, przechowuj wyniki w A.
  • Pod tym nowym adresem reprezentowanym przez A, należy zapisać wartość x.
  • przechowuj wartość i w rejestrze a / / nieefektywne, ponieważ dodatkowa Instrukcja tutaj, zrobiliśmy to już raz.
  • rejestr Przyrostowy A.
  • przechowywać rejestr A w i.

Kompilator równie dobrze mógłby produkować kod wydajniej, np.:

  • przechowuj wartość i w rejestrze A.
  • przechowuj adres tablicy w rejestrze B.
  • Dodaj i B, wyniki przechowywania w B.
  • rejestr Przyrostowy A.
  • przechowywać rejestr A w i.
  • ... // reszta kodu.

Tylko dlatego, że jako programista C jest wyszkolony do myślenia, że postfix ++ dzieje się na końcu, kod maszynowy nie musi być tak uporządkowany.

Więc nie ma różnicy między prefiksem a postfixem ++ w C. Teraz to, co Ty jako programista C powinieneś się różnić, to ludzie, którzy niekonsekwentnie używają prefiksu w niektórych przypadkach i postfix w innych przypadkach, bez uzasadnienia dlaczego. Sugeruje to, że nie są pewni, jak działa C lub że mają nieprawidłową znajomość języka. Jest to zawsze zły znak, z kolei sugeruje, że w swoim programie podejmują inne wątpliwe decyzje, oparte na przesądach lub "dogmatach religijnych".

"prefiks ++ jest zawsze szybszy" jest rzeczywiście jednym z takich fałszywych dogmatów, które są powszechne wśród potencjalnych programistów C.

 11
Author: Lundin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-26 07:23:50

Nie pozwól, aby kwestia" który z nich jest szybszy " była decydującym czynnikiem, którego użyć. Są szanse, że nigdy nie będzie cię to tak bardzo obchodzić, a poza tym, czas odczytu programisty jest znacznie droższy niż czas maszyny.

Użyj tego, co ma największy sens dla człowieka czytającego kod.

 10
Author: Andy Lester,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-20 05:09:18

Po pierwsze: różnica między i++ i ++i jest neglegiable w C.


Do szczegółów.

1. Dobrze znane Wydanie C++: ++i jest szybsze

W C++, ++i jest bardziej wydajny iff i jest rodzajem obiektu z przeciążonym operatorem inkrementacji.

Dlaczego?
W ++i obiekt jest najpierw zwiększany, a następnie może być przekazywany jako odniesienie do dowolnej innej funkcji. Nie jest to możliwe, jeśli wyrażenie jest foo(i++), ponieważ teraz inkrement musi być wykonany przed wywołaniem foo(), ale stara wartość musi być przekazana do foo(). W związku z tym kompilator jest zmuszony wykonać kopię i przed wykonaniem operatora inkrementacji na oryginale. Dodatkowe wywołania konstruktora/destruktora są złą częścią.

Jak wspomniano powyżej, nie dotyczy to typów podstawowych.

2. Mało znany fakt: i++ may be faster

Jeśli nie trzeba wywoływać konstruktora/destruktora, czyli zawsze sprawa w C, ++i i i++ powinna być równie szybka, prawda? Nie. Są praktycznie równie szybkie, ale mogą występować niewielkie różnice, które większość innych odpowiedziających pomyliła.

Jak i++ być szybszym?
Chodzi o zależności danych. Jeśli wartość ma zostać załadowana z pamięci, należy wykonać dwie kolejne operacje, zwiększyć ją i użyć. Z ++i, inkrementacja musi być wykonana przed wartość może być użyta. Z Procesor może wykonywać operację use równolegle do operacji increment. Różnica polega co najwyżej na jednym cyklu procesora, więc jest naprawdę neglegible, ale jest. I jest odwrotnie, niż wielu by się spodziewało.

 9
Author: cmaster,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-04 18:34:44

@ Mark Nawet jeśli kompilator może zoptymalizować (opartą na stosie) tymczasową kopię zmiennej, a gcc (w ostatnich wersjach) robi to, nie oznacza to, że wszystkie Kompilatory zawsze będą to robić.

Właśnie Przetestowałem go z kompilatorami, których używamy w naszym obecnym projekcie i 3 z 4 nie optymalizują go.

Nigdy nie zakładaj, że kompilator robi to dobrze, zwłaszcza jeśli prawdopodobnie szybszy, ale nigdy wolniejszy kod jest tak łatwy do odczytania.

Jeśli nie masz naprawdę głupiego implementacja jednego z operatorów w kodzie:

Alwas woli ++i Niż i++.

 7
Author: Andreas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-09 15:40:45

W języku C kompilator może ogólnie zoptymalizować je tak, aby były takie same, jeśli wynik jest nieużywany.

Jednak w C++ jeśli używasz innych typów, które zapewniają własne operatory++, wersja prefiksu prawdopodobnie będzie szybsza niż wersja postfixa. Jeśli więc nie potrzebujesz semantyki postfixa, lepiej jest użyć operatora prefiksu.

 4
Author: Kristopher Johnson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-24 14:29:01

Myślę o sytuacji, w której postfix jest wolniejszy niż przyrost prefiksu:

Wyobraź sobie, że procesor z rejestrem A jest używany jako akumulator i jest to jedyny rejestr używany w wielu instrukcjach(niektóre małe mikrokontrolery są w rzeczywistości takie).

Teraz wyobraź sobie następujący program i ich tłumaczenie na hipotetyczne Zgromadzenie:

Przyrostek:

a = ++b + c;

; increment b
LD    A, [&b]
INC   A
ST    A, [&b]

; add with c
ADD   A, [&c]

; store in a
ST    A, [&a]

Postfix increment:

a = b++ + c;

; load b
LD    A, [&b]

; add with c
ADD   A, [&c]

; store in a
ST    A, [&a]

; increment b
LD    A, [&b]
INC   A
ST    A, [&b]

Zauważ, że wartość b została zmuszona do przeładowany. Z prefiksem increment, kompilator może po prostu zwiększyć wartość i iść do przodu z jej użyciem, ewentualnie uniknąć przeładowania go, ponieważ żądana wartość jest już w rejestrze po inkrement. Jednak z Postfix increment, kompilator ma do czynienia z dwiema wartościami, jedną starą i jedną zwiększoną, co, jak pokazałem powyżej, skutkuje jeszcze jednym dostępem do pamięci.

Oczywiście, jeśli wartość przyrostu nie jest używana, np. pojedyncza Instrukcja i++;, kompilator może (i robi) po prostu generuje instrukcję przyrostu niezależnie od użycia postfixa lub prefiksu.


Na marginesie chciałbym wspomnieć, że wyrażenie, w którym występuje b++, nie może być po prostu zamienione na jedno z ++b bez dodatkowego wysiłku (na przykład przez dodanie - 1). Więc porównywanie tych dwóch, jeśli są one częścią jakiegoś wyrażenia, nie jest naprawdę poprawne. Często, gdy używasz b++ wewnątrz wyrażenia, nie możesz użyć ++b, więc nawet jeśli ++b były potencjalnie bardziej efektywne, to byłoby po prostu złe. Wyjątkiem jest oczywiście, jeśli wyrażenie się o to prosi (na przykład {[11] } które można zmienić na a = ++b;).
 4
Author: Shahbaz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-04 17:57:51

Zawsze jednak wolę pre-increment ...

Chciałem zwrócić uwagę, że nawet w przypadku wywołania funkcji operator++, kompilator będzie w stanie zoptymalizować tymczasowe, jeśli funkcja zostanie inlined. Ponieważ operator++ jest zwykle krótki i często zaimplementowany w nagłówku, prawdopodobnie zostanie zainlinowany.

Więc, dla celów praktycznych, prawdopodobnie nie ma dużej różnicy między wydajnością tych dwóch form. Jednak zawsze wolę pre-increment ponieważ wydaje się lepiej bezpośrednio wyrazić to, co próbuję powiedzieć, zamiast polegać na optymalizatorze, aby to rozgryźć.

Ponadto, dajÄ ... c optmizer mniej do zrobienia prawdopodobnie oznacza, Ĺźe kompilator dziaĹ ' a szybciej.

 2
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-16 11:28:13

Moja C jest trochę zardzewiała, więc z góry przepraszam. Speedwise, Rozumiem wyniki. Ale jestem zdezorientowany, jak oba pliki wyszły na ten sam hash MD5. Może pętla for działa tak samo, ale czy kolejne 2 linie kodu nie generują innego złożenia?

myArray[i++] = "hello";

Vs

myArray[++i] = "hello";

Pierwszy zapisuje wartość do tablicy, następnie inkrementuje i. drugi inkrementuje i następnie zapisuje do tablicy. Nie jestem ekspertem od montażu, ale nie widzę jak to samo wykonywalny byłby generowany przez te 2 różne linie kodu.

Tylko moje dwa centy.
 0
Author: Jason Z,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-24 14:22:47