2013-06-12 17 views
6

Ek sınır kontrolünün döngülerde ne kadar fark yarattığını görmek için bazı testler yapıyorum. Bu, dizilere eriştiğinizde C#, Java vb. Gibi diller tarafından eklenen örtülü sınırların kontrolünün maliyetini düşünerek sorulur.Ekstra kontrol döngüsünün eklenmesi neden bazı makinelerde büyük fark yaratıyor ve diğerlerinde küçük fark yaratıyor?

Güncelleme: Aynı çalıştırılabilir programı birkaç tane daha bilgisayar üzerinde denedim, bu da neler olup bittiğine daha fazla ışık tutuyor. İlk önce orjinal bilgisayarı ve ikinci dizüstü bilgisayarımı listeledim. Modern dizüstü bilgisayarımda, döngüde ek kontroller eklemek, orijinal donanım için% 3 ile% 30 arasındayken, yalnızca% 1 ile% 4 arasında bir zaman harcar. Test programında

Processor x86 Family 6 Model 30 Stepping 5 GenuineIntel ~2793 Mhz 
Ratio 2 checks : 1 check = 1.0310 
Ratio 3 checks : 1 check = 1.2769 

Processor Intel(R) Core(TM) i7-3610QM CPU @ 2.30GHz, 2301 Mhz, 4 Core(s), 8 Logical Processor(s) 
Ratio 2 checks : 1 check = 1.0090 
Ratio 3 checks : 1 check = 1.0393 

Processor Intel(R) Core(TM) i5-2500 CPU @ 3.30GHz, 4 Cores(s) 
Ratio 2 checks : 1 check = 1.0035 
Ratio 3 checks : 1 check = 1.0639 

Processor Intel(R) Core(TM)2 Duo CPU  T9300 @ 2.50GHz, 2501 Mhz, 2 Core(s), 2 Logical Processor(s) 
Ratio 2 checks : 1 check = 1.1195 
Ratio 3 checks : 1 check = 1.3597 

Processor x86 Family 15 Model 43 Stepping 1 AuthenticAMD ~2010 Mhz 
Ratio 2 checks : 1 check = 1.0776 
Ratio 3 checks : 1 check = 1.1451 

, aşağıda birinci fonksiyon kontrol tek bağlı, ikinci fonksiyon iki kontrol eder ve üçüncü kontrol (arama kodu, n1=n2=n3) üç. Ben iki çek oranını buldum: bir yaklaşık 1.03 idi ve üç çek: yaklaşık 1.3 idi. Performansa bir fark daha ekleyerek bir kontrol daha ekleyerek şaşırdım. Orijinal soruma bir aldım, ki burada gözlenen farklılıklar hakkında biraz ışık tutabilir.

Programın tüm program optimizasyonu açık olmadan derlenmesinin önemli olduğunu unutmayın; Aksi takdirde derleyici ek sınır kontrolünü kaldırabilir.

// dotprod.cpp 
#include "dotprod.h" 

double SumProduct(const double* v1, const double* v2, int n) 
{ 
    double sum=0; 
    for(int i=0; 
     i<n; 
     ++i) 
     sum += v1[i]*v2[i]; 
    return sum; 
} 

double SumProduct(const double* v1, const double* v2, int n1, int n2) 
{ 
    double sum=0; 
    for(int i=0; 
     i<n1 && i <n2; 
     ++i) 
     sum += v1[i]*v2[i]; 
    return sum; 
} 

double SumProduct(const double* v1, const double* v2, int n1, int n2, int n3) 
{ 
    double sum=0; 
    for(int i=0; 
     i<n1 && i <n2 && i <n3; 
     ++i) 
     sum += v1[i]*v2[i]; 
    return sum; 
} 

Bu kod başlangıçta Visual Studio 2010 kullanılarak inşa edilmiş, Yayın, Win32 (ben hız farkının arkasındaki mantık C++ spesifik olacaktır olmadığından 'C' etiketini ekledikten ve olmayabilir Windows belirli). Bunu açıklayabilir mi? Bilgi için aşağıdaki kodun geri kalanı,

. Bunun içinde bazı C++ özellikli şeyler var.

Başlık dosyası

