2014-05-07 10 views
5

Ho due schemi di codice che iterano su vettori di dimensione 500. Uno dei disegni contiene array di 64 bit e il secondo progetto utilizza array contenenti numeri interi a 32 bit. Mi aspettavo che il design a 32 bit fosse più veloce perché nella cache sono disponibili più dati utili.Vettore di doppio bit più veloce a 64 bit rispetto a un vettore di int non firmato a 32 bit?

Compilatore MSVC, CPU Ivy Bridge, modalità di compilazione a 64 bit.

Questo codice 1, utilizzando gli interi a 32-bit (funzionamenti in cicli CPU):

#include <vector> 
#include <iostream> 

int main(){ 

    std::vector<unsigned int> x1; 
    std::vector<unsigned int> x2; 
    std::vector<unsigned int> x3; 
    x1.resize(500); 
    x2.resize(500); 
    x3.resize(500); 

    for(int i =0; i<500; i++){ 
     x1[i] = i; 
     x2[i] = 2*i; 
     x3[i] = 4*i; 
    } 


    int counter = 0; 
    while(counter < 1000){ 
     unsigned long long start = 0; 
     unsigned long long end = 0; 

     double m = 0; 
     double n = 0; 

     start = __rdtsc(); 

     for(int i=0; i < 500; i++){ 
      unsigned int a = x1[i]; 
      unsigned int b = x2[i]; 
      unsigned int g = x3[i]; 
      m = m + (a * g); 
      n = n + (b * g); 
     } 

     end = __rdtscp(); 

     std::cout << (end-start) << "\t\t"<<m << n << std::endl; 
     counter++; 
    } 
} 

producono questo asm (-Os):

start = __rdtscp(&p); 
rdtscp 
lea   r8,[rbp+6Fh] 
mov   dword ptr [r8],ecx 
shl   rdx,20h 
or   rax,rdx 
mov   r10,rax 
     unsigned int p; 
     unsigned int q; 
     unsigned long long start = 0; 
     unsigned long long end = 0; 

     double m = 0; 
mov   r8,rbx 
mov   r9d,1F4h 
      unsigned int a = x1[i]; 
      unsigned int b = x2[i]; 
      unsigned int g = x3[i]; 
mov   edx,dword ptr [r8+r15] 
      m = m + (a * g); 
mov   ecx,edx 
imul  ecx,dword ptr [r8+r14] 
xorps  xmm0,xmm0 
cvtsi2sd xmm0,rcx 
addsd  xmm7,xmm0 
      n = n + (b * g); 
imul  edx,dword ptr [r8] 
mov   eax,edx 
xorps  xmm0,xmm0 
cvtsi2sd xmm0,rax 
addsd  xmm8,xmm0 

     for(int i=0; i < 500; i++){ 
add   r8,4 
dec   r9 
jne   main+0E5h (013F681261h) 
     } 

     end = __rdtscp(&q); 
rdtscp 
     } 

     end = __rdtscp(&q); 
lea   r8,[rbp+6Fh] 
mov   dword ptr [r8],ecx 
shl   rdx,20h 
or   rdx,rax 

Questo è un codice 2, utilizzando il doppio a 64 bit (il codice viene eseguito nei cicli CPU):

#include <vector> 
#include <iostream> 

int main(){ 

    std::vector<double> x1; 
    std::vector<double> x2; 
    std::vector<unsigned long long> x3; 
    x1.resize(500); 
    x2.resize(500); 
    x3.resize(500); 

    for(int i =0; i<500; i++){ 
     x1[i] = i; 
     x2[i] = 2*i; 
     x3[i] = 4*i; 
    } 

    int counter = 0; 
    while(counter < 1000){ 
     unsigned int p; 
     unsigned int q; 
     unsigned long long start = 0; 
     unsigned long long end = 0; 

     double m = 0; 
     double n = 0; 

     start = __rdtscp(&p); 

     for(int i=0; i < 500; i++){ 
      double a = x1[i]; 
      double b = x2[i]; 
      unsigned long long g = x3[i]; 
      m = m + (a * g); 
      n = n + (b * g); 
     } 

     end = __rdtscp(&q); 

     std::cout << (end-start) << "\t\t"<<m << n << std::endl; 
     counter++; 
    } 
} 

ed ecco ASM (-Os) prodotto:

start = __rdtscp(&p); 
rdtscp 
lea   r8,[rbp+6Fh] 
mov   dword ptr [r8],ecx 
shl   rdx,20h 
or   rax,rdx 
mov   r9,rax 
     unsigned int p; 
     unsigned int q; 
     unsigned long long start = 0; 
     unsigned long long end = 0; 

     double m = 0; 
mov   rdx,rbx 
mov   r8d,1F4h 
      double a = x1[i]; 
      double b = x2[i]; 
      unsigned long long g = x3[i]; 
mov   rcx,qword ptr [rdx+r15] 
xorps  xmm1,xmm1 
      m = m + (a * g); 
cvtsi2sd xmm1,rcx 
test  rcx,rcx 
jns   main+120h (013F32129Ch) 
addsd  xmm1,xmm9 
movaps  xmm0,xmm1 
mulsd  xmm0,mmword ptr [rdx+r14] 
addsd  xmm6,xmm0 
      n = n + (b * g); 
mulsd  xmm1,mmword ptr [rdx] 
addsd  xmm7,xmm1 

     for(int i=0; i < 500; i++){ 
add   rdx,8 
dec   r8 
jne   main+10Ah (013F321286h) 
     } 

     end = __rdtscp(&q); 
rdtscp 
     } 

     end = __rdtscp(&q); 
lea   r8,[rbp+6Fh] 
mov   dword ptr [r8],ecx 
shl   rdx,20h 
or   rdx,rax 
+0

È una CPU a 64 bit, giusto? –

+1

Le operazioni in virgola mobile possono essere eseguite in parallelo con le altre istruzioni della CPU, in modo da poter tenere conto della discrepanza. Sebbene lo smontaggio mostri entrambi usando i registri xmm quindi ora sono confuso. –

+0

Una domanda simile da un altro forum con alcuni confronti. [floating point vs integer] (http://stackoverflow.com/questions/2550281/floating-point-vs-integer-calculations-on-modern-hardware). Da quanto ho capito, le procedure matematiche di precisione estesa come APFLOAT eseguono le loro operazioni utilizzando il punto mobile, ma non so se vengono utilizzate le istruzioni del tipo SSE. – rcgldr

risposta

6

La differenza è la conversione di interi a doppi in primo codice (i vettori contengono unsigned int, il prodotto è in aritmetica intera, ma gli usi di accumulo double, in assembler questo aggiunge l'istruzione cvtsi2sd al codice).

Nel secondo codice, si usano i doppi ovunque, quindi non si ha una conversione e il codice viene eseguito più velocemente.

Questa differenza sarebbe stata molto più pronunciata su una CPU che ha una distinzione più rigorosa tra le unità di elaborazione a virgola mobile e fissa (la piattaforma POWER ne è un esempio). La piattaforma X86 è molto permissiva in questo senso.

Problemi correlati