2015-02-04 28 views
7

In C++, sto provando a scrivere un wrapper attorno a un intero di 64 bit. La mia aspettativa è che se scritto correttamente e tutti i metodi sono in linea, tale wrapper dovrebbe essere performante quanto il tipo reale. Rispondere a questo question su SO sembra essere d'accordo con le mie aspettative.Perché VC++ non è in grado di ottimizzare un wrapper intero?

Ho scritto questo codice per verificare la mia aspettativa:

class B 
{ 
private: 
    uint64_t _v; 

public: 
    inline B() {}; 
    inline B(uint64_t v) : _v(v) {}; 

    inline B& operator=(B rhs) { _v = rhs._v; return *this; }; 
    inline B& operator+=(B rhs) { _v += rhs._v; return *this; }; 
    inline operator uint64_t() const { return _v; }; 
}; 

int main(int argc, char* argv[]) 
{ 
    typedef uint64_t; 
    //typedef B T; 
    const unsigned int x = 100000000; 

    Utils::CTimer timer; 
    timer.start(); 

    T sum = 0; 
    for (unsigned int i = 0; i < 100; ++i) 
    { 
     for (uint64_t f = 0; f < x; ++f) 
     { 
     sum += f; 
     } 
    } 

    float time = timer.GetSeconds(); 

    cout << sum << endl 
     << time << " seconds" << endl; 

    return 0; 
} 

Quando eseguo questo con typedef B T; invece di typedef uint64_t T i tempi riportati sono costantemente più lenti del 10% quando compilati con VC++. Con g ++ le prestazioni sono le stesse se uso il wrapper o meno.

Dal momento che g ++ fa suppongo che non vi sia alcun motivo tecnico per cui VC++ non possa ottimizzarlo correttamente. C'è qualcosa che potrei fare per farlo ottimizzare?

Ho già provato a giocare con la bandiera ottimizzazioni senza successo

+0

Hai eseguito il codice da Visual Studio o da una console Windows? – jpo38

+0

Non sarò sorpreso se g ++ ha piegato l'intero ciclo. –

+1

Tuffati nell'assemblaggio generato! –

risposta

3

Utilizzando /O2 (massimizzare la velocità), entrambe le alternative generano esattamente lo stesso assembly utilizzando Visual Studio 2012. Questo è il tuo codice, meno i tempi e output:

00FB1000 push  ebp 
00FB1001 mov   ebp,esp 
00FB1003 and   esp,0FFFFFFF8h 
00FB1006 sub   esp,8 
00FB1009 mov   edx,64h 
00FB100E mov   edi,edi 
00FB1010 xorps  xmm0,xmm0 
00FB1013 movlpd  qword ptr [esp],xmm0 
00FB1018 mov   ecx,dword ptr [esp+4] 
00FB101C mov   eax,dword ptr [esp] 
00FB101F nop 
00FB1020 add   eax,1 
00FB1023 adc   ecx,0 
00FB1026 jne   main+2Fh (0FB102Fh) 
00FB1028 cmp   eax,5F5E100h 
00FB102D jb   main+20h (0FB1020h) 
00FB102F dec   edx 
00FB1030 jne   main+10h (0FB1010h) 
00FB1032 xor   eax,eax 

Suppongo che i tempi misurati oscillino o che non siano sempre corretti.

+0

'xmm0'! Registri MMX! * * * Ha vettorializzato l'operazione! –

+0

@PanagiotisKanavos Effettivamente, una vista rara direi. – Daerst

+1

In realtà non è raro che VC sia superato solo dai compilatori Intel nel codice di parallelizzazione. –

4

Per la cronaca, questo è ciò g ++ e clang ++ 's assembly generato a -O2 traduce (in entrambi i casi involucro e non involucro), Modulo parte temporizzazione:

sum = 499999995000000000; 
cout << sum << endl; 

In altre parole, ottimizzato il estendersi interamente. A prescindere da quanto sia difficile provare a vettorizzare il ciclo, è piuttosto difficile non eseguire il looping del tutto :)