// dotprod.h 
double SumProduct(const double*, const double*, int n); 
double SumProduct(const double*, const double*, int n1, int n2); 
double SumProduct(const double*, const double*, int n1, int n2, int n3); 

, aşağıdaki asm dosyasını üreten, (tüm program optimizasyonu kapatarak, davayı 2) this answer içinde tavsiye aldı takımını üretmek amacıyla

// main.cpp 

#include <stdio.h> 
#include <math.h> 
#include <numeric> 
#include <vector> 

#include <windows.h> 

#include "../dotprod/dotprod.h" // separate lib 

typedef __int64 timecount_t; 
inline timecount_t GetTimeCount() 
{ 
    LARGE_INTEGER li; 
    if (!QueryPerformanceCounter(&li)) { 
     exit(1); 
    } 
    return li.QuadPart; 
} 

int main() 
{ 
    typedef std::vector<double> dvec; 
    const int N = 100 * 1000; 

    // Initialize 
    dvec v1(N); 
    dvec v2(N); 
    dvec dp1(N); 
    dvec dp2(N); 
    dvec dp3(N); 
    for(int i=0; i<N; ++i) { 
     v1[i] = i; 
     v2[i] = log(static_cast<double>(i+1)); 
    } 

    const timecount_t t0 = GetTimeCount(); 

    // Check cost with one bound 
    for(int n=0; n<N; ++n) { 
     dp1[n] = SumProduct(&(v1[0]),&(v2[0]),n); 
    } 

    const timecount_t t1 = GetTimeCount(); 

    // Check cost with two bounds 
    for(int n=0; n<N; ++n) { 
     dp2[n] = SumProduct(&(v1[0]),&(v2[0]),n,n); 
    } 

    const timecount_t t2 = GetTimeCount(); 

    // Check cost with three bounds 
    for(int n=0; n<N; ++n) { 
     dp3[n] = SumProduct(&(v1[0]),&(v2[0]),n,n,n); 
    } 
    const timecount_t t3 = GetTimeCount(); 

    // Check results 
    const double sumSumProducts1 = std::accumulate(dp1.begin(), dp1.end(), 0.0); 
    const double sumSumProducts2 = std::accumulate(dp2.begin(), dp2.end(), 0.0); 
    const double sumSumProducts3 = std::accumulate(dp3.begin(), dp3.end(), 0.0); 
    printf("Sums of dot products: %.1f, %.1f, %.1f\n", sumSumProducts1, sumSumProducts2, sumSumProducts3); 

    // Output timings 
    const timecount_t elapsed1 = t1-t0; 
    const timecount_t elapsed2 = t2-t1; 
    const timecount_t elapsed3 = t3-t2; 
    printf("Elapsed: %.0f, %.0f, %.0f\n", 
     static_cast<double>(elapsed1), 
     static_cast<double>(elapsed2), 
     static_cast<double>(elapsed3)); 
    const double ratio2to1 = elapsed2/static_cast<double>(elapsed1); 
    const double ratio3to1 = elapsed3/static_cast<double>(elapsed1); 
    printf("Ratio 2:1=%.2f\n", ratio2to1); 
    printf("Ratio 3:1=%.2f\n", ratio3to1); 

    return 0; 
} 

Testi koşum . CPU'lar arasında

; Listing generated by Microsoft (R) Optimizing Compiler Version 16.00.40219.01 

    TITLE C:\dev\TestSpeed\dotprod\dotprod.cpp 
    .686P 
    .XMM 
    include listing.inc 
    .model flat 

INCLUDELIB OLDNAMES 

PUBLIC [email protected] 
PUBLIC [email protected]@[email protected]   ; SumProduct 
EXTRN __fltused:DWORD 
; COMDAT [email protected] 
; File c:\dev\testspeed\dotprod\dotprod.cpp 
CONST SEGMENT 
[email protected] DQ 00000000000000000r ; 0 
; Function compile flags: /Ogtp 
CONST ENDS 
; COMDAT [email protected]@[email protected] 
_TEXT SEGMENT 
tv491 = -4      ; size = 4 
_v1$ = 8      ; size = 4 
_v2$ = 12      ; size = 4 
_n1$ = 16      ; size = 4 
_n2$ = 20      ; size = 4 
_n3$ = 24      ; size = 4 
[email protected]@[email protected] PROC    ; SumProduct, COMDAT 

