SORU
27 Mart 2013, ÇARŞAMBA


C : bir kitapta bir işlenen tutmak Gizemli büyük hızlanma

Ben ulaşmaya çalışıyorlardı bir fikir etkisi olan bir dizi L1 önbelleği karşı hafıza tarafından bir zamanlama rutin ölçekler ve toplamları öğeleri bir dizi kullanarak aşağıdaki kod (farkındayım ben sadece ölçek olarak 'a' sonunda; asıl yapmak hem bir çarpma ve bir ekleme döngü içinde, şimdiye kadar, derleyici olmadı anladım faktörü 'a'):

double sum(double a,double* X,int size)
{
    double total = 0.0;
    for(int i = 0;  i < size;   i)
    {
        total  = a*X[i];
    }
    return total;
}

#define KB 1024
int main()
{
    //Approximately half the L1 cache size of my machine
    int operand_size = (32*KB)/(sizeof(double)*2);
    printf("Operand size: %d\n", operand_size);
    double* X = new double[operand_size];
    fill(X,operand_size);

    double seconds = timer();
    double result;
    int n_iterations = 100000;
    for(int i = 0; i < n_iterations;   i)
    {
        result = sum(3.5,X,operand_size);
        //result  = rand();  
    }
    seconds = timer() - seconds; 

    double mflops = 2e-6*double(n_iterations*operand_size)/seconds;
    printf("Vector size %d: mflops=%.1f, result=%.1f\n",operand_size,mflops,result);
    return 0;
}

Zamanlayıcı not() ve dolgu() yordamları için kısaltma dahil değildir; tam kaynak kodu çalıştırmak istiyorsanız, burada bulunabilir:

http://codepad.org/agPWItZS

Şimdi, burada ilginç bir hal alıyor. Bu çıktı

Operand size: 2048
Vector size 2048: mflops=588.8, result=-67.8

Bu tamamen un-önbellek performans, X Tüm elemanları döngü yineleme arasında önbelleğinde yapılması gerektiğini rağmen. Derleme kod tarafından oluşturulan bakarak:

g   -O3 -S -fno-asynchronous-unwind-tables register_opt_example.cpp

Sum fonksiyonu döngü içinde bir gariplik fark ettim:

L55:
    movsd   (%r12,%rax,8), %xmm0
    mulsd   %xmm1, %xmm0
    addsd   -72(%rbp), %xmm0
    movsd   %xmm0, -72(%rbp)
    incq    %rax
    cmpq    $2048, %rax
    jne L55

Talimatlar:

    addsd   -72(%rbp), %xmm0
    movsd   %xmm0, -72(%rbp)

değeri saklamak olduğunu belirtmek "" sum() yığın, ve okuma ve her döngü tekrarında de yazma. toplam Bu işlenen bir kayıt bir şekilde saklanması için Meclis değiştirdim

...
addsd   %xmm0, %xmm3
...

Bu küçük bir değişiklik yaratırbüyükyüksek performans:

Operand size: 2048
Vector size 2048: mflops=1958.9, result=-67.8

tl;dr Benim soru şudur: tek bir bellek konumu bir kayıt ile erişim yerine geliyor neden, kod, çok, tek konumu L1 önbellek saklanmalıdır verilen hızlandırmak? Ne mimari faktörler bunu mümkün kılmak? Bir yerde yığın defalarca yazı tamamen bir önbellek etkinliğini yok edecek çok garip görünüyor.

Ek

Gcc benim sürüm değil

Target: i686-apple-darwin10
Configured with: /var/tmp/gcc/gcc-5646.1~2/src/configure --disable-checking --enable-werror --prefix=/usr --mandir=/share/man --enable-languages=c,objc,c  ,obj-c   --program-transform-name=/^[cg][^.-]*$/s/$/-4.2/ --with-slibdir=/usr/lib --build=i686-apple-darwin10 --with-gxx-include-dir=/include/c  /4.2.1 --program-prefix=i686-apple-darwin10- --host=x86_64-apple-darwin10 --target=i686-apple-darwin10
Thread model: posix
gcc version 4.2.1 (Apple Inc. build 5646) (dot 1)

Benim CPU

Intel X5650 Getirdiğini Belirtti

CEVAP
27 Mart 2013, ÇARŞAMBA


Büyük olasılıkla daha uzun bir bağımlılık zinciri, Yük Misprediction* ile birlikte bir kombinasyon.


Artık Bağımlılık Zinciri:

İlk ve en önemli bağımlılık yollarını belirliyoruz. Talimat baktığımız sağladığı o zaman gecikmeleri: http://www.agner.org/optimize/instruction_tables.pdf (sayfa 117)

Unoptimized sürümünde, kritik bağımlılık yoludur:

  • addsd -72(%rbp), %xmm0
  • movsd %xmm0, -72(%rbp)

DAHİLİ olarak, muhtemelen kırılır:

  • yük (2 döngü)
  • addsd (3 dönemdir)
  • mağaza (3 dönemdir)

Eğer optimize edilmiş sürümü bakacak olursak, sadece bir şey

  • addsd (3 dönemdir)

Vs 3 kür 8 kür var. 3 kat neredeyse.

Ne kadar hassas Çekirdek işlemci mağazası-yük bağımlılıkları ve forwarding bunu nasıl emin değilim. Ama makul bir sıfır değil, bu yüzden.


Yük deposu Misprediction:

Modern işlemciler hayal edebileceğinizden fazla şekilde tahmin için kullanın. Bunların en ünlüsü muhtemelen Branch Prediction. Daha az bilinen olanlar bir Yük Tahmin.

İşlemci yükü görünce, hemen tüm bekleyen yazar bitirmeden bu yükü. Bu yüklenen değerleri ile çakışan yazar üstlenecek.

Bir önceki döner çatışma için bir yük ile yazma, yük yeniden yargılanmalı ve hesaplama geri yükleme noktasına yuvarlandı. (çok şube mispredictions aynı şekilde geri)

Ne kadar alakalı işte burada:

Söylemeye gerek yok, modern işlemciler bu döngü birden çok yineleme aynı anda yürütmek mümkün olacak. İşlemci yükü gerçekleştirmek için (addsd -72(%rbp), %xmm0) mağaza (movsd %xmm0, -72(%rbp)) önceki yineleme gelen tamamlanmadan önce. çalışıyor olacak, bu yüzden

Sonuç? Yük ile önceki deposu çatışmalar - böylece bir misprediction ve geri.

*Adı emin değilim unutmayın "Yük Tahmini". Ben sadece Intel docs bu konuda okumak ve ona bir isim vermek görünmüyorlardı.

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • Adam Outler

    Adam Outler

    19 EKİM 2006
  • AverageBroTV

    AverageBroTV

    20 Mart 2013
  • Jordie Jordan

    Jordie Jorda

    27 Ocak 2008