Profiler: Implement QPCClock to get better precision on Win32
MSVC 2013 (at least) doesn't use QueryPerformanceCounter to implement std::chrono::high_resolution_clock, so it has bad precision. Manually implementing our own clock type using it works around this for now.
This commit is contained in:
parent
cd1fbfcf1b
commit
dc8a3f8bc8
2 changed files with 42 additions and 1 deletions
|
@ -6,6 +6,12 @@
|
||||||
#include "common/profiler_reporting.h"
|
#include "common/profiler_reporting.h"
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && _MSC_VER <= 1800 // MSVC 2013.
|
||||||
|
#define NOMINMAX
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <Windows.h> // For QueryPerformanceCounter/Frequency
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
namespace Profiling {
|
namespace Profiling {
|
||||||
|
|
||||||
|
@ -13,6 +19,23 @@ namespace Profiling {
|
||||||
thread_local Timer* Timer::current_timer = nullptr;
|
thread_local Timer* Timer::current_timer = nullptr;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && _MSC_VER <= 1800 // MSVC 2013
|
||||||
|
QPCClock::time_point QPCClock::now() {
|
||||||
|
static LARGE_INTEGER freq;
|
||||||
|
// Use this dummy local static to ensure this gets initialized once.
|
||||||
|
static BOOL dummy = QueryPerformanceFrequency(&freq);
|
||||||
|
|
||||||
|
LARGE_INTEGER ticks;
|
||||||
|
QueryPerformanceCounter(&ticks);
|
||||||
|
|
||||||
|
// This is prone to overflow when multiplying, which is why I'm using micro instead of nano. The
|
||||||
|
// correct way to approach this would be to just return ticks as a time_point and then subtract
|
||||||
|
// and do this conversion when creating a duration from two time_points, however, as far as I
|
||||||
|
// could tell the C++ requirements for these types are incompatible with this approach.
|
||||||
|
return time_point(duration(ticks.QuadPart * std::micro::den / freq.QuadPart));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
TimingCategory::TimingCategory(const char* name, TimingCategory* parent)
|
TimingCategory::TimingCategory(const char* name, TimingCategory* parent)
|
||||||
: accumulated_duration(0) {
|
: accumulated_duration(0) {
|
||||||
|
|
||||||
|
|
|
@ -18,8 +18,26 @@ namespace Profiling {
|
||||||
#define ENABLE_PROFILING 1
|
#define ENABLE_PROFILING 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
using Duration = std::chrono::nanoseconds;
|
#if defined(_MSC_VER) && _MSC_VER <= 1800 // MSVC 2013
|
||||||
|
// MSVC up to 2013 doesn't use QueryPerformanceCounter for high_resolution_clock, so it has bad
|
||||||
|
// precision. We manually implement a clock based on QPC to get good results.
|
||||||
|
|
||||||
|
struct QPCClock {
|
||||||
|
using duration = std::chrono::microseconds;
|
||||||
|
using time_point = std::chrono::time_point<QPCClock>;
|
||||||
|
using rep = duration::rep;
|
||||||
|
using period = duration::period;
|
||||||
|
static const bool is_steady = false;
|
||||||
|
|
||||||
|
static time_point now();
|
||||||
|
};
|
||||||
|
|
||||||
|
using Clock = QPCClock;
|
||||||
|
#else
|
||||||
using Clock = std::chrono::high_resolution_clock;
|
using Clock = std::chrono::high_resolution_clock;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using Duration = Clock::duration;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a timing category that measured time can be accounted towards. Should be declared as a
|
* Represents a timing category that measured time can be accounted towards. Should be declared as a
|
||||||
|
|
Loading…
Reference in a new issue