; 25 : { 

    push ebp 
    mov ebp, esp 
    push ecx 

; 26 :  double sum=0; 

    fldz 
    push ebx 
    mov ebx, DWORD PTR _v2$[ebp] 
    push esi 
    push edi 
    mov edi, DWORD PTR _n1$[ebp] 

; 27 :  for(int i=0; 

    xor ecx, ecx 

; 28 :   i<n1 && i <n2 && i <n3; 
; 29 :   ++i) 

    cmp edi, 4 
    jl [email protected] 

; 26 :  double sum=0; 

    mov edi, DWORD PTR _v1$[ebp] 
    lea esi, DWORD PTR [edi+24] 

; 30 :   sum += v1[i]*v2[i]; 

    sub edi, ebx 
    lea edx, DWORD PTR [ecx+2] 
    lea eax, DWORD PTR [ebx+8] 
    mov DWORD PTR tv491[ebp], edi 
[email protected]: 

; 28 :   i<n1 && i <n2 && i <n3; 
; 29 :   ++i) 

    mov ebx, DWORD PTR _n2$[ebp] 
    cmp ecx, ebx 
    jge [email protected] 
    cmp ecx, DWORD PTR _n3$[ebp] 
    jge [email protected] 

; 30 :   sum += v1[i]*v2[i]; 

    fld QWORD PTR [eax-8] 
    lea edi, DWORD PTR [edx-1] 
    fmul QWORD PTR [esi-24] 
    faddp ST(1), ST(0) 
    cmp edi, ebx 
    jge SHORT [email protected] 

; 28 :   i<n1 && i <n2 && i <n3; 
; 29 :   ++i) 

    cmp edi, DWORD PTR _n3$[ebp] 
    jge SHORT [email protected] 

; 30 :   sum += v1[i]*v2[i]; 

    mov edi, DWORD PTR tv491[ebp] 
    fld QWORD PTR [edi+eax] 
    fmul QWORD PTR [eax] 
    faddp ST(1), ST(0) 
    cmp edx, ebx 
    jge SHORT [email protected] 

; 28 :   i<n1 && i <n2 && i <n3; 
; 29 :   ++i) 

    cmp edx, DWORD PTR _n3$[ebp] 
    jge SHORT [email protected] 

; 30 :   sum += v1[i]*v2[i]; 

    fld QWORD PTR [eax+8] 
    lea edi, DWORD PTR [edx+1] 
    fmul QWORD PTR [esi-8] 
    faddp ST(1), ST(0) 
    cmp edi, ebx 
    jge SHORT [email protected] 

; 28 :   i<n1 && i <n2 && i <n3; 
; 29 :   ++i) 

    cmp edi, DWORD PTR _n3$[ebp] 
    jge SHORT [email protected] 

; 30 :   sum += v1[i]*v2[i]; 

    fld QWORD PTR [eax+16] 
    mov edi, DWORD PTR _n1$[ebp] 
    fmul QWORD PTR [esi] 
    add ecx, 4 
    lea ebx, DWORD PTR [edi-3] 
    add eax, 32     ; 00000020H 
    add esi, 32     ; 00000020H 
    faddp ST(1), ST(0) 
    add edx, 4 
    cmp ecx, ebx 
    jl SHORT [email protected] 
    mov ebx, DWORD PTR _v2$[ebp] 
[email protected]: 

; 28 :   i<n1 && i <n2 && i <n3; 
; 29 :   ++i) 

    cmp ecx, edi 
    jge SHORT [email protected] 
    mov edx, DWORD PTR _v1$[ebp] 
    lea eax, DWORD PTR [ebx+ecx*8] 
    sub edx, ebx 
[email protected]: 
    cmp ecx, DWORD PTR _n2$[ebp] 
    jge SHORT [email protected] 
    cmp ecx, DWORD PTR _n3$[ebp] 
    jge SHORT [email protected] 

; 30 :   sum += v1[i]*v2[i]; 

    fld QWORD PTR [eax+edx] 
    inc ecx 
    fmul QWORD PTR [eax] 
    add eax, 8 
    faddp ST(1), ST(0) 
    cmp ecx, edi 
    jl SHORT [email protected] 
