2014-05-07 8 views
5

Ich habe zwei Designs Code über Vektoren der Größe 500 iterieren. Eine der Designs enthält Arrays von 64-Bit-Doppel und der zweite Entwurf verwendet Arrays mit 32-Bit-Ganzzahlen. Ich habe erwartet, dass das 32-Bit-Design schneller ist, weil mehr nützliche Daten in den Cache gepackt werden können.Vektor von 64-Bit doppelt schneller zu Skalarprodukt als ein Vektor von 32-Bit unsigned int?

Compiler MSVC, CPU Ivy Bridge, Kompilierung 64-Bit-Modus.

Dies ist Code 1, unter Verwendung der 32-Bit-Ganzzahlen (läuft in CPU-Zyklen):

#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++; 
    } 
} 

Herstellung dieser 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 

Dieser Code ist 2, mit dem 64-Bit-Doppel (Code läuft in CPU-Zyklen):

#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++; 
    } 
} 

und hier ist die ASM (-Os) hergestellt:

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

Es ist eine 64-Bit-CPU, oder? –

+1

Fließkommaoperationen können parallel zu anderen CPU-Anweisungen ausgeführt werden, sodass die Diskrepanz berücksichtigt werden kann. Obwohl die Disassemblierung beide Xmm-Register verwendet, bin ich jetzt verwirrt. –

+0

Eine ähnliche Frage aus einem anderen Forum mit einigen Vergleiche. [Fließkomma vs. Integer] (http://stackoverflow.com/questions/2550281/floating-point-vs-integer-calculations-on-moder-hardware). Soweit ich weiß, führen erweiterte Präzisions-Mathematikroutinen wie APFLOAT ihre Operationen mit Fließkomma aus, aber ich weiß nicht, ob Anweisungen vom Typ SSE verwendet werden. – rcgldr

Antwort

6

Der Unterschied ist die Umwandlung von ganzen Zahlen in dem ersten Doppel-Code (die Vektoren enthalten unsigned int, das Produkt in Integer-Arithmetik ist, aber die Anhäufung Verwendungen double, in Assembler fügt dies Ihrem Befehl den cvtsi2sd Befehl hinzu).

Im zweiten Code verwenden Sie doppelt überall, so dass Sie keine Konvertierung haben und der Code schneller läuft.

Dieser Unterschied wäre viel mehr ausgeprägt auf einer CPU, die eine strengere Unterscheidung zwischen den festen und Gleitkomma-Prozessoreinheiten hat (die POWER-Plattform ist ein Beispiel dafür). Die X86-Plattform ist in dieser Hinsicht sehr nachsichtig.