Neden gcc eğer hız yerine BOYUTU için optimize eğer 15-20% daha hızlı kod oluşturur mu?
Ben ilk gcc (benim makinede en azından benim projeler üzerinde ve eğer ben optimize ederseniz daha hızlı kod üretmek için bir eğilim var, 2009 yılında bu farkboyutu(-Os
) yerine hızı (-O2
-O3
) ve şimdiye kadar neden beri merak edilmiştir.
Bu şaşırtıcı davranış gösterir (çok) saçma bir kod oluşturmak için başarmış ve yeterince küçük buraya asılacak.
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;
}
I -Os
, ile derleme 0.38 s Bu programı çalıştırmak için gereken, ve 0.44 -O2
-O3
ile derlenen. Bu kez neredeyse hiç gürültü (4.7.2, gcc x86_64 GNU/Linux, Intel Core ı5-3320M) ile sürekli ve elde edilir.
(Güncelleme: tüm montaj kodları github taşındı: post şişkinlik yaptı fno-align-*
bayraklar aynı etkiye sahip olarak. bu soru için çok az değer ekleyin) anlaşılan
-Os
-O2
ile oluşturulan derleme.
Ne yazık ki, benim anlayışıma göre montajı çok sınırlı, bu yüzden hiçbir fikrim yok olsun bakalım sonra ne yaptım oldu Doğru: aldım montaj için -O2
ve birleştirilmiş tüm farklılıkları içine montaj için -Os
hariç.p2align
satır here sonuç. Bu kod hala 0.38 s olarak geçertek fark bu.p2align
şeyler.
Eğer doğru tahmin edersem, bu hizalama yığını için doldurma. Why does GCC pad functions with NOPs? göre kodu daha hızlı çalışır umuduyla yapılır ama görünüşe göre bu iyileştirme benim durumumda geri tepti.
Bu durumda suçlu doldurma mı? Neden ve nasıl?
Çıkardığı sesi oldukça fazla mikro-optimizasyon zamanlama imkansız kılıyor.
Nasıl böyle bir kaza sonucu şanslı / şanssız doğrultma işlemi yaptığımda engel olmadığından emin olun C üzerinde mikro-iyileştirme (hizalama yığın ilgisiz) veya C kaynak kodlarını alabilir miyim?
GÜNCELLEME:
Pascal Cuoq's answer aşağıdaki dizilimi ile biraz tinkered. -O2 -fno-align-functions -fno-align-loops
gcc geçerek, .p2align
tüm montaj gitti ve oluşturulan yürütülebilir 0.38 s çalışır. gcc doc göre:
-Os-O2 tüm iyileştirmeleri [ama] -Os, aşağıdaki optimizasyon bayrakları devre dışı bırakır sağlar:
-falign-functions -falign-jumps -falign-loops -falign-labels -freorder-blocks -freorder-blocks-and-partition -fprefetch-loop-arrays
Yani, oldukça fazla (yanlış)uyum sorunu gibi görünüyor.
Hala Marat Dukhan's answer önerilen -march=native
hakkında kuşkuluyum. Sadece (yanlış)bu uyum sorunu mani olmuyor ben ikna olmuş değilim; kesinlikle benim makine üzerinde hiçbir etkisi yoktur. (Yine de, onun cevabını ben upvoted.)
GÜNCELLEME 2:
Resmin dışında -Os
alabiliriz.Aşağıdaki zamanları ile derleme ile elde edilir
*24 0.37* ler
*25 0.37* ler
-S -O2
work()
0.37 s sonra manuel olarakadd()
Meclisi hareketli*29 0.44* ler
Arama sitesinden add()
mesafe çok önemli gibi görünüyor. perf
denedim ama perf stat
perf report
çıkış bana pek mantıklı gelmiyor. Ancak, tek bir tutarlı sonuç alabilirim:
-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),�x
│ }
│ ← retq
[...]
│ int z = add(x, y);
1.93 │ → callq add(int const&, int const&) [clone .isra.0]
│ sum = z;
79.79 │ add �x,�x
fno-align-*
için:
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),�x
│ }
[...]
│ __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),�i
│ 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 �x,�x
│ }
-fno-omit-frame-pointer
için:
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),�x
│ 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 �x,�x
Yavaş durumda add()
çağrı oyalamak gibi görünüyor.
İnceledimher şeyiperf -e
benim makinede tükürmek olabilir; sadece yukarıda belirtilen istatistikte.
Aynı yürütülebilir için, stalled-cycles-frontend
yürütme süresi ile doğrusal korelasyon gösterir; çok net bir şekilde bağ başka bir şey fark etmedi. (Farklı yürütülebilir dosyalar için stalled-cycles-frontend
karşılaştırılması bana pek mantıklı gelmiyor.)
İlk Yorum gibi geldi önbellek özlüyor ben dahil. Önbellek performans, yalnızca olanları yukarıda verilen benim makinede ölçülebilen özlüyor hepsini inceledim. Önbellek çok çok gürültülü ve yürütme kez bir ilişki için küçük Haritayı özlüyor.
CEVAP
Varsayılan Derleyiciler "" işlemci. ortalama için optimize ederek Farklı işlemciler farklı talimat dizileri iyilik beri, derleyici en iyi duruma getirme -O2
etkin ortalama işlemci yarar, ama özellikle işlemci performansı (ve aynı 50 ** için geçerlidir) düşebilir. Eğer farklı işlemciler üzerinde aynı örnek çalışırsanız, bazıları o diğer -Os
iyileştirmeler için daha uygun iken -O2
fayda bulacaksınız.
Burada çeşitli işlemciler üzerinde time ./test 0 0
sonuçları (kullanıcı zaman rapor):
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
Bazı durumlarda gcc
belirli bir işlemci (seçenek -mtune=native
ya -march=native
kullanarak) için optimize sorarak dezavantajlı optimizasyon etkisini azaltabilir:
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
Güncelleme: Ivy Bridge tabanlı Core i3 üç sürümleri gcc
(4.6.4
, 4.7.3
, ve 4.8.1
) üretmek ikili ile önemli ölçüde farklı performans, ama derleme kod var sadece ince farklılıklar. Şimdiye kadar, bu gerçek, hiçbir açıklaması var.
gcc-4.6.4 -Os
(0.709 saniye içinde çalıştırır) montaj:
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
gcc-4.7.3 -Os
Meclisi (0.822 saniye içinde yürütür):
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
gcc-4.8.1 -Os
(0.994 saniye içinde çalıştırır) montaj:
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
Neden eğer sözlük anahtarı varsa kontr...
Neden't varsa PyPy 6.3 kat daha h...
Bu "yeterli" rasgele algorit...
Eğer kayıtları cayır cayır yanan hızlı...
'in daha hızlı ve eğer bir dizi a...