2012-02-14 17 views
10

Ho scritto il programma e lo ho compilato per la piattaforma x64 e x86 in Visual Studio 2010 su Intel Core i5-2500. la versione x64 impiega circa 19 secondi per l'esecuzione e x86 impiega circa 17 secondi. Quale può essere la ragione di tale comportamento?Perché il programma C++ compilato per la piattaforma x64 è più lento di compilato per x86?

#include "timer.h" 

#include <vector> 
#include <iostream> 
#include <algorithm> 
#include <string> 
#include <sstream> 

/********************DECLARATIONS************************************************/ 
class Vector 
{ 
public: 
    Vector():x(0),y(0),z(0){} 

    Vector(double x, double y, double z) 
     : x(x) 
     , y(y) 
     , z(z) 
    { 
    } 

    double x; 
    double y; 
    double z; 
}; 


double Dot(const Vector& a, const Vector& b) 
{ 
    return a.x * b.x + a.y * b.y + a.z * b.z; 
} 


class Vector2 
{ 
public: 
    typedef double value_type; 

    Vector2():x(0),y(0){} 

    Vector2(double x, double y) 
     : x(x) 
     , y(y) 
    { 
    } 

    double x; 
    double y; 
}; 

/******************************TESTS***************************************************/ 

void Test(const std::vector<Vector>& m, std::vector<Vector2>& m2) 
{ 
    Vector axisX(0.3f, 0.001f, 0.25f); 
    Vector axisY(0.043f, 0.021f, 0.45f); 

    std::vector<Vector2>::iterator i2 = m2.begin(); 

    std::for_each(m.begin(), m.end(), 
     [&](const Vector& v) 
    { 
     Vector2 r(0,0); 
     r.x = Dot(axisX, v); 
     r.y = Dot(axisY, v); 

     (*i2) = r; 
     ++i2; 
    }); 
} 


int main() 
{ 
    cpptask::Timer timer; 

    int len2 = 300; 
    size_t len = 5000000; 
    std::vector<Vector> m; 
    m.reserve(len); 
    for (size_t i = 0; i < len; ++i) 
    { 
     m.push_back(Vector(i * 0.2345, i * 2.67, i * 0.98)); 
    } 

    /***********************************************************************************/ 
    { 
     std::vector<Vector2> m2(m.size()); 
     double time = 0; 
     for (int i = 0; i < len2; ++i) 
     { 
      timer.Start(); 
      Test(m, m2); 
      time += timer.End(); 
     } 
     std::cout << "Dot product double - " << time/len2 << std::endl; 
    } 
    /***********************************************************************************/ 


    return 0; 
} 
+1

Interessante. Sono in grado di riprodurlo su un Core i7 920. – Mysticial

+0

Vale la pena notare che è possibile utilizzare le intrinseche XMM e risparmiare molto più tempo. –

risposta

19

Risposta breve: È un singhiozzo del compilatore. Errore x64 fallito.


lungo Risposta:

Questa versione x86 è molto lento se SSE2 è disattivato. Ma sono in grado di riprodurre i risultati con SSE2 abilitato in x86.

Se ci si immerge nell'assemblaggio di quel circuito interno. La versione x64 ha due copie di memoria extra alla fine.

x86:

[email protected]: 
movsd xmm2, QWORD PTR [eax-8] 
movsd xmm0, QWORD PTR [eax-16] 
movsd xmm3, QWORD PTR [eax] 
movapd xmm1, xmm0 
mulsd xmm0, QWORD PTR [email protected] 
movapd xmm7, xmm2 
mulsd xmm2, QWORD PTR [email protected] 
mulsd xmm7, xmm5 
mulsd xmm1, xmm4 
addsd xmm1, xmm7 
movapd xmm7, xmm3 
mulsd xmm3, QWORD PTR [email protected] 
mulsd xmm7, xmm6 
add eax, 24     ; 00000018H 
addsd xmm1, xmm7 
addsd xmm0, xmm2 
movq QWORD PTR [ecx], xmm1 
addsd xmm0, xmm3 
movq QWORD PTR [ecx+8], xmm0 
lea edx, DWORD PTR [eax-16] 
add ecx, 16     ; 00000010H 
cmp edx, esi 
jne SHORT [email protected] 

x64:

