C++, Operator zwiększania

0

Witam,
który operator zadziała szybciej:
++i

czy i++

i dlaczego?

0

W tym pierwszy od razu zwiekszana jest wartosc,a w i++ to dopiero w nastepnym kroku widac ze jest zwiększona.

0

Przy ++A najpierw wykonywana zostaje operacja inkrementacji, potem zwracana jest wartość zmiennej.

Przy A++ Pierw zwracana jest wartość zmiennej, potem dokonywana operacja inkrementacji.

Nie ma różnicy w działaniu pod względem prędkości, różnica jest w zwracanej wartości.

0

To zalezy jesli chodzi o typy wbudowane to maja porownywalna predkość (chodź chyba ++i i tak jest ciut szybszy ). Jeśli natomiast chodzi o typy tworzone przez użytkonika to ++i jest bezsprzecznie szybsze niż i++. Dlaczego ? Poczytaj troche o przeciążaniu tych dwoch operatorów to zrozumiesz różnice.

0

@xxxxxx: Poczytaj troche jak wygladaja te operatory w asmie to zobaczysz, ze roznicy w predkosci nie ma...

0
johny_bravo napisał(a)

@xxxxxx: Poczytaj troche jak wygladaja te operatory w asmie to zobaczysz, ze roznicy w predkosci nie ma...

Dla typów prostych tak, to praktycznie nie ma znaczenia, ale iteratorów już może mieć - w przypadku It++ tworzona jest kopia.

--- edit ---

int i=0;
string text("tralala");
string::iterator it=text.begin();


while(it!=text.end())
{
	cout<<*it<<endl;
	it++;
	i++;
}

while(it!=text.end())
{
	cout<<*it<<endl;
	++it;
	++i;
}

W obu przypadkach na VC to absolutnie nie ma znaczenia - kod asemblerowy jest identyczny.

0

W obu przypadkach na VC to absolutnie nie ma znaczenia - kod asemblerowy jest identyczny

to ładnie operator napisali. Ale czasem, jak jest jakiś skomplikowany obiekt, to nie ma bata - trzeba stary stan zapamiętać, zmodyfikować obiekt i zwrócić "wspomnienie". Wtedy, jeśli kompilator nie ma jakiejś agresywnej optymalizacji, może być narzut - szczególnie, jeśli jeszcze konstruktor kopiujący nie jest trywialny.

btw: w jakiejś książce o czystym C czytałem dyskusję nt typów wbudowanych. Mianowicie zmienna++ daje podobno większą swobodę optymalizatorowi, no bo sam może zdecydować, kiedy to zwiększenie wykonać (zaraz po zwróceniu wartości, trochę później, albo i duużo później: dopiero podczas kolejnego odczytu zmienna) - to może pozwolić na agresywniejszą optymalizację (np w ogóle nie zmieni wartości zmiennej, jeśli nigdzie później ta wartość nie jest używana). ++zmienna ogranicza tutaj optymalizator: modyfikacja musi nastąpić u i właśnie tu.

0
Ranides napisał(a)

to ładnie operator napisali. Ale czasem, jak jest jakiś skomplikowany obiekt, to nie ma bata - trzeba stary stan zapamiętać, zmodyfikować obiekt i zwrócić "wspomnienie".

Tak, tylko zwróć uwagę, że stara wersja It nie jest nigdzie wykorzystywana więc kompilator usunął deklarację i inicjalizację obiektu tymczasowego (sprytny kompilator poszukałby operatora preinkrementacji :)) Prawdopodobnie w przypadku bardziej skomplikowanych iteratorów wyglądałoby to podobnie. Dodam, że operator++ z przykładu został ładnie wmiksowany w rozwinięty kod cout - nie dało się ustawić breakpoint'a.

0

więc kompilator usunął deklarację i inicjalizację obiektu tymczasowego (sprytny kompilator poszukałby operatora preinkrementacji :))

a co by się działo, gdyby inicjalizacja miała efekty uboczne? takie coś przejdzie tylko wtedy, kiedy używasz do zachowania stanu automatycznie wygenerowanego konstruktora kopiującego / operatora przypisania . Jak zdefiniujesz dla klasy własny, to jego wywołanie jest gwarantowane. Ale to, co opisałeś, to i tak bardzo dużo i przyznaję, tak od razu o tym nie pomyślałem.

Dodam, że operator++ z przykładu został ładnie wmiksowany w rozwinięty kod cout - nie dało się ustawić breakpoint'a.

mów mi więcej. ja śledzę debugerem tylko niezoptymalizowany kod z wyłączonym wszystkim (w tym inline'ami), bo bym się pogubił całkiem. A co się dzieje w wersji release, to już pojęcia nie mam ;)

0
Ranides napisał(a)

a co by się działo, gdyby inicjalizacja miała efekty uboczne? takie coś przejdzie tylko wtedy, kiedy używasz do zachowania stanu automatycznie wygenerowanego konstruktora kopiującego / operatora przypisania . Jak zdefiniujesz dla klasy własny, to jego wywołanie jest gwarantowane. Ale to, co opisałeś, to i tak bardzo dużo i przyznaję, tak od razu o tym nie pomyślałem.

Nie no wiadomo, że nie zawsze się da usunąć deklaracje/inicjalizacje obiektu. W sumie to tylko kompilator ;)

