2016-08-09 56 views
8

, ben ilginç bir yığın ayırma özelliğini fark, işte burada tedbir kez şablon toplam 500000 ints tahsis edilebilir:Yığın ayırma özelliği (performans) benim küçük performans sorunları soruşturması sırasında

:

  1. bir öbekte ayrılan

    void foo() 
    { 
        const int size = 500000; 
        int a1[size]; 
    
        x = a1[size - 1]; 
    } 
    

    Sonuç: 7.3 saniye;

    void foo() 
    { 
        const int size = 250000; 
        int a1[size]; 
        int a2[size]; 
    
        x = a1[size - 1] + a2[size - 1]; 
    } 
    

    Sonuç:

  2. iki parçalar halinde Ayrılan 3.5 saniye;

  3. dört parçalar Ayrılan:

    void foo() 
    { 
        const int size = 125000; 
        int a1[size]; 
        int a2[size]; 
        int a3[size]; 
        int a4[size]; 
    
        x = a1[size - 1] + a2[size - 1] + 
         a3[size - 1] + a4[size - 1]; 
    } 
    

    Sonucu: 1.8 saniye.

vb ... Ben16 parçalar halinde bölmek ve seferinde 0,38 saniye sonucu olsun.


Açıkla bana, lütfen, neden ve nasıl olur?
MSVC 2013 (v120), Release build'i kullandım.

UPD:
Makinem x64 platformudur. Ve ben Win32 platformu ile derledim.
x64 Platform ile derlediğimde, her durumda yaklaşık 40ms verir.
Platform seçimi neden bu kadar çok etkileniyor?

+0

Kör: Mümkünse derleyici optimizasyonu. Orada gördüğünüz perfomances kesinlikle ** Cache Misses ** tarafından sakatlandı ... Genellikle, benchmarklarınızı en yüksek optimizasyon seviyelerinde yapmalısınız. :-) – WhiZTiM

+0

Bilgisayarınızın özellikleri nedir? Derleyici sürümü ve Derleme bayrakları? – WhiZTiM

+0

@WhiZTiM, optimizasyondan kaçınmaya çalışıyorum :) _Compiler optimization_ ve _Cache Misses_ 'ı önlemek için geliştirmeyi önerebilir misiniz? – MrPisarik

cevap

8

VS2015 Güncelleştirmesi 3'ten çıkarıma bakıldığında, foo'un 2 ve 4 dizi sürümlerinde, derleyici kullanılmayan dizileri en iyi duruma getirir, böylece her işlevde yalnızca 1 dizi için yığın alanı ayırır. Sonraki işlevler daha küçük dizilere sahip olduğundan, bu daha az zaman alır. X için atama, 4 dizinin her ikisi için de aynı bellek konumunu okur. (Diziler başlatılmamış olduğundan, onlardan okuma, tanımlanmamış bir davranıştır.) Kodu optimize etmeden, okunan 2 veya 4 ayrı dizi vardır.

Bu işlevler için uzun süre, yığın taşması algılamasının bir parçası olarak __chkstk tarafından gerçekleştirilen yığın sondalarından kaynaklanır (derleyici, tüm yerel değişkenleri tutmak için 1 sayfadan fazla alana gereksinim duyduğunda gereklidir).

+0

Evet, işte bu! Ancak x64'te hedef platformu değiştirdiğimde hız neden bu kadar dramatik bir şekilde artıyor? Platformun x64'e değiştirilmesinin sayfa boyutunu iki kez artırmasını bekliyorum, yani 8K oldu. Bu yüzden hız artmalıdır, ancak her bir dizi bildirimi '= {0} 'eklerseniz (ve iki null'u n'den silmek için n'yi silerseniz) bir diğeri söz konusu olduğunda (önemli ölçüde artar), platformlar arasında hız artışı olmaz. (ama her 3 vaka bir seferde çalışır).Belki de geri kalanına nispeten küçük bir pay ayırma maliyeti yüzünden mi? – MrPisarik

1

Derleyicinin gerçekten kodla ne yaptığını görmek için sonuçta oluşan derleyici koduna bakmalısınız. Gcc/clang/icc için Matt Godbolt's Compiler Explorer'u kullanabilirsiniz.

clang çünkü UB her şeyi optimize eder ve sonuç (foo olduğunu - ilk sürümü, foo2 - İkinci versiyon:

foo:         # @foo 
     retq 

foo2:         # @foo2 
     retq 

icc davranır çok benzer iki sürüm:

foo: 
     pushq  %rbp           #4.1 
     movq  %rsp, %rbp         #4.1 
     subq  $2000000, %rsp        #4.1 
     movl  -4(%rbp), %eax        #8.9 
     movl  %eax, x(%rip)         #8.5 
     leave             #10.1 
     ret              #10.1 

foo2: 
     pushq  %rbp           #13.1 
     movq  %rsp, %rbp         #13.1 
     subq  $2000000, %rsp        #13.1 
     movl  -1000004(%rbp), %eax       #18.9 
     addl  -4(%rbp), %eax        #18.24 
     movl  %eax, x(%rip)         #18.5 
     leave             #19.1 
     ret 

ve gcc, farklı sürümler için farklı assembler kodu oluşturur. her şey sadece tahmin edilir Böylece

foo: 
     pushq %rbp 
     movq %rsp, %rbp 
     subq $2000016, %rsp 
     movl 1999996(%rsp), %eax 
     movl %eax, x(%rip) 
     leave 
     ret 
foo2: 
     pushq %rbp 
     movl $1000016, %edx #only the first array is allocated 
     movq %rsp, %rbp 
     subq %rdx, %rsp 
     leaq 3(%rsp), %rax 
     subq %rdx, %rsp 
     shrq $2, %rax 
     movl 999996(,%rax,4), %eax 
     addl 999996(%rsp), %eax 
     movl %eax, x(%rip) 
     leave 
     ret 

farkı anlamak için tek yol sizin derleyici tarafından üretilen montajcı kodu bakmaktır: n 6,1 denemelerinizin benzer davranışlar sergilemiştir kodu üretir.