benchmarking, zmiana kolejności kodu, zmienność

Postanawiam, że chcę przetestować konkretną funkcję, więc naiwnie piszę taki kod:

#include <ctime>
#include <iostream>

int SlowCalculation(int input) { ... }

int main() {
    std::cout << "Benchmark running..." << std::endl;
    std::clock_t start = std::clock();
    int answer = SlowCalculation(42);
    std::clock_t stop = std::clock();
    double delta = (stop - start) * 1.0 / CLOCKS_PER_SEC;
    std::cout << "Benchmark took " << delta << " seconds, and the answer was "
              << answer << '.' << std::endl;
    return 0;
}

Kolega zauważył, że powinienem zadeklarowaćstart istop zmienne jakvolatile aby uniknąć zmiany kolejności kodu. Zasugerował, że optymalizator może na przykład skutecznie zmienić kolejność kodu w ten sposób:

    std::clock_t start = std::clock();
    std::clock_t stop = std::clock();
    int answer = SlowCalculation(42);

Na początku byłem sceptyczny, że takie ekstremalne zmiany kolejności są dozwolone, ale po kilku badaniach i eksperymentach dowiedziałem się, że tak jest.

Ale zmienność nie wydawała się właściwym rozwiązaniem; nie jest ulotny tylko dla I / O z mapowaniem pamięci?

Niemniej jednak dodałemvolatile i okazało się, że benchmark nie tylko znacznie wydłużył czas działania, ale także był bardzo niespójny od uruchomienia do uruchomienia. Bez zmienności (i mając szczęście, że kod nie został zmieniony), benchmark konsekwentnie trwał 600-700 ms. Przy niestabilności często trwało 1200 ms, a czasami ponad 5000 ms. Wykazy demontażu dla dwóch wersji nie wykazały praktycznie żadnej różnicy poza innym wyborem rejestrów. Zastanawiam się, czy istnieje inny sposób na uniknięcie zmiany kodu, który nie ma tak przytłaczających skutków ubocznych.

Moje pytanie brzmi:

Jak najlepiej zapobiegać zmianie kolejności kodu w kodzie benchmarkingu?

Moje pytanie jest podobne doten (co polegało na używaniu lotnych, aby uniknąć elisji, a nie zmiany kolejności),ten (który nie odpowiadał na pytanie, jak zapobiegać zmianie kolejności) iten (który debatował, czy problem polegał na zmianie kolejności kodu, czy na eliminacji martwego kodu). Chociaż wszystkie trzy dotyczą tego właśnie tematu, żaden z nich nie odpowiada na moje pytanie.

Aktualizacja: Wydaje się, że mój kolega się pomylił i że zmiana kolejności nie jest zgodna ze standardem. Poprosiłem wszystkich, którzy tak powiedzieli, i przyznaję nagrodę Maximowi.

Widziałem jeden przypadek (oparty na kodzie wto pytanie) gdzie Visual Studio 2010 zmieniło kolejność wywołań zegara, jak to zilustrowałem (tylko w kompilacjach 64-bitowych). Próbuję zrobić minimalny przypadek, aby to zilustrować, abym mógł zgłosić błąd w Microsoft Connect.

Dla tych, którzy powiedzieli, że lotność powinna być znacznie wolniejsza, ponieważ wymusza odczyt i zapis do pamięci, nie jest to całkowicie zgodne z emitowanym kodem. W mojej odpowiedzi nato pytanie, Pokazuję demontaż dla kodu z i bez lotności. Wewnątrz pętli wszystko jest przechowywane w rejestrach. Jedyne znaczące różnice wydają się być wyborem rejestru. Nie rozumiem zespołu X86 na tyle dobrze, aby wiedzieć, dlaczego wydajność wersji nieulotnej jestkonsekwentnie szybko, podczas gdy wersja zmienna jestniekonsekwentnie (a czasami dramatycznie) wolniej.

questionAnswers(8)

yourAnswerToTheQuestion