[email protected]: 
movsdx xmm3, QWORD PTR [rdx-8] 
movsdx xmm5, QWORD PTR [rdx-16] 
movsdx xmm4, QWORD PTR [rdx] 
movapd xmm2, xmm3 
mulsd xmm2, xmm6 
movapd xmm0, xmm5 
mulsd xmm0, xmm7 
addsd xmm2, xmm0 
movapd xmm1, xmm4 
mulsd xmm1, xmm8 
addsd xmm2, xmm1 
movsdx QWORD PTR r$109492[rsp], xmm2 
mulsd xmm5, xmm9 
mulsd xmm3, xmm10 
addsd xmm5, xmm3 
mulsd xmm4, xmm11 
addsd xmm5, xmm4 
movsdx QWORD PTR r$109492[rsp+8], xmm5 
mov rcx, QWORD PTR r$109492[rsp] 
mov QWORD PTR [rax], rcx 
mov rcx, QWORD PTR r$109492[rsp+8] 
mov QWORD PTR [rax+8], rcx 
add rax, 16 
add rdx, 24 
lea rcx, QWORD PTR [rdx-16] 
cmp rcx, rbx 
jne SHORT [email protected] 

La versione x64 ha molto più (inspiegabile) si sposta alla fine del ciclo. Sembra una sorta di copia dei dati da memoria a memoria.

EDIT:

Si scopre che l'ottimizzatore x64 non è in grado di ottimizzare il seguente copia:

(*i2) = r; 

Questo è il motivo per cui l'anello interno ha due copie di memoria extra. Se si modifica il ciclo su questo:

std::for_each(m.begin(), m.end(), 
    [&](const Vector& v) 
{ 
    i2->x = Dot(axisX, v); 
    i2->y = Dot(axisY, v); 
    ++i2; 
}); 

Ciò elimina le copie. Ora la versione x64 è altrettanto veloce come la versione x86:

x86: 0.0249423 
x64: 0.0249348 

Lesson Learned: compilatori non sono perfetti.

+0

Non penso che lo faccia ... ma è 'double' 64-bit se compilato per 64-bit contro 32-bit se compilato per arch di 32-bit. Credo che le lunghe modifiche cambino, ma non sono sicuro che il doppio lo faccia. Lo controllerei, ma lo studio visivo mi sta solo permettendo di compilare 32-bit oggi. double dovrebbe essere 64 bit su entrambi (8 byte). – David

+0

Nah, 'double' è standard IEEE a doppia precisione su x86. L'assemblaggio qui è abbastanza chiaro, è tutto SSE scalare a doppia precisione. – Mysticial

+0

Sembra lo stesso problema: http://www.dreamincode.net/forums/topic/127989-compiler-made-optimizations/ Per risolvere il problema, hanno utilizzato l'ottimizzazione del compilatore/O2 che ha portato la versione a 64 bit a essere più veloce rispetto alla versione a 32 bit. Puoi provarlo e vedere se aiuta? – David

-2

64-bit normalmente è leggermente più lenta rispetto a 32-bit (per il codice che specificamente non sfruttare funzionalità a 64 bit). Un problema particolare è che i puntatori sono più grandi, riducendo la quantità che può essere trattenuta nella cache.

+0

Potrebbe essere vero, ma dove sono i puntatori qui? Vedo molta memoria per accedere al grande vettore e alle operazioni in virgola mobile. Non vedo consumare molta larghezza di banda di memoria in giro. –

+0

Ma perché questo articolo http://msdn.microsoft.com/en-us/library/windows/desktop/ee418798%28v=vs.85%29.aspx dice che l'architettura x64 ha migliorato le operazioni in virgola mobile. – KolKir

+3

L'ISA x86_64 include SSE + SSE2, che non può essere detto di x86. Pertanto, i binari generati solo con le istruzioni del minimo comun denominatore, senza alcun ASM creato a mano e senza rilevamento della cpuid e blocchi insn separati per ogni SSEage - e questo è probabilmente ciò a cui si riferisce Microsoft - sono più lenti come devono fare senza usare SSE. –

0

I non rispondere alla tua domanda, ma penso che vale la pena menzionare:

Si consiglia di non scrivere le classi di vettore da soli. Per i vettori a lunghezza fissa piuttosto usano boost::Array o cv::Vec2d and cv::Vec3d che ha costruito a punti e di altre funzioni veloci come operazione +, - ecc ... (anche cv :: Vec < tipo, la lunghezza > è offerto)

Problemi correlati