mów mi więcej. ja śledzę debugerem tylko niezoptymalizowany kod z wyłączonym wszystkim (w tym inline'ami), bo bym się pogubił całkiem. A co się dzieje w wersji release, to już pojęcia nie mam ;)

Heh to fakt, kod wygląda zupełnie inaczej ;) Np. iterator stringa to zwykły wskaźnik. Ale sprawdziłem dla list<string> i:

list<string>::iterator it=lista.begin();

while(it!=lista.end())
{
	cout<<*it;
	it++;
}

//*** asm ***
//	while(it!=lista.end())
0040409B  cmp         esi,edi 
0040409D  je          main+1B7h (4040B7h) 
0040409F  nop              
//	{
//		cout<<*it;
004040A0  lea         ecx,[esi+8] 
004040A3  push        ecx  
004040A4  push        offset std::cout (417218h) 
004040A9  call        std::operator<<<char,std::char_traits<char>,std::allocator<char> > (401D00h) 
//		it++;
004040AE  mov         esi,dword ptr [esi] //<--- it=it->next
004040B0  add         esp,8 

004040B3  cmp         esi,edi //<---  while(it!=lista.end())
004040B5  jne         main+1A0h (4040A0h) 
//	}
0

proponuje sprobowac raczej z kodem, ktory potem uzywa wartosci zwracanych.. tzn porownuj razcej:

list<string>::iterator it=lista.begin();
list<string>::iterator it2;
list<string>::iterator it3;
it2 = it++;
it3 = ++it;

zwracana wartosci przy it++ jest obiektem tymczasowym - kompilator ma w tym przypadku totalna dowolnosc optymalizacji i nie musi ich tworzyc jesli nie sa konieczne --- nawet jesli maja wlasne cctory z efektami ubocznymi!

0

No ok, ale zwróć uwagę, że w wersji release operuje na wskaźnikach więc narzut będzie nieznaczny. Inny przykład:

int c=0;
while(lista.end()!=it++)c++;
mov         ebx,dword ptr [eax] 
xor         edi,edi 
xor         ecx,ecx 
cmp         esi,eax 
je          main+1A9h (4040A9h) 
nop              
mov         eax,ebx ; eax - kopia
mov         ebx,dword ptr [ebx] ; it+=1
inc         ecx  ; tu wiadomo
cmp         esi,eax ; esi - end(); 
jne         main+1A0h (4040A0h) 

czyli jak widać w przypadku iteratorów listy nie ma mowy o obiektach tymczasowych :P

0

o to mi wlasnie chodzilo mowiac o optymalizacji. jesli jest mozliwa, obiekty tymczasowe sa wycinane. nie zmienia to jednak faktu, ze napisanie [abstrahujac od poprawnosci!!! lista przeciez moze miec 0 elem i boom]:

int c=0;
while(lista.end()!=++it)++c;

da asm "optymalniejszy" o instrukcje:

mov         ebx,dword ptr [eax] 
xor         edi,edi 
xor         ecx,ecx 
cmp         esi,eax 
je          main+1A9h (4040A9h) 
nop              
mov         ebx,dword ptr [ebx] ; it+=1, bez kopii w EAX
inc         ecx 
cmp         esi, ebx         ;EBX!
jne         main+1A0h (4040A0h) 

o wiele wieksza roznica bedzie, jesli to nie beda iteratory po stringu, tylko np. iteratory kontenera trzymajacego obiekty na pofragmentowanej tablicy, czy iteratory majace zaimplementowana jakies ciezka logike np. zestaw wskaznikow do szybkiego skakania +1,+5,+10,+50 elementow itp wtedy kod uzywajacy it++ wymusza (jak w Twoim przypadku!) niepotrzebne utworzenie kopii i jest ciezsze niz napisanie analogicznego (choc moze totalnie innego i mniej czytelnego) kodu opartego na ++it. nie twierdze ze it++ zawsze bedzie wolniejszy/cieszy -- zerknij na c++ i ++c. wynik c++ jesl w proznie, wiec c++ jest optymalizowane i kopia wartosc nie jest tworzona i nigdy nie bedzie w takim przypadku. poprsotu: X++ moze byc ciezsze od ++X, ale wcale nie musi. jesli nam zalezy na tym zeby na pewno uzywac jak najlzejszych operacji -- uzywajmy ++X, a X++ dopiero jak naprawde musimy i jest w tym sens

0

o wiele wieksza roznica bedzie, jesli to nie beda iteratory po stringu, tylko np. iteratory kontenera trzymajacego obiekty na pofragmentowanej tablicy

Nie wiem czy zauważyłeś, że ostatni przykład był z iteratorami listy ;)

Zresztą, ja nigdzie nie pisałem, że w ogóle nie ma różnicy - czasem ma, czasem nie ma. Sam stosuję (z nawyku) post inkrementację i jakoś nie zauważyłem znacznego spadku wydajności. Jeżeli takowy by wystąpił wtedy bym kombinował.

0

sens mojej wypowiedzi dotyczyl "iteratory majace zaimplementowana jakies ciezka logike np. zestaw wskaznikow do szybkiego skakania +1,+5,+10,+50 elementow "

1 użytkowników online, w tym zalogowanych: 0, gości: 1