2014-10-04 4 views
6

Questa uscita code example volontà time: 0 indipendentemente dal valore di N quando compilato con Visual Studio Professional 2013 Update 3 in modalità di rilascio, sia l'opzione 32 e 64-bit:È possibile riprodurre o spiegare questo errore di Visual C++ con ctime?

#include <iostream> 
#include <functional> 
#include <ctime> 

using namespace std; 

void bar(int i, int& x, int& y) {x = i%13; y = i%23;} 

int g(int N = 1E9) { 
    int x, y; 
    int r = 0; 

    for (int i = 1; i <= N; ++i) { 
    bar(i, x, y); 
    r += x+y; 
    } 

    return r; 
} 

int main() 
{ 
    auto t0 = clock(); 
    auto r = g(); 
    auto t1 = clock(); 

    cout << r << " time: " << t1-t0 << endl; 

    return 0; 
} 

Quando la prova con gcc, clangore e altra versione di vC++ su rextester.com, si comporta correttamente ed emette time maggiore di zero. Qualche indizio su cosa sta succedendo qui?

ho notato che la funzione inlining g() ripristina comportamento corretto, ma cambiando dichiarazione e inizializzazione ordine di t0, r e t1 no.

+3

Probabilmente il tempo per eseguire l'operazione è inferiore alla risoluzione del timer utilizzato da Windows quando si chiama clock(). Recentemente ho scritto qui una risposta che potrebbe darti un'idea dell'uso dei timer di risoluzione superiore su Windows: http://stackoverflow.com/questions/25954602/ctimespan-always-gets-zero –

+0

Prova N = 2147483646 e vedi se questo fa un differenza. –

+0

@MichaelPetch _time per fare l'operazione è sotto la risoluzione del timer_ - non è il caso, ho provato grande 'N', dove ci vogliono alcuni secondi per eseguire' f() '. –

risposta

9

Se si guarda il winddow di smontaggio utilizzando il debugger è possibile visualizzare il codice generato. Per VS2012 esprimere in modalità di rilascio si ottiene questo:

00AF1310 push  edi 
    auto t0 = clock(); 
00AF1311 call  dword ptr ds:[0AF30E0h] 
00AF1317 mov   edi,eax 
    auto r = g(); 
    auto t1 = clock(); 
00AF1319 call  dword ptr ds:[0AF30E0h] 

    cout << r << " time: " << t1-t0 << endl; 
00AF131F push  dword ptr ds:[0AF3040h] 
00AF1325 sub   eax,edi 
00AF1327 push  eax 
00AF1328 call  g (0AF1270h) 
00AF132D mov   ecx,dword ptr ds:[0AF3058h] 
00AF1333 push  eax 
00AF1334 call  dword ptr ds:[0AF3030h] 
00AF133A mov   ecx,eax 
00AF133C call  std::operator<<<std::char_traits<char> > (0AF17F0h) 
00AF1341 mov   ecx,eax 
00AF1343 call  dword ptr ds:[0AF302Ch] 
00AF1349 mov   ecx,eax 
00AF134B call  dword ptr ds:[0AF3034h] 

dalle prime 4 linee di montaggio si può vedere che le due chiamate per clock (ds:[0AF30E0h]) accada prima della chiamata a g. Quindi, in questo caso, non importa quanto tempo impieghi g, il risultato mostrerà solo il tempo necessario tra queste due chiamate sequenziali.

Sembra che VS abbia determinato che g non ha effetti collaterali che potrebbero influire sullo clock, quindi è possibile spostare le chiamate in tutta sicurezza.

Come indicato da Michael Petch nei commenti, l'aggiunta di volatile alla dichiarazione di r impedirà al compilatore di spostare la chiamata.

+1

Contrassegnare 'r' come' volatile' sarebbe probabilmente sufficiente per il compilatore per modificare tale comportamento. Mi aspetto che questo funzioni 'volatile auto r = g();' –

+0

È legale? Seguendo questo ragionamento, quasi tutti i calcoli non influenzano il 'clock', quindi potrebbe essere spostato ovunque dal compilatore, ma sarebbe una pura follia. –

+0

@MichaelPatch sei corretto - questo lo fa - lo aggiungo nella risposta. –