Dlaczego GCC generuje 15-20% szybszy Kod, jeśli optymalizuję rozmiar zamiast prędkości?
Po raz pierwszy zauważyłem w 2009 roku, że GCC (przynajmniej na moich projektach i na moich maszynach) mają tendencję do generowania zauważalnie szybszego kodu, jeśli zoptymalizuję Rozmiar (-Os
) zamiast speed (-O2
lub -O3
) i od tamtej pory zastanawiam się dlaczego.
Udało mi się stworzyć (dość głupi) kod, który pokazuje to zaskakujące zachowanie i jest wystarczająco mały, aby można go tutaj opublikować.
const int LOOP_BOUND = 200000000;
__attribute__((noinline))
static int add(const int& x, const int& y) {
return x + y;
}
__attribute__((noinline))
static int work(int xval, int yval) {
int sum(0);
for (int i=0; i<LOOP_BOUND; ++i) {
int x(xval+sum);
int y(yval+sum);
int z = add(x, y);
sum += z;
}
return sum;
}
int main(int , char* argv[]) {
int result = work(*argv[1], *argv[2]);
return result;
}
Jeśli skompiluję go za pomocą -Os
, wykonanie tego programu zajmuje 0,38 s, a 0.44 s jeśli jest skompilowany z -O2
lub -O3
. Czasy te są uzyskiwane konsekwentnie i praktycznie bez szumów (gcc 4.7.2, x86_64 GNU/Linux, Intel Core i5-3320M).
(Update: przeniosłem cały kod assembly do GitHub : sprawili, że post był nadęty i najwyraźniej dodali bardzo mało wartości do pytań, ponieważ flagi fno-align-*
mają taki sam efekt.)
Oto wygenerowany zestaw z -Os
oraz -O2
.
Niestety, mój zrozumienie assembly jest bardzo ograniczone, więc nie mam pojęcia, czy to, co zrobiłem dalej, było poprawne: chwyciłem assembly za -O2
i połączyłem wszystkie jego różnice w assembly za -Os
Z wyjątkiem linii .p2align
, wynik tutaj . Ten kod nadal działa w 0.38 s i jedyna różnica to .p2align
rzeczy.
Jeśli dobrze zgaduję, są to podkładki do wyrównania stosu. Zgodnie z dlaczego GCC Pad działa z NOPs? jest robione w nadziei, że kod będzie działać szybciej, ale najwyraźniej ta optymalizacja odwróciła się w moim przypadku.
Czy to wyściółka jest winowajcą w tym przypadku? Dlaczego i jak?
Hałas, który wytwarza, praktycznie uniemożliwia mikro-optymalizację czasu.Jak mogę się upewnić, że takie przypadkowe wyrównania szczęścia / pecha nie przeszkadzają, gdy robię mikro-optymalizacje (niezwiązane z wyrównaniem stosu) w C lub C++ source kod?
UPDATE:
Podążając za odpowiedź Pascala Cuoqa trochę majstrowałem z wyrównaniami. Po przejściu -O2 -fno-align-functions -fno-align-loops
do GCC, wszystkie .p2align
znikają z zestawu, a wygenerowany plik wykonywalny działa w ciągu 0,38 s. zgodnie z dokumentacja gcc :
- Os włącza wszystkie optymalizacje - O2 [ale] - Os wyłącza następujące flagi optymalizacji:
-falign-functions -falign-jumps -falign-loops <br/> -falign-labels -freorder-blocks -freorder-blocks-and-partition <br/> -fprefetch-loop-arrays <br/>
Wygląda to na (mis)wyrównanie problem.
Nadal jestem sceptyczny -march=native
, jak sugerowano w odpowiedzi Marat Duhan . Nie jestem przekonany, że nie chodzi tylko o ingerencję w ten (błędny)problem wyrównania; nie ma to absolutnie żadnego wpływu na moją maszynę. (Niemniej jednak, podniosłem jego odpowiedź.)
Aktualizacja 2:
Możemy zdjąć -Os
ze zdjęcia. następujące czasy są uzyskiwane przez kompilację z
-
-O2 -fno-omit-frame-pointer
0.37 s -
-O2 -fno-align-functions -fno-align-loops
0.37 s -
-S -O2
następnie ręcznie przesuwając zespóładd()
powork()
0.37 s -
-O2
0.44 s
perf
, ale wyjście perf stat
i perf report
nie ma dla mnie większego sensu. Jednak mogłem uzyskać tylko jeden spójny wynik:
-O2
:
602,312,864 stalled-cycles-frontend # 0.00% frontend cycles idle
3,318 cache-misses
0.432703993 seconds time elapsed
[...]
81.23% a.out a.out [.] work(int, int)
18.50% a.out a.out [.] add(int const&, int const&) [clone .isra.0]
[...]
¦ __attribute__((noinline))
¦ static int add(const int& x, const int& y) {
¦ return x + y;
100.00 ¦ lea (%rdi,%rsi,1),%eax
¦ }
¦ ? retq
[...]
¦ int z = add(x, y);
1.93 ¦ ? callq add(int const&, int const&) [clone .isra.0]
¦ sum += z;
79.79 ¦ add %eax,%ebx
Dla fno-align-*
:
604,072,552 stalled-cycles-frontend # 0.00% frontend cycles idle
9,508 cache-misses
0.375681928 seconds time elapsed
[...]
82.58% a.out a.out [.] work(int, int)
16.83% a.out a.out [.] add(int const&, int const&) [clone .isra.0]
[...]
¦ __attribute__((noinline))
¦ static int add(const int& x, const int& y) {
¦ return x + y;
51.59 ¦ lea (%rdi,%rsi,1),%eax
¦ }
[...]
¦ __attribute__((noinline))
¦ static int work(int xval, int yval) {
¦ int sum(0);
¦ for (int i=0; i<LOOP_BOUND; ++i) {
¦ int x(xval+sum);
8.20 ¦ lea 0x0(%r13,%rbx,1),%edi
¦ int y(yval+sum);
¦ int z = add(x, y);
35.34 ¦ ? callq add(int const&, int const&) [clone .isra.0]
¦ sum += z;
39.48 ¦ add %eax,%ebx
¦ }
Dla -fno-omit-frame-pointer
:
404,625,639 stalled-cycles-frontend # 0.00% frontend cycles idle
10,514 cache-misses
0.375445137 seconds time elapsed
[...]
75.35% a.out a.out [.] add(int const&, int const&) [clone .isra.0] ¦
24.46% a.out a.out [.] work(int, int)
[...]
¦ __attribute__((noinline))
¦ static int add(const int& x, const int& y) {
18.67 ¦ push %rbp
¦ return x + y;
18.49 ¦ lea (%rdi,%rsi,1),%eax
¦ const int LOOP_BOUND = 200000000;
¦
¦ __attribute__((noinline))
¦ static int add(const int& x, const int& y) {
¦ mov %rsp,%rbp
¦ return x + y;
¦ }
12.71 ¦ pop %rbp
¦ ? retq
[...]
¦ int z = add(x, y);
¦ ? callq add(int const&, int const&) [clone .isra.0]
¦ sum += z;
29.83 ¦ add %eax,%ebx
Wygląda na to, że opóźniamy połączenie do add()
w powolnym przypadku.
Zbadałem Wszystko co perf -e
może wypluć na moją maszynę; nie tylko statystyki, które są podane powyżej.
Dla tego samego pliku wykonywalnego stalled-cycles-frontend
pokazuje liniową korelację z czasem wykonania; nie zauważyłem niczego innego, co by tak wyraźnie korelowało. (Porównywanie stalled-cycles-frontend
dla różnych plików wykonywalnych nie ma sensu ja.)
Dodałem błędy pamięci podręcznej, gdy pojawił się jako pierwszy komentarz. Zbadałem wszystkie błędy pamięci podręcznej, które można zmierzyć na mojej maszynie przez perf
, nie tylko te podane powyżej. Chybienia pamięci podręcznej są bardzo głośne i wykazują niewielką lub zerową korelację z czasem wykonania.
6 answers
Domyślnie Kompilatory optymalizują dla" przeciętnego " procesora. Ponieważ różne procesory preferują różne sekwencje instrukcji, optymalizacje kompilatorów włączone przez -O2
mogą przynieść korzyści przeciętnemu procesorowi, ale zmniejszyć wydajność konkretnego procesora(i to samo dotyczy -Os
). Jeśli spróbujesz tego samego przykładu na różnych procesorach, przekonasz się, że niektóre z nich korzystają z -O2
, podczas gdy inne są bardziej korzystne dla -Os
optymalizacji.
Oto wyniki dla time ./test 0 0
na kilka procesorów (podany czas użytkownika):
Processor (System-on-Chip) Compiler Time (-O2) Time (-Os) Fastest
AMD Opteron 8350 gcc-4.8.1 0.704s 0.896s -O2
AMD FX-6300 gcc-4.8.1 0.392s 0.340s -Os
AMD E2-1800 gcc-4.7.2 0.740s 0.832s -O2
Intel Xeon E5405 gcc-4.8.1 0.603s 0.804s -O2
Intel Xeon E5-2603 gcc-4.4.7 1.121s 1.122s -
Intel Core i3-3217U gcc-4.6.4 0.709s 0.709s -
Intel Core i3-3217U gcc-4.7.3 0.708s 0.822s -O2
Intel Core i3-3217U gcc-4.8.1 0.708s 0.944s -O2
Intel Core i7-4770K gcc-4.8.1 0.296s 0.288s -Os
Intel Atom 330 gcc-4.8.1 2.003s 2.007s -O2
ARM 1176JZF-S (Broadcom BCM2835) gcc-4.6.3 3.470s 3.480s -O2
ARM Cortex-A8 (TI OMAP DM3730) gcc-4.6.3 2.727s 2.727s -
ARM Cortex-A9 (TI OMAP 4460) gcc-4.6.3 1.648s 1.648s -
ARM Cortex-A9 (Samsung Exynos 4412) gcc-4.6.3 1.250s 1.250s -
ARM Cortex-A15 (Samsung Exynos 5250) gcc-4.7.2 0.700s 0.700s -
Qualcomm Snapdragon APQ8060A gcc-4.8 1.53s 1.52s -Os
W niektórych przypadkach możesz złagodzić efekt niekorzystnych optymalizacji, prosząc gcc
o optymalizację dla konkretnego procesora (używając opcji -mtune=native
lub -march=native
):
Processor Compiler Time (-O2 -mtune=native) Time (-Os -mtune=native)
AMD FX-6300 gcc-4.8.1 0.340s 0.340s
AMD E2-1800 gcc-4.7.2 0.740s 0.832s
Intel Xeon E5405 gcc-4.8.1 0.603s 0.803s
Intel Core i7-4770K gcc-4.8.1 0.296s 0.288s
Aktualizacja: Na Core i3 bazującym na Ivy Bridge trzy wersje gcc
(4.6.4
, 4.7.3
, i 4.8.1
) wytwarzają pliki binarne o znacznie różnej wydajności, ale kod złożenia ma tylko subtelne różnice. Jak na razie nie mam wyjaśnienia tego fakt.
Assembly from gcc-4.6.4 -Os
(executes in 0.709 secs):
00000000004004d2 <_ZL3addRKiS0_.isra.0>:
4004d2: 8d 04 37 lea eax,[rdi+rsi*1]
4004d5: c3 ret
00000000004004d6 <_ZL4workii>:
4004d6: 41 55 push r13
4004d8: 41 89 fd mov r13d,edi
4004db: 41 54 push r12
4004dd: 41 89 f4 mov r12d,esi
4004e0: 55 push rbp
4004e1: bd 00 c2 eb 0b mov ebp,0xbebc200
4004e6: 53 push rbx
4004e7: 31 db xor ebx,ebx
4004e9: 41 8d 34 1c lea esi,[r12+rbx*1]
4004ed: 41 8d 7c 1d 00 lea edi,[r13+rbx*1+0x0]
4004f2: e8 db ff ff ff call 4004d2 <_ZL3addRKiS0_.isra.0>
4004f7: 01 c3 add ebx,eax
4004f9: ff cd dec ebp
4004fb: 75 ec jne 4004e9 <_ZL4workii+0x13>
4004fd: 89 d8 mov eax,ebx
4004ff: 5b pop rbx
400500: 5d pop rbp
400501: 41 5c pop r12
400503: 41 5d pop r13
400505: c3 ret
Assembly from gcc-4.7.3 -Os
(executes in 0.822 secs):
00000000004004fa <_ZL3addRKiS0_.isra.0>:
4004fa: 8d 04 37 lea eax,[rdi+rsi*1]
4004fd: c3 ret
00000000004004fe <_ZL4workii>:
4004fe: 41 55 push r13
400500: 41 89 f5 mov r13d,esi
400503: 41 54 push r12
400505: 41 89 fc mov r12d,edi
400508: 55 push rbp
400509: bd 00 c2 eb 0b mov ebp,0xbebc200
40050e: 53 push rbx
40050f: 31 db xor ebx,ebx
400511: 41 8d 74 1d 00 lea esi,[r13+rbx*1+0x0]
400516: 41 8d 3c 1c lea edi,[r12+rbx*1]
40051a: e8 db ff ff ff call 4004fa <_ZL3addRKiS0_.isra.0>
40051f: 01 c3 add ebx,eax
400521: ff cd dec ebp
400523: 75 ec jne 400511 <_ZL4workii+0x13>
400525: 89 d8 mov eax,ebx
400527: 5b pop rbx
400528: 5d pop rbp
400529: 41 5c pop r12
40052b: 41 5d pop r13
40052d: c3 ret
Assembly from gcc-4.8.1 -Os
(executes in 0.994 secs):
00000000004004fd <_ZL3addRKiS0_.isra.0>:
4004fd: 8d 04 37 lea eax,[rdi+rsi*1]
400500: c3 ret
0000000000400501 <_ZL4workii>:
400501: 41 55 push r13
400503: 41 89 f5 mov r13d,esi
400506: 41 54 push r12
400508: 41 89 fc mov r12d,edi
40050b: 55 push rbp
40050c: bd 00 c2 eb 0b mov ebp,0xbebc200
400511: 53 push rbx
400512: 31 db xor ebx,ebx
400514: 41 8d 74 1d 00 lea esi,[r13+rbx*1+0x0]
400519: 41 8d 3c 1c lea edi,[r12+rbx*1]
40051d: e8 db ff ff ff call 4004fd <_ZL3addRKiS0_.isra.0>
400522: 01 c3 add ebx,eax
400524: ff cd dec ebp
400526: 75 ec jne 400514 <_ZL4workii+0x13>
400528: 89 d8 mov eax,ebx
40052a: 5b pop rbx
40052b: 5d pop rbp
40052c: 41 5c pop r12
40052e: 41 5d pop r13
400530: c3 ret
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
2017-05-07 21:46:18
Mój kolega pomógł mi znaleźć wiarygodną odpowiedź na moje pytanie. Zauważył znaczenie granicy 256 bajtów. Nie jest zarejestrowany tutaj i zachęcił mnie do napisania odpowiedzi sam (i wziąć całą sławę).
Krótka odpowiedź:
Czy to wyściółka jest winowajcą w tym przypadku? Dlaczego i jak?
Wszystko sprowadza się do wyrównania. wyrównania mogą mieć znaczący wpływ na wydajność, dlatego mamy -falign-*
flagi na pierwszym miejscu.
Złożyłem a (fałszywy?) Zgłoś błąd deweloperom gcc . Okazuje się, że domyślnym zachowaniem jest " domyślnie wyrównujemy pętle do 8 bajtów, ale staramy się wyrównać je do 16 bajtów, jeśli nie musimy wypełniać ponad 10 bajtów." najwyraźniej ta wartość domyślna nie jest najlepszym wyborem w tym konkretnym przypadku i na moim komputerze. Clang 3.4 (trunk) z -O3
robi odpowiednie wyrównanie i wygenerowany kod nie pokazuje tego dziwnego zachowanie.
Oczywiście, jeśli niewłaściwe dopasowanie zostanie zrobione, to pogorszy sprawę. niepotrzebne / złe wyrównanie po prostu pochłania bajty bez powodu i potencjalnie zwiększa liczbę braków pamięci podręcznej itp.
Hałas, który wytwarza, sprawia, że mikro-optymalizacje czasowe niemożliwe.
Jak mogę się upewnić, że tak przypadkowe szczęście / pecha nie przeszkadzają mi mikro-optymalizacje (niezwiązane ze stosem alignment) w C lub c++ kody źródłowe?
Po Prostu powiedz gcc, aby wykonała właściwe wyrównanie:
g++ -O2 -falign-functions=16 -falign-loops=16
Długa odpowiedź:
Kod będzie działał wolniej, jeśli:
Granica
XX
bajtów przecinaadd()
w środku (XX
jest zależna od maszyny).Jeśli wywołanie {[9] } musi przeskoczyć granicę
XX
bajtów, a cel nie jest wyrównany.If
add()
is not / align = "left" /Jeśli pętla nie jest wyrównana.
Pierwsze 2 są pięknie widoczne na kodach i wynikach, które Marat Dukhan uprzejmie opublikował . W tym przypadku, gcc-4.8.1 -Os
(wykonuje w 0.994 sekund):
00000000004004fd <_ZL3addRKiS0_.isra.0>:
4004fd: 8d 04 37 lea eax,[rdi+rsi*1]
400500: c3
Granica 256 bajtów przecina add()
w samym środku i ani add()
, ani pętla nie są wyrównane. Niespodzianka, niespodzianka, to najwolniejsza sprawa!
W przypadku gcc-4.7.3 -Os
(wykonuje się w 0.822 sek.), granica 256 bajtów przecina się tylko na zimno sekcja (ale ani pętla, ani add()
nie jest przecięta):
00000000004004fa <_ZL3addRKiS0_.isra.0>:
4004fa: 8d 04 37 lea eax,[rdi+rsi*1]
4004fd: c3 ret
[...]
40051a: e8 db ff ff ff call 4004fa <_ZL3addRKiS0_.isra.0>
Nic nie jest wyrównane, a wywołanie add()
musi przeskoczyć granicę 256 bajtów. Ten kod jest drugim najwolniejszym.
gcc-4.6.4 -Os
(wykonuje się w 0.709 sek.), mimo że nic nie jest wyrównane, wywołanie add()
nie musi przeskakiwać przez granicę 256 bajtów, a cel jest dokładnie 32 bajty od siebie:
4004f2: e8 db ff ff ff call 4004d2 <_ZL3addRKiS0_.isra.0>
4004f7: 01 c3 add ebx,eax
4004f9: ff cd dec ebp
4004fb: 75 ec jne 4004e9 <_ZL4workii+0x13>
To najszybszy ze wszystkich trzech. Dlaczego granica 256 bajtów jest speacial na jego maszynie, zostawię go od niego zależy, czy to rozgryzie. Nie mam takiego procesora.
Teraz, na mojej maszynie nie dostaję tego 256-bajtowego efektu granicznego. Tylko funkcja i wyrównanie pętli działa na mojej maszynie. Jeśli zdam g++ -O2 -falign-functions=16 -falign-loops=16
, wszystko wróci do normy: zawsze dostaję najszybszą sprawę, a czas nie jest już wrażliwy na flagę -fno-omit-frame-pointer
. Mogę przekazać g++ -O2 -falign-functions=32 -falign-loops=32
lub dowolną wielokrotność 16, Kod też nie jest wrażliwy na to.
Po raz pierwszy zauważyłem w 2009 roku, że gcc (przynajmniej na moim projekty i na mojej maszyn) mają tendencję do generowania zauważalnie szybszego kodu, jeśli I optimize for size (- Os) zamiast speed (- O2 lub-O3) and I have been zastanawiam się, dlaczego.
Prawdopodobnym wyjaśnieniem jest to, że miałem hotspoty, które były wrażliwe na wyrównanie, tak jak w tym przykładzie. Przez mieszanie się z flagami (przechodząc -Os
zamiast -O2
), hotspoty zostały wyrównane w szczęśliwy sposób przez przypadek i Kod stał się szybszy. to nie miało nic wspólnego optymalizacja pod kątem rozmiaru: dzięki temu hotspoty zostały lepiej wyrównane przez przypadek. od teraz będę sprawdzał efekty dostosowania do moich projektów.
add()
może zawieść?
Rozważ to:
// add.cpp
int add(const int& x, const int& y) {
return x + y;
}
I w osobnym pliku:
// main.cpp
int add(const int& x, const int& y);
const int LOOP_BOUND = 200000000;
__attribute__((noinline))
static int work(int xval, int yval) {
int sum(0);
for (int i=0; i<LOOP_BOUND; ++i) {
int x(xval+sum);
int y(yval+sum);
int z = add(x, y);
sum += z;
}
return sum;
}
int main(int , char* argv[]) {
int result = work(*argv[1], *argv[2]);
return result;
}
I skompilowane jako: g++ -O2 add.cpp main.cpp
.
Gcc nie będzie w linii add()
!
To wszystko, tak łatwo jest bezinteresownie tworzyć hotspoty, jak ten w OP. oczywiście to częściowo moja wina: gcc jest doskonałym kompilatorem. jeśli skompiluję powyższe jako: g++ -O2 -flto add.cpp main.cpp
, czyli jeśli wykonam optymalizację czasu łącza, kod uruchomi się w 0.19 s!
(Inlining jest sztucznie wyłączany w OP, stąd kod w OP był 2x wolniejszy).
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
2017-05-23 12:18:02
Dodaję ten post-Akceptuj, aby podkreślić, że badano wpływ wyrównania na ogólną wydajność programów - w tym dużych -. Na przykład Ten artykuł (i wierzę, że wersja ta pojawiła się również w CACM) pokazuje, w jaki sposób kolejność połączeń i zmiany rozmiaru środowiska OS były wystarczające, aby znacząco zmienić wydajność. Przypisują to wyrównaniu "gorących pętli".
Ten artykuł, zatytułowany " wytwarzanie błędnych danych bez robienia czegokolwiek oczywiście źle!"mówi, że niezamierzone eksperymentalne uprzedzenia spowodowane niemal niekontrolowanymi różnicami w środowiskach uruchamiania programów prawdopodobnie pozbawiają wiele wyników testów odniesienia znaczenia.
Myślę, że napotkasz inny kąt przy tej samej obserwacji.
W przypadku kodu krytycznego dla wydajności jest to całkiem dobry argument dla Systemów, które oceniają środowisko w czasie instalacji lub uruchomienia i wybierają najlepsze lokalne spośród różnie zoptymalizowanych wersji kluczowych procedur.
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
2013-10-27 21:07:00
Myślę, że możesz uzyskać taki sam wynik jak to, co zrobiłeś:
Chwyciłem zespół dla-O2 i połączyłem wszystkie jego różnice w zespole dla-Os z wyjątkiem .linie P2:
... za pomocą -O2 -falign-functions=1 -falign-jumps=1 -falign-loops=1 -falign-labels=1
. Kompilowałem wszystko z tymi opcjami, które były szybsze niż zwykłe -O2
za każdym razem, gdy trudziłem się mierzeniem, przez 15 lat.
Również, dla zupełnie innego kontekstu (w tym innego kompilatora), zauważyłem, że sytuacja jest similar : opcja, która ma "optymalizować rozmiar kodu, a nie szybkość", optymalizuje rozmiar i szybkość kodu.
Jeśli dobrze zgaduję, są to podkładki do wyrównania stosu.
Nie, to nie ma nic wspólnego ze stosem, NOPs, które są generowane domyślnie i że opcje-falign - * = 1 prevent służą do wyrównania kodu.
Zgodnie z Dlaczego GCC Pad działa z NOPs? robi się to w nadziei, że kod będzie działał szybciej, ale najwyraźniej w moim przypadku ta optymalizacja przyniosła odwrotny skutek.
Czy to padding jest winowajcą w tym przypadku? Dlaczego i jak?
Jest bardzo prawdopodobne, że padding jest winowajcą. Powodem, dla którego padding jest uważany za konieczny i przydatny w niektórych przypadkach, jest to, że kod jest zwykle pobierany w liniach po 16 bajtów (szczegóły można znaleźć w zasobach optymalizacyjnych Agnera Foga , które różnią się w zależności od modelu procesora). Wyrównanie funkcji, pętli lub etykiety na granicy 16 bajtów oznacza, że statystycznie zwiększa się prawdopodobieństwo, że jedna linia mniej będzie potrzebna do przechowywania funkcji lub pętli. Oczywiście, to działa wstecz, ponieważ te NOP zmniejszają gęstość kodu, a tym samym wydajność pamięci podręcznej. W przypadku pętli i etykiety, NOPs może być nawet konieczne wykonanie raz (gdy wykonanie dociera do pętli/etykiety normalnie, w przeciwieństwie do skoku).
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
2013-10-19 21:59:11
Jeśli twój program jest ograniczony kodem L1 cache, optymalizacja pod kątem rozmiaru nagle zaczyna się opłacać.
Kiedy ostatnio sprawdzałem, kompilator nie jest wystarczająco mądry, aby to rozgryźć we wszystkich przypadkach.
W Twoim przypadku,- O3 prawdopodobnie generuje kod wystarczający dla dwóch linii bufora, ale-Os mieści się w jednej linii bufora.
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
2013-10-24 15:48:50
Bynajmniej nie jestem ekspertem w tej dziedzinie, ale pamiętam, że nowoczesne procesory są dość wrażliwe, jeśli chodzi o przewidywanie gałęzi . Algorytmy używane do przewidywania gałęzi są (a przynajmniej były w czasach, gdy pisałem kod asemblera) oparte na kilku właściwościach kodu, w tym odległości celu i kierunku.
Scenariusz, który przychodzi na myśl, to małe pętle. Gdy gałąź cofała się i odległość nie była zbyt duża, przewidywanie gałęzi optymalizowało się w tym przypadku, ponieważ wszystkie małe pętle są wykonane w ten sposób. Te same zasady mogą wejść w grę, gdy zamienisz lokalizację add
i work
w wygenerowanym kodzie lub gdy pozycja obu nieznacznie się zmieni.
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
2017-05-23 10:31:09