[email protected]: 

; 31 :  return sum; 
; 32 : } 

    pop edi 
    pop esi 
    pop ebx 
    mov esp, ebp 
    pop ebp 
    ret 0 
[email protected]@[email protected] ENDP    ; SumProduct 
_TEXT ENDS 
PUBLIC [email protected]@[email protected]   ; SumProduct 
; Function compile flags: /Ogtp 
; COMDAT [email protected]@[email protected] 
_TEXT SEGMENT 
tv448 = -4      ; size = 4 
_v1$ = 8      ; size = 4 
_v2$ = 12      ; size = 4 
_n1$ = 16      ; size = 4 
_n2$ = 20      ; size = 4 
[email protected]@[email protected] PROC    ; SumProduct, COMDAT 

; 15 : { 

    push ebp 
    mov ebp, esp 
    push ecx 

; 16 :  double sum=0; 

    fldz 
    push ebx 
    mov ebx, DWORD PTR _v2$[ebp] 
    push esi 
    push edi 
    mov edi, DWORD PTR _n1$[ebp] 

; 17 :  for(int i=0; 

    xor ecx, ecx 

; 18 :   i<n1 && i <n2; 
; 19 :   ++i) 

    cmp edi, 4 
    jl SHORT [email protected]@2 

; 16 :  double sum=0; 

    mov edi, DWORD PTR _v1$[ebp] 
    lea edx, DWORD PTR [edi+24] 

; 20 :   sum += v1[i]*v2[i]; 

    sub edi, ebx 
    lea esi, DWORD PTR [ecx+2] 
    lea eax, DWORD PTR [ebx+8] 
    mov DWORD PTR tv448[ebp], edi 
[email protected]@2: 
    mov edi, DWORD PTR _n2$[ebp] 
    cmp ecx, edi 
    jge SHORT [email protected]@2 
    fld QWORD PTR [eax-8] 
    lea ebx, DWORD PTR [esi-1] 
    fmul QWORD PTR [edx-24] 
    faddp ST(1), ST(0) 
    cmp ebx, edi 
    jge SHORT [email protected]@2 
    mov ebx, DWORD PTR tv448[ebp] 
    fld QWORD PTR [ebx+eax] 
    fmul QWORD PTR [eax] 
    faddp ST(1), ST(0) 
    cmp esi, edi 
    jge SHORT [email protected]@2 
    fld QWORD PTR [eax+8] 
    lea ebx, DWORD PTR [esi+1] 
    fmul QWORD PTR [edx-8] 
    faddp ST(1), ST(0) 
    cmp ebx, edi 
    jge SHORT [email protected]@2 
    fld QWORD PTR [eax+16] 
    mov edi, DWORD PTR _n1$[ebp] 
    fmul QWORD PTR [edx] 
    add ecx, 4 
    lea ebx, DWORD PTR [edi-3] 
    add eax, 32     ; 00000020H 
    add edx, 32     ; 00000020H 
    faddp ST(1), ST(0) 
    add esi, 4 
    cmp ecx, ebx 
    jl SHORT [email protected]@2 
    mov ebx, DWORD PTR _v2$[ebp] 
[email protected]@2: 

; 18 :   i<n1 && i <n2; 
; 19 :   ++i) 

    cmp ecx, edi 
    jge SHORT [email protected]@2 
    mov edx, DWORD PTR _v1$[ebp] 
    lea eax, DWORD PTR [ebx+ecx*8] 
    sub edx, ebx 
[email protected]@2: 
    cmp ecx, DWORD PTR _n2$[ebp] 
    jge SHORT [email protected]@2 

; 20 :   sum += v1[i]*v2[i]; 

    fld QWORD PTR [eax+edx] 
    inc ecx 
    fmul QWORD PTR [eax] 
    add eax, 8 
    faddp ST(1), ST(0) 
    cmp ecx, edi 
    jl SHORT [email protected]@2 
[email protected]@2: 

; 21 :  return sum; 
; 22 : } 

    pop edi 
    pop esi 
    pop ebx 
    mov esp, ebp 
    pop ebp 
    ret 0 
