Zgaduję, że bez wyrafinowanej optymalizacji, która może wykryć, że „ss” nigdy nie zmienia się w pętli, to tak. Najlepiej skompilować i spojrzeć na zestaw, aby zobaczyć.
MerickOWA
6
Zależy to od kompilatora, poziomu optymalizacji i tego, co możesz (możesz) zrobić sswewnątrz pętli.
Hristo Iliev
4
Jeśli kompilator może udowodnić, że ssnigdy nie jest modyfikowany, może wyciągnąć obliczenia z pętli.
Daniel Fischer,
10
@Mike: „wymaga analizy w czasie kompilacji dokładnie tego, co robi strlen” - strlen jest prawdopodobnie elementem wewnętrznym, w którym to przypadku optymalizator wie, co robi.
Steve Jessop
3
@MikeSeymour: Nie ma może, może nie. strlen jest zdefiniowane w standardzie języka C, a jego nazwa jest zarezerwowana do użytku zdefiniowanego przez język, więc program nie może swobodnie podawać innej definicji. Kompilator i optymalizator mają prawo przyjąć, że strlen zależy wyłącznie od jego danych wejściowych i nie modyfikuje go ani żadnego stanu globalnego. Wyzwaniem dla optymalizacji jest tutaj określenie, że pamięć wskazywana przez ss nie jest zmieniana przez żaden kod wewnątrz pętli. Jest to całkowicie wykonalne w przypadku obecnych kompilatorów, w zależności od konkretnego kodu.
Eric Postpischil
Odpowiedzi:
138
Tak, strlen()zostaną ocenione w każdej iteracji. Jest możliwe, że w idealnych okolicznościach optymalizator mógłby wywnioskować, że wartość się nie zmieni, ale ja osobiście nie polegałbym na tym.
Zrobiłbym coś takiego
for(int i =0, n = strlen(ss); i < n;++i)
lub ewentualnie
for(int i =0; ss[i];++i)
pod warunkiem, że łańcuch nie zmieni długości podczas iteracji. Jeśli tak, będziesz musiał albo dzwonić za strlen()każdym razem, albo obsługiwać to za pomocą bardziej skomplikowanej logiki.
Jeśli wiesz, że nie manipulujesz łańcuchem, druga jest o wiele bardziej preferowana, ponieważ jest to zasadniczo pętla, która i tak zostanie wykonana strlen.
mlibby
26
@alk: Jeśli ciąg może się skrócić, to oba są nieprawidłowe.
Mike Seymour
3
@alk: jeśli zmieniasz ciąg, pętla for prawdopodobnie nie jest najlepszym sposobem na iterację po każdym znaku. Myślę, że pętla while jest bardziej bezpośrednia i łatwiejsza do zarządzania licznikiem indeksu.
mlibby
2
Idealne okoliczności to kompilacja z GCC pod linuxem, gdzie strlenjest oznaczona jako __attribute__((pure))pozwalająca kompilatorowi na uniknięcie wielu wywołań. GCC Attributes
David Rodríguez - dribeas
6
Druga wersja to forma idealna i najbardziej idiomatyczna. Pozwala na przekazanie ciągu tylko raz, a nie dwa razy, co będzie miało znacznie lepszą wydajność (szczególnie spójność pamięci podręcznej) dla długich ciągów.
R .. GitHub PRZESTAŃ POMÓC NA LODZIE
14
Tak, za każdym razem, gdy używasz pętli. Wtedy za każdym razem obliczy długość struny. więc użyj tego w ten sposób:
char str[30];for(int i =0; str[i]!='\0'; i++){//Something;}
W powyższym kodzie str[i]weryfikuje tylko jeden określony znak w ciągu w lokalizacjii każdym razem, gdy pętla rozpoczyna cykl, dzięki czemu zajmie mniej pamięci i jest bardziej wydajna.
W poniższym kodzie za każdym razem, gdy pętla strlenzostanie uruchomiona , policzy długość całego ciągu, który jest mniej wydajny, zajmuje więcej czasu i zajmuje więcej pamięci.
char str[];for(int i =0; i < strlen(str); i++){//Something;}
Zgadzam się z twierdzeniem „[to] jest bardziej wydajne”, ale zużywa mniej pamięci? Jedyna różnica w zużyciu pamięci, o której mogę pomyśleć, dotyczyłaby stosu wywołań podczas strlenpołączenia, a jeśli pracujesz tak napięty, prawdopodobnie powinieneś pomyśleć o usunięciu kilku innych wywołań funkcji ...
a CVn
@ MichaelKjörling Cóż, jeśli używasz "strlen", to w pętli musi on skanować cały ciąg za każdym razem, gdy pętla jest uruchomiona, podczas gdy w powyższym kodzie "str [ix]" skanuje tylko jeden element podczas każdego cyklu pętla, której położenie jest reprezentowane przez „ix”. Dzięki temu zajmuje mniej pamięci niż „strlen”.
codeDEXTER
1
Nie jestem pewien, czy to ma sens. Bardzo naiwna implementacja strlen byłaby czymś w rodzaju tego, int strlen(char *s) { int len = 0; while(s[len] != '\0') len++; return len; }co jest dokładnie tym, co robisz w kodzie w swojej odpowiedzi. Nie twierdzę, że iterowanie po ciągu raz, a nie dwa, jest bardziej wydajne czasowo , ale nie widzę, aby jedno lub drugie wykorzystywało mniej lub więcej pamięci. A może odnosisz się do zmiennej używanej do przechowywania długości łańcucha?
CVn
@ MichaelKjörling Proszę zobaczyć powyższy edytowany kod i link. A jeśli chodzi o pamięć - za każdym razem, gdy pętla jest uruchomiona, każda iterowana wartość jest przechowywana w pamięci, aw przypadku 'strlen', ponieważ zlicza cały ciąg raz po raz, wymaga więcej pamięci do przechowywania. a także dlatego, że w przeciwieństwie do Javy, C ++ nie ma „garbage collectora”. Wtedy też mogę się mylić. zobacz odnośnik dotyczący braku "Garbage Collectora" w C ++.
codeDEXTER
1
@ aashis2s Brak garbage collectora odgrywa rolę tylko podczas tworzenia obiektów na stercie. Obiekty na stosie zostają zniszczone, gdy tylko zasięg i kończy się.
Ikke,
9
Dobry kompilator może nie obliczyć tego za każdym razem, ale nie sądzę, aby można było być pewnym, że robi to każdy kompilator.
Poza tym kompilator musi wiedzieć, że strlen(ss)to się nie zmienia. Jest to prawdą tylko wtedy, gdy ssnie jest zmieniane w forpętli.
Na przykład, jeśli używasz funkcji tylko do odczytu ssw forpętli, ale nie zadeklarujesz ssparametru -parameter as const, kompilator nie może nawet wiedzieć, że ssnie jest zmieniany w pętli i musi obliczyć strlen(ss)w każdej iteracji.
+1: Nie tylko ssnie można zmieniać w forpętli; nie może być dostępny i zmieniany przez żadną funkcję wywoływaną w pętli (albo dlatego, że jest przekazywany jako argument, albo ponieważ jest zmienną globalną lub zmienną o zasięgu plikowym). Kwalifikacja Const również może być czynnikiem.
Jonathan Leffler
4
Myślę, że jest bardzo mało prawdopodobne, aby kompilator wiedział, że „ss” się nie zmienia. Mogą istnieć zbłąkane wskaźniki wskazujące na pamięć wewnątrz 'ss', o czym kompilator nie ma pojęcia, że mogą zmienić 'ss'
MerickOWA
Jonathan ma rację, lokalny ciąg znaków const może być jedynym sposobem, aby kompilator miał pewność, że nie ma możliwości zmiany „ss”.
MerickOWA
2
@MerickOWA: rzeczywiście, to jedna z rzeczy, które restrictsą potrzebne w C99.
Steve Jessop
4
Odnośnie ostatniego paragrafu: jeśli wywołasz funkcję tylko do odczytu ssw pętli for, to nawet jeśli jej parametr jest zadeklarowany const char*, kompilator nadal musi przeliczyć długość, chyba że (a) wie, że sswskazuje na obiekt const, w przeciwieństwie do bycia wskaźnikiem do stałej lub (b) może wbudować funkcję lub w inny sposób zobaczyć, że jest tylko do odczytu. Pobranie const char*parametru nie jest obietnicą nie modyfikowania wskazanych danych, ponieważ można je rzutować char*i modyfikować, pod warunkiem, że zmodyfikowany obiekt nie jest stałą i nie jest literałem ciągu.
Steve Jessop
4
Jeśli ssjest typu const char *i nie odrzucasz tego, co constw pętli, kompilator może tylko wywołaćstrlen raz, jeśli optymalizacje są włączone. Ale z pewnością nie jest to zachowanie, na które można liczyć.
strlenWynik należy zapisać w zmiennej i użyć tej zmiennej w pętli. Jeśli nie chcesz tworzyć dodatkowej zmiennej, w zależności od tego, co robisz, możesz uciec od odwrócenia pętli w celu wykonania iteracji wstecz.
for(auto i = strlen(s); i >0;--i ){// do whatever// remember value of s[strlen(s)] is the terminating NULL character}
W strlenogóle jest błędem dzwonić . Po prostu zapętlaj, aż dotrzesz do końca.
R .. GitHub PRZESTAŃ POMÓC NA LODZIE
i > 0? Nie powinno tego i >= 0tu być ? Osobiście chciałbym również zacząć od strlen(s) - 1iterowania łańcucha wstecz, wtedy zakończenie \0nie wymaga specjalnego rozważania.
CVn
2
@ MichaelKjörling i >= 0działa tylko wtedy, gdy do zainicjowania strlen(s) - 1, ale jeśli masz łańcuch o zerowej długości początkowych niedomiarów wartości
pretorianów
@ Prætorian, dobry punkt na łańcuchu o zerowej długości. Nie brałem pod uwagę tego przypadku, kiedy pisałem swój komentarz. Czy C ++ ocenia i > 0wyrażenie w początkowej pętli? Jeśli tak się nie stanie, masz rację, przypadek o zerowej długości na pewno przerwie pętlę. Jeśli tak, otrzymujesz „po prostu” znak ze znakiem i== -1 <0, więc nie ma wpisu w pętli, jeśli warunek jest taki i >= 0.
CVn
@ MichaelKjörling Tak, warunek wyjścia jest oceniany przed wykonaniem pętli po raz pierwszy. strlenzwracany typ jest bez znaku, więc zwraca (strlen(s)-1) >= 0wartość true dla łańcuchów o zerowej długości.
Praetorian
3
Formalnie tak, strlen() oczekuje się, że będzie wywoływana przy każdej iteracji.
W każdym razie nie chcę zaprzeczać możliwości istnienia jakiejś sprytnej optymalizacji kompilatora, która zoptymalizuje każde kolejne wywołanie strlen () po pierwszym.
Cały kod predykatu będzie wykonywany w każdej iteracji forpętli. Aby zapamiętać wynik strlen(ss)wywołania, kompilator musiałby przynajmniej to wiedzieć
Funkcja strlen była wolna od skutków ubocznych
Pamięć wskazywana przez ssnie zmienia się w czasie trwania pętli
Kompilator nie wie żadnej z tych rzeczy i dlatego nie może bezpiecznie zapamiętać wyniku pierwszego wywołania
Cóż, mógłby to wiedzieć dzięki analizie statycznej, ale myślę, że chodzi o to, że taka analiza nie jest obecnie zaimplementowana w żadnym kompilatorze C ++, tak?
GManNickG,
@GManNickG z pewnością może udowodnić, że # 1, ale # 2 jest trudniejsze. W przypadku pojedynczego wątku tak, zdecydowanie może to udowodnić, ale nie w środowisku wielowątkowym.
JaredPar
1
Może jestem uparty, ale myślę, że numer dwa jest możliwy również w środowiskach wielowątkowych, ale zdecydowanie nie bez szalenie silnego systemu wnioskowania. Tylko rozmyślam tutaj; zdecydowanie poza zakresem jakiegokolwiek obecnego kompilatora C ++.
GManNickG,
@GManNickG Myślę, że nie jest to możliwe w C / C ++. Bardzo łatwo mogłem ukryć adres ssw a size_tlub podzielić go na kilka bytewartości. Mój przebiegły wątek mógłby wtedy po prostu zapisać bajty do tego adresu, a kompilator znałby sposób zrozumienia, z którym się on wiąże ss.
JaredPar
1
@JaredPar: Przykro mi, że uderzam, możesz twierdzić, że int a = 0; do_something(); printf("%d",a);nie można go zoptymalizować, na podstawie tego, że do_something()może zrobić twoją niezainicjowaną rzecz, lub możesz indeksować kopię zapasową stosu i acelowo modyfikować . W rzeczywistości, gcc 4.5 robi zoptymalizować go do do_something(); printf("%d",0);z -O3
Steve Jessop
2
tak . strlen będzie obliczana za każdym razem, gdy i wzrośnie.
Jeśli nie zmieniłeś ss z in the loop, oznacza to, że nie wpłynie to na logikę inaczej będzie to miało wpływ.
Bezpieczniej jest użyć następującego kodu.
int length = strlen(ss);for(int i =0; i < length ;++ i ){// blabla}
TAK, w prostych słowach. I nie ma małego nie w rzadkich warunkach, w których kompilator chce, jako krok optymalizacji, jeśli stwierdzi, że nie wprowadzono żadnych zmian ss. Ale w bezpiecznym stanie powinieneś pomyśleć, że TAK. Istnieją sytuacje, takie jak multithreadedprogram sterowany zdarzeniami, może on ulec błędom, jeśli uznasz to za NIE. Graj bezpiecznie, ponieważ nie poprawi to zbytnio złożoności programu.
Myślę, że wykonanie tej konkretnej modyfikacji nigdy nie zmienia długości ss, tylko jego zawartość, więc (naprawdę, bardzo sprytny) kompilator może nadal optymalizować strlen.
Stan pętli jest oceniany po każdym powtórzeniu, przed ponownym uruchomieniem pętli.
Uważaj również na typ używany do obsługi długości strun. powinno być size_ttym, co zostało zdefiniowane jak unsigned intw stdio. porównywanie i przesyłanie go do intmoże spowodować poważny problem z luką w zabezpieczeniach.
cóż, zauważyłem, że ktoś mówi, że jest domyślnie optymalizowany przez jakikolwiek "sprytny" nowoczesny kompilator. Przy okazji spójrz na wyniki bez optymalizacji. Próbowałem: Minimalny kod C:
Mój kompilator: g ++ (Ubuntu / Linaro 4.6.3-1ubuntu5) 4.6.3
Polecenie do generowania kodu asemblera: g ++ -S -masm = intel test.cpp
Gotten assembly code at the output:...
L3:
mov DWORD PTR [esp],97
call putchar
add DWORD PTR [esp+40],1.L2:
THIS LOOP IS HERE
**<b>mov ebx, DWORD PTR [esp+40]
mov eax, DWORD PTR [esp+44]
mov DWORD PTR [esp+28],-1
mov edx, eax
mov eax,0
mov ecx, DWORD PTR [esp+28]
mov edi, edx
repnz scasb</b>**
AS YOU CAN SEE it's done every time
mov eax, ecx
not eax
sub eax, 1
cmp ebx, eax
setb al
test al, al
jne .L3
mov eax, 0
.....
Nie chciałbym ufać żadnemu kompilatorowi, który próbował go zoptymalizować, chyba że adres ciągu został restrictzakwalifikowany. Chociaż istnieją przypadki, w których taka optymalizacja byłaby uzasadniona, wysiłek wymagany do wiarygodnej identyfikacji takich przypadków w przypadku braku takiej optymalizacji restrictprawie na pewno przewyższyłby korzyści. Gdyby jednak adres ciągu miał const restrictkwalifikator, wystarczyłoby to samo w sobie do uzasadnienia optymalizacji bez konieczności patrzenia na cokolwiek innego.
superkat
0
Rozwijając odpowiedź Prætoriana, polecam:
for(auto i = strlen(s)-1; i >0;--i ){foo(s[i-1];}
autoponieważ nie chcesz przejmować się tym, który typ zwraca strlen. Kompilator C ++ 11 (npgcc -std=c++0x Nie do końca C ++ 11, ale typy automatyczne działają) zrobi to za Ciebie.
i = strlen(s)ponieważ chcesz porównać 0(patrz poniżej)
i > 0 ponieważ porównanie do 0 jest (nieco) szybsze niż porównanie z jakąkolwiek inną liczbą.
Wadą jest to, że musisz użyć i-1, aby uzyskać dostęp do znaków ciągu.
ss
wewnątrz pętli.ss
nigdy nie jest modyfikowany, może wyciągnąć obliczenia z pętli.Odpowiedzi:
Tak,
strlen()
zostaną ocenione w każdej iteracji. Jest możliwe, że w idealnych okolicznościach optymalizator mógłby wywnioskować, że wartość się nie zmieni, ale ja osobiście nie polegałbym na tym.Zrobiłbym coś takiego
lub ewentualnie
pod warunkiem, że łańcuch nie zmieni długości podczas iteracji. Jeśli tak, będziesz musiał albo dzwonić za
strlen()
każdym razem, albo obsługiwać to za pomocą bardziej skomplikowanej logiki.źródło
strlen
.strlen
jest oznaczona jako__attribute__((pure))
pozwalająca kompilatorowi na uniknięcie wielu wywołań. GCC AttributesTak, za każdym razem, gdy używasz pętli. Wtedy za każdym razem obliczy długość struny. więc użyj tego w ten sposób:
W powyższym kodzie
str[i]
weryfikuje tylko jeden określony znak w ciągu w lokalizacjii
każdym razem, gdy pętla rozpoczyna cykl, dzięki czemu zajmie mniej pamięci i jest bardziej wydajna.Zobacz ten link, aby uzyskać więcej informacji.
W poniższym kodzie za każdym razem, gdy pętla
strlen
zostanie uruchomiona , policzy długość całego ciągu, który jest mniej wydajny, zajmuje więcej czasu i zajmuje więcej pamięci.źródło
strlen
połączenia, a jeśli pracujesz tak napięty, prawdopodobnie powinieneś pomyśleć o usunięciu kilku innych wywołań funkcji ...int strlen(char *s) { int len = 0; while(s[len] != '\0') len++; return len; }
co jest dokładnie tym, co robisz w kodzie w swojej odpowiedzi. Nie twierdzę, że iterowanie po ciągu raz, a nie dwa, jest bardziej wydajne czasowo , ale nie widzę, aby jedno lub drugie wykorzystywało mniej lub więcej pamięci. A może odnosisz się do zmiennej używanej do przechowywania długości łańcucha?Dobry kompilator może nie obliczyć tego za każdym razem, ale nie sądzę, aby można było być pewnym, że robi to każdy kompilator.
Poza tym kompilator musi wiedzieć, że
strlen(ss)
to się nie zmienia. Jest to prawdą tylko wtedy, gdyss
nie jest zmieniane wfor
pętli.Na przykład, jeśli używasz funkcji tylko do odczytu
ss
wfor
pętli, ale nie zadeklarujeszss
parametru -parameter asconst
, kompilator nie może nawet wiedzieć, żess
nie jest zmieniany w pętli i musi obliczyćstrlen(ss)
w każdej iteracji.źródło
ss
nie można zmieniać wfor
pętli; nie może być dostępny i zmieniany przez żadną funkcję wywoływaną w pętli (albo dlatego, że jest przekazywany jako argument, albo ponieważ jest zmienną globalną lub zmienną o zasięgu plikowym). Kwalifikacja Const również może być czynnikiem.restrict
są potrzebne w C99.ss
w pętli for, to nawet jeśli jej parametr jest zadeklarowanyconst char*
, kompilator nadal musi przeliczyć długość, chyba że (a) wie, żess
wskazuje na obiekt const, w przeciwieństwie do bycia wskaźnikiem do stałej lub (b) może wbudować funkcję lub w inny sposób zobaczyć, że jest tylko do odczytu. Pobranieconst char*
parametru nie jest obietnicą nie modyfikowania wskazanych danych, ponieważ można je rzutowaćchar*
i modyfikować, pod warunkiem, że zmodyfikowany obiekt nie jest stałą i nie jest literałem ciągu.Jeśli
ss
jest typuconst char *
i nie odrzucasz tego, coconst
w pętli, kompilator może tylko wywołaćstrlen
raz, jeśli optymalizacje są włączone. Ale z pewnością nie jest to zachowanie, na które można liczyć.strlen
Wynik należy zapisać w zmiennej i użyć tej zmiennej w pętli. Jeśli nie chcesz tworzyć dodatkowej zmiennej, w zależności od tego, co robisz, możesz uciec od odwrócenia pętli w celu wykonania iteracji wstecz.źródło
strlen
ogóle jest błędem dzwonić . Po prostu zapętlaj, aż dotrzesz do końca.i > 0
? Nie powinno tegoi >= 0
tu być ? Osobiście chciałbym również zacząć odstrlen(s) - 1
iterowania łańcucha wstecz, wtedy zakończenie\0
nie wymaga specjalnego rozważania.i >= 0
działa tylko wtedy, gdy do zainicjowaniastrlen(s) - 1
, ale jeśli masz łańcuch o zerowej długości początkowych niedomiarów wartościi > 0
wyrażenie w początkowej pętli? Jeśli tak się nie stanie, masz rację, przypadek o zerowej długości na pewno przerwie pętlę. Jeśli tak, otrzymujesz „po prostu” znak ze znakiemi
== -1 <0, więc nie ma wpisu w pętli, jeśli warunek jest takii >= 0
.strlen
zwracany typ jest bez znaku, więc zwraca(strlen(s)-1) >= 0
wartość true dla łańcuchów o zerowej długości.Formalnie tak,
strlen()
oczekuje się, że będzie wywoływana przy każdej iteracji.W każdym razie nie chcę zaprzeczać możliwości istnienia jakiejś sprytnej optymalizacji kompilatora, która zoptymalizuje każde kolejne wywołanie strlen () po pierwszym.
źródło
Cały kod predykatu będzie wykonywany w każdej iteracji
for
pętli. Aby zapamiętać wynikstrlen(ss)
wywołania, kompilator musiałby przynajmniej to wiedziećstrlen
była wolna od skutków ubocznychss
nie zmienia się w czasie trwania pętliKompilator nie wie żadnej z tych rzeczy i dlatego nie może bezpiecznie zapamiętać wyniku pierwszego wywołania
źródło
ss
w asize_t
lub podzielić go na kilkabyte
wartości. Mój przebiegły wątek mógłby wtedy po prostu zapisać bajty do tego adresu, a kompilator znałby sposób zrozumienia, z którym się on wiążess
.int a = 0; do_something(); printf("%d",a);
nie można go zoptymalizować, na podstawie tego, żedo_something()
może zrobić twoją niezainicjowaną rzecz, lub możesz indeksować kopię zapasową stosu ia
celowo modyfikować . W rzeczywistości, gcc 4.5 robi zoptymalizować go dodo_something(); printf("%d",0);
z -O3tak . strlen będzie obliczana za każdym razem, gdy i wzrośnie.
Jeśli nie zmieniłeś ss z in the loop, oznacza to, że nie wpłynie to na logikę inaczej będzie to miało wpływ.
Bezpieczniej jest użyć następującego kodu.
źródło
Tak,
strlen(ss)
będzie obliczyć długość w każdej iteracji. Jeśli wss
jakiś sposób zwiększasz, a także zwiększaszi
; byłaby nieskończona pętla.źródło
Tak,
strlen()
funkcja jest wywoływana za każdym razem gdy pętla jest oceniana.Jeśli chcesz poprawić wydajność, zawsze pamiętaj, aby zapisać wszystko w zmiennych lokalnych ... To zajmie trochę czasu, ale jest bardzo przydatne ...
Możesz użyć kodu jak poniżej:
źródło
Tak,
strlen(ss)
zostanie obliczona przy każdym uruchomieniu kodu.źródło
Obecnie nie jest to powszechne, ale 20 lat temu na platformach 16-bitowych poleciłbym to:
Nawet jeśli Twój kompilator nie jest zbyt inteligentny w optymalizacji, powyższy kod może jeszcze dać dobry kod asemblera.
źródło
Tak. Test nie wie, że ss nie zmienia się w pętli. Jeśli wiesz, że to się nie zmieni to napisałbym:
źródło
Arrgh, do cholery, nawet w idealnych okolicznościach będzie!
Na dzień dzisiejszy (styczeń 2018 r.) Oraz gcc 7.3 i clang 5.0, jeśli kompilujesz:
Więc mamy:
ss
jest stałym wskaźnikiem.ss
jest zaznaczony__restrict__
ss
(no chyba, że narusza__restrict__
).a mimo to oba kompilatory wykonują
strlen()
każdą pojedynczą iterację tej pętli . Niesamowity.Oznacza to również, że aluzje / pobożne życzenia @Praetorian i @JaredPar nie sprawdzają się.
źródło
TAK, w prostych słowach. I nie ma małego nie w rzadkich warunkach, w których kompilator chce, jako krok optymalizacji, jeśli stwierdzi, że nie wprowadzono żadnych zmian
ss
. Ale w bezpiecznym stanie powinieneś pomyśleć, że TAK. Istnieją sytuacje, takie jakmultithreaded
program sterowany zdarzeniami, może on ulec błędom, jeśli uznasz to za NIE. Graj bezpiecznie, ponieważ nie poprawi to zbytnio złożoności programu.źródło
Tak.
strlen()
obliczane za każdym razem, kiedyi
rośnie i nie jest zoptymalizowany.Poniższy kod pokazuje, dlaczego kompilator nie powinien optymalizować
strlen()
.źródło
strlen
.Z łatwością możemy to przetestować:
Stan pętli jest oceniany po każdym powtórzeniu, przed ponownym uruchomieniem pętli.
Uważaj również na typ używany do obsługi długości strun. powinno być
size_t
tym, co zostało zdefiniowane jakunsigned int
w stdio. porównywanie i przesyłanie go doint
może spowodować poważny problem z luką w zabezpieczeniach.źródło
cóż, zauważyłem, że ktoś mówi, że jest domyślnie optymalizowany przez jakikolwiek "sprytny" nowoczesny kompilator. Przy okazji spójrz na wyniki bez optymalizacji. Próbowałem:
Minimalny kod C:
Mój kompilator: g ++ (Ubuntu / Linaro 4.6.3-1ubuntu5) 4.6.3
Polecenie do generowania kodu asemblera: g ++ -S -masm = intel test.cpp
źródło
restrict
zakwalifikowany. Chociaż istnieją przypadki, w których taka optymalizacja byłaby uzasadniona, wysiłek wymagany do wiarygodnej identyfikacji takich przypadków w przypadku braku takiej optymalizacjirestrict
prawie na pewno przewyższyłby korzyści. Gdyby jednak adres ciągu miałconst restrict
kwalifikator, wystarczyłoby to samo w sobie do uzasadnienia optymalizacji bez konieczności patrzenia na cokolwiek innego.Rozwijając odpowiedź Prætoriana, polecam:
auto
ponieważ nie chcesz przejmować się tym, który typ zwraca strlen. Kompilator C ++ 11 (npgcc -std=c++0x
Nie do końca C ++ 11, ale typy automatyczne działają) zrobi to za Ciebie.i = strlen(s)
ponieważ chcesz porównać0
(patrz poniżej)i > 0
ponieważ porównanie do 0 jest (nieco) szybsze niż porównanie z jakąkolwiek inną liczbą.Wadą jest to, że musisz użyć
i-1
, aby uzyskać dostęp do znaków ciągu.źródło