فهرست منبع

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.
Yuri Kunde Schlesner 11 سال پیش
والد
کامیت
dc8a3f8bc8
2فایلهای تغییر یافته به همراه42 افزوده شده و 1 حذف شده
  1. 23 0
      src/common/profiler.cpp
  2. 19 1
      src/common/profiler.h

+ 23 - 0
src/common/profiler.cpp

@@ -6,6 +6,12 @@
 #include "common/profiler_reporting.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 Profiling {
 
@@ -13,6 +19,23 @@ namespace Profiling {
 thread_local Timer* Timer::current_timer = nullptr;
 #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)
         : accumulated_duration(0) {
 

+ 19 - 1
src/common/profiler.h

@@ -18,8 +18,26 @@ namespace Profiling {
 #define ENABLE_PROFILING 1
 #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;
+#endif
+
+using Duration = Clock::duration;
 
 /**
  * Represents a timing category that measured time can be accounted towards. Should be declared as a