2014-10-04 4 views
6

Wyjście code example wola time: 0 niezależnie od wartości N gdy skompilowany z Visual Studio Professional 2013 Update 3 w trybie zwolnienia, zarówno w opcji 32 i 64-bitowe:Czy można odtworzyć lub wyjaśnić ten błąd Visual C++ za pomocą 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; 
} 

Kiedy testowane z GCC, brzękiem i drugiej wersji vC++ na rextester.com, zachowuje się poprawnie i wyprowadza time większe od zera. Jakieś wskazówki, co tu się dzieje?

zauważyłem, że inline funkcję g() przywraca prawidłowe zachowanie, ale zmienia deklaracji i inicjalizacji kolejność t0, r i t1 nie.

+3

Prawdopodobnie czas wykonania operacji jest niższy niż czasomierza, którego Windows używa podczas wywoływania zegara(). Niedawno napisałem tutaj odpowiedź, która może dać ci pomysł korzystania z timerów o wyższej rozdzielczości w systemie Windows: http://stackoverflow.com/questions/25954602/ctimespan-always-gets-zero –

+0

Spróbuj N = 2147483646 i sprawdź, czy to sprawia, że różnica. –

+0

@MichaelPetch _time do wykonania operacji jest poniżej rozdzielczości timera_ - nie jest tak w przypadku, próbowałem dużego 'N', gdzie wykonanie' f() 'trwa kilka sekund. –

Odpowiedz

9

Jeśli spojrzysz na demontaż winddow za pomocą debuggera, zobaczysz wygenerowany kod. Dla VS2012 wyrazić w trybie zwolnienia masz to:

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] 

od pierwszych 4 liniach montażowych można zobaczyć, że dwa zaproszenia do clock (ds:[0AF30E0h]) stało przed wywołaniem g. Tak więc w tym przypadku nie ma znaczenia, jak długo potrwa g, a wynik pokaże tylko czas między tymi dwoma kolejnymi połączeniami.

Wygląda na to, że VS stwierdził, że g nie ma żadnych skutków ubocznych, które mogłyby wpłynąć na clock, więc można bezpiecznie przenosić połączenia.

Jak zauważa Michael Petch w komentarzach, dodanie volatile do deklaracji r zatrzyma kompilator przed przeniesieniem połączenia.

+1

Oznaczenie 'r' jako' volatile' prawdopodobnie wystarczyłoby, aby kompilator zmienił to zachowanie. Spodziewałbym się, że to zadziała 'volatile auto r = g();' –

+0

Czy to jest legalne? Po tym rozumowaniu prawie wszystkie obliczenia nie wpływają na "zegar", więc można go przenieść w dowolnym miejscu przez kompilator, ale byłoby to zwykłe szaleństwo. –

+0

@MichaelPetch masz rację - to robi - dodam to w odpowiedzi. –

Powiązane problemy