2012-02-14 20 views
10

He escrito el programa y lo compilé para la plataforma x64 y x86 en Visual Studio 2010 en Intel Core i5-2500. La versión x64 toma alrededor de 19 segundos para la ejecución y x86 toma alrededor de 17 segundos. ¿Cuál puede ser la razón de tal comportamiento?¿Por qué el programa C++ compilado para la plataforma x64 es más lento que el compilado para 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

interesante. Puedo reproducir esto en un Core i7 920. – Mysticial

+0

Vale la pena señalar que puede utilizar XMM intrínsecos y ahorrar mucho más tiempo. –

Respuesta

19

Respuesta corta: Es un error del compilador. el optimizador x64 falla


respuesta Largo:

Esta versión x86 es muy lento si SSE2 está desactivado. Pero puedo reproducir los resultados con SSE2 habilitado en x86.

Si te sumerges en el ensamblaje de ese lazo más interno. La versión x64 tiene dos copias de memoria adicionales al final.

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 versión x64 tiene mucho más (no explicada) se mueve al final del bucle. Parece una especie de copia de datos de memoria a memoria.

EDIT:

Resulta que el optimizador x64 no es capaz de optimizar el siguiente copia:

(*i2) = r; 

Esta es la razón por el bucle interior tiene dos copias de memoria adicionales. Si cambia el ciclo a este:

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

Esto elimina las copias. Ahora la versión de 64 bits es igual de rápido que la versión x86:

x86: 0.0249423 
x64: 0.0249348 

lección aprendida: compiladores no son perfectos.

+0

No creo que sí ... pero es 'doble' de 64 bits si se compila para 64 bits frente a 32 bits si se compila para el arco de 32 bits. Creo que el tamaño cambia mucho, pero no estoy seguro si el doble sí lo hace. Lo verificaría, pero el estudio visual solo me permite compilar 32 bits hoy. el doble debe ser de 64 bits en ambos (8 bytes). – David

+0

Nah, 'double' es estándar IEEE de doble precisión en x86. El ensamblaje aquí es bastante claro, es todo escaso de precisión doble SSE. – Mysticial

+0

Parece el mismo problema: http://www.dreamincode.net/forums/topic/127989-compiler-made-optimizations/ Para solucionarlo usaron la optimización del compilador/O2 que resultó en la versión de 64 bits que es más rápida que la versión de 32 bits. ¿Puedes probar eso y ver si ayuda? – David

-2

64-bit normalmente es un poco más lento que 32 bits (para el código que específicamente no toma ventaja de las características de 64 bits). Un problema particular es que los punteros son más grandes, lo que reduce la cantidad que se puede retener en el caché.

+0

Eso podría ser cierto, pero ¿dónde están los indicadores aquí? Veo mucho acceso de memoria al vector grande y operaciones de coma flotante. No veo que se consuma mucho ancho de banda de memoria al pasar punteros. –

+0

Pero por qué este artículo http://msdn.microsoft.com/en-us/library/windows/desktop/ee418798%28v=vs.85%29.aspx dice que la arquitectura x64 ha mejorado las operaciones de coma flotante. – KolKir

+3

El ISA x86_64 incluye SSE + SSE2, que no se puede decir de x86. Por lo tanto, los binarios generados con las instrucciones de denominador común más bajo, sin ASM diseñado a mano, y sin detección de cpuid y bloques de insn separados para cada SSEage, y eso es probablemente a lo que se refiere Microsoft, son más lentos ya que tienen que hacerlo sin usar SSE. –

0

que no responde a tu pregunta, pero creo que vale la pena mencionar:

No se debe escribir clases vector por sí mismo. Para los vectores de longitud fija y no utilizan o boost::Arraycv::Vec2d and cv::Vec3d que se ha construido en el punto y otras funciones rápidas como la operación +, - etc ... (también cv :: Vec < tipo, longitud > se ofrece)

Cuestiones relacionadas