[email protected]@[email protected] ENDP    ; SumProduct 
_TEXT ENDS 
PUBLIC [email protected]@[email protected]    ; SumProduct 
; Function compile flags: /Ogtp 
; COMDAT [email protected]@[email protected] 
_TEXT SEGMENT 
_v1$ = 8      ; size = 4 
_v2$ = 12      ; size = 4 
[email protected]@[email protected] PROC    ; SumProduct, COMDAT 
; _n$ = eax 

; 5 : { 

    push ebp 
    mov ebp, esp 
    mov edx, DWORD PTR _v2$[ebp] 

; 6 :  double sum=0; 

    fldz 
    push ebx 
    push esi 
    mov esi, eax 

; 7 :  for(int i=0; 

    xor ebx, ebx 
    push edi 
    mov edi, DWORD PTR _v1$[ebp] 

; 8 :   i<n; 
; 9 :   ++i) 

    cmp esi, 4 
    jl SHORT [email protected]@3 

; 6 :  double sum=0; 

    lea eax, DWORD PTR [edx+8] 
    lea ecx, DWORD PTR [edi+24] 

; 10 :   sum += v1[i]*v2[i]; 

    sub edi, edx 
    lea edx, DWORD PTR [esi-4] 
    shr edx, 2 
    inc edx 
    lea ebx, DWORD PTR [edx*4] 
[email protected]@3: 
    fld QWORD PTR [eax-8] 
    add eax, 32     ; 00000020H 
    fmul QWORD PTR [ecx-24] 
    add ecx, 32     ; 00000020H 
    dec edx 
    faddp ST(1), ST(0) 
    fld QWORD PTR [edi+eax-32] 
    fmul QWORD PTR [eax-32] 
    faddp ST(1), ST(0) 
    fld QWORD PTR [eax-24] 
    fmul QWORD PTR [ecx-40] 
    faddp ST(1), ST(0) 
    fld QWORD PTR [eax-16] 
    fmul QWORD PTR [ecx-32] 
    faddp ST(1), ST(0) 
    jne SHORT [email protected]@3 

; 6 :  double sum=0; 

    mov edx, DWORD PTR _v2$[ebp] 
    mov edi, DWORD PTR _v1$[ebp] 
[email protected]@3: 

; 8 :   i<n; 
; 9 :   ++i) 

    cmp ebx, esi 
    jge SHORT [email protected]@3 
    sub edi, edx 
    lea eax, DWORD PTR [edx+ebx*8] 
    sub esi, ebx 
[email protected]@3: 

; 10 :   sum += v1[i]*v2[i]; 

    fld QWORD PTR [eax+edi] 
    add eax, 8 
    dec esi 
    fmul QWORD PTR [eax-8] 
    faddp ST(1), ST(0) 
    jne SHORT [email protected]@3 
[email protected]@3: 

; 11 :  return sum; 
; 12 : } 

    pop edi 
    pop esi 
    pop ebx 
    pop ebp 
    ret 0 
[email protected]@[email protected] ENDP    ; SumProduct 
_TEXT ENDS 
END 
+4

'void main()' yasal değil C++. Int main() 'yi kullanmanızı öneririm. Hafızayı kendiniz yönetmeye çalışmak yerine 'std :: vector' kullanmayı da öneririm. – chris

+1

Testlerinizin sonucunu bir işlemci için optimize edebilen bir derleyicide görmek oldukça ilginç olabilir. 'Gcc -march = native' gibi. Bunu karşılaştırma şansın var mı? –

+4

GetTickCount kesin değil, QueryPerformanceCounter: http: // stackoverflow kullanın.com/questions/1739259/how-to-use-queryperformancecounter –

cevap

2

Bir büyük fark koşullu dalı ulaşıncaya kadar

işlemci paralel çeşitli talimatlarda yürütebileceği boru hattı optimizasyonu olduğunu. Bu noktadan, tüm talimatlar yerine getirilene kadar beklemek yerine CPU, durum mevcut olana ve değerlendirilmeye hazır olana kadar paralel olarak bir şube ile devam edebilir. Varsayım doğruysa, o zaman bir kazancımız olur. Aksi halde, CPU diğer şubeye gider.

Bir CPU'nun zor kısmı, en iyi varsayımları bulmak ve olabildiğince çok sayıda yönergeyi yürütmektir.

İlgili konular