Я пытаюсь реализовать класс с высоким разрешением, используемый для измерения времени выполнения функций в 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 ответ
Избегайте ненужного хранения данных
Ваш класс хранит время начала, время остановки и частоту в переменных-членах, однако вам нужно сохранить только время начала:
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локально не требует затрат времени выполнения.— Г. Сон