Класс синхронизации Windows с высоким разрешением

Я пытаюсь реализовать класс с высоким разрешением, используемый для измерения времени выполнения функций в Windows. После использования это документации, я придумал следующее:

#include <iostream>
#include <windows.h>

class Timer {

private:

    LARGE_INTEGER start_count;
    LARGE_INTEGER stop_count;

    LARGE_INTEGER tick_freq;

public:
    // Query the tick frequency upon instantiation of the class
    Timer() {
        QueryPerformanceFrequency(&tick_freq);
    }
    // Query performance counter at the start of a block of code
    void start() {
        QueryPerformanceCounter(&start_count);
    }
    // Query the performance counter at the end of a block of code, then calculate time elapsed in seconds
    double stop() {
        QueryPerformanceCounter(&stop_count);
        return (double)(stop_count.QuadPart - start_count.QuadPart) / tick_freq.QuadPart;
    }
};

int main() {
    Timer t;
    t.start();
    // code to be timed
    std::cout << t.stop();
}

Я так понимаю, что будет ошибка плюс-минус период каждого тика. Однако различные функции, которые я рассчитал, кажется, дают неожиданные результаты. Например, синхронизация функции, которая находит наибольший общий знаменатель с помощью алгоритма Евклида, последовательно использует 100 или 200 нс, но добавление одного int к вектору занимает 1600 нс. Это подводит меня к вопросу: насколько точен этот класс времени и что я могу сделать, чтобы его улучшить?

1 ответ
1

Избегайте ненужного хранения данных

Ваш класс хранит время начала, время остановки и частоту в переменных-членах, однако вам нужно сохранить только время начала:

class Timer {
    LARGE_INTEGER start_count;

public:
    // Query performance counter at the start of a block of code
    void start() {
        QueryPerformanceCounter(&start_count);
    }

    // Query the performance counter at the end of a block of code, then calculate time elapsed in seconds
    double stop() {
        LARGE_INTEGER stop_count;
        QueryPerformanceCounter(&stop_count);

        LARGE_INTEGER tick_freq_count;
        QueryPerformanceFrequency(&tick_freq);
        return (double)(stop_count.QuadPart - start_count.QuadPart) / tick_freq.QuadPart;
    }
};

Рассмотрите возможность использования std::chrono функции

Если вы можете использовать C ++ 11 или более позднюю версию, вы сможете получить ту же информацию о времени независимым от платформы способом, используя std::chrono::high_resolution_clock. Видеть этот пример как это можно использовать.

Точность

Фактическая точность зависит от многих вещей. Собственная точность зависит от точности счетчика производительности процессора, которая может варьироваться в зависимости от модели. Однако есть много причин, по которым измеренное время будет отличаться, среди них:

  • Состояние кеша (как предлагает Кейси, перед выполнением реальных измерений производительности следует выполнить прогрев)
  • Частота процессора изменяется (ОС обычно регулирует частоту много раз в секунду, чтобы ваш процессор работал с максимальной энергоэффективностью)
  • Другие потоки могут получить временной интервал во время выполнения вашего кода

Вы можете попытаться решить все эти проблемы самостоятельно (многие из них решаются путем прогрева, затем многократного выполнения измерений производительности и вычисления среднего значения, каким-либо образом удаляя выбросы). В качестве альтернативы рассмотрите возможность использования существующих библиотек, которые уже решают эту проблему, например Google Benchmark.

  • Спасибо за ссылку на Google Benchmark. Мои аргументы в пользу сохранения времени и частоты остановки в частных переменных заключались в том, что это предотвратит использование дополнительного времени при объявлении локальных переменных в функции остановки и вызове функций Windows для получения их значений. Будет ли время, затрачиваемое на вышеизложенное, достаточно незначительным, чтобы оправдать лучший стиль кодирования, как вы предложили?

    — Джордж Тиан

  • @GeorgeTian FYI, std::chrono::high_resolution_clock во всех случаях, если просто typedef для одного из двух других часов. Не позволяйте исполнителю выбирать. Просто используйте std::chrono::steady_clock

    — Кейси

  • @GeorgeTian Объявление LARGE_INTEGER локально не требует затрат времени выполнения.

    — Г. Сон

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *