diff --git a/millgame.pro b/millgame.pro
index eb97cbc0..7e48f350 100644
--- a/millgame.pro
+++ b/millgame.pro
@@ -64,6 +64,7 @@ HEADERS += \
src/base/misc.h \
src/base/sort.h \
src/base/stack.h \
+ src/base/stopwatch.h \
src/base/thread.h \
src/ai/search.h \
src/base/zobrist.h \
diff --git a/millgame.vcxproj b/millgame.vcxproj
index e2944d2c..287231a2 100644
--- a/millgame.vcxproj
+++ b/millgame.vcxproj
@@ -122,7 +122,7 @@
Sync
$(IntDir)
MaxSpeed
- _WINDOWS;UNICODE;_UNICODE;WIN32;WIN64;QT_NO_DEBUG;NDEBUG;QT_CORE_LIB;QT_GUI_LIB;QT_MULTIMEDIA_LIB;QT_MULTIMEDIAWIDGETS_LIB;QT_NETWORK_LIB;QT_NETWORKAUTH_LIB;QT_WIDGETS_LIB;%(PreprocessorDefinitions)
+ _WINDOWS;UNICODE;_UNICODE;WIN32;WIN64;QT_NO_DEBUG;QT_CORE_LIB;QT_GUI_LIB;QT_MULTIMEDIA_LIB;QT_MULTIMEDIAWIDGETS_LIB;QT_NETWORK_LIB;QT_NETWORKAUTH_LIB;QT_WIDGETS_LIB;%(PreprocessorDefinitions)
false
$(IntDir)vc$(PlatformToolsetVersion).pdb
MultiThreadedDLL
@@ -158,7 +158,7 @@
$(QTDIR)
.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp
- _WINDOWS;UNICODE;_UNICODE;WIN32;WIN64;QT_NO_DEBUG;NDEBUG;QT_CORE_LIB;QT_GUI_LIB;QT_MULTIMEDIA_LIB;QT_MULTIMEDIAWIDGETS_LIB;QT_NETWORK_LIB;QT_NETWORKAUTH_LIB;QT_WIDGETS_LIB;%(PreprocessorDefinitions)
+ _WINDOWS;UNICODE;_UNICODE;WIN32;WIN64;QT_NO_DEBUG;QT_CORE_LIB;QT_GUI_LIB;QT_MULTIMEDIA_LIB;QT_MULTIMEDIAWIDGETS_LIB;QT_NETWORK_LIB;QT_NETWORKAUTH_LIB;QT_WIDGETS_LIB;%(PreprocessorDefinitions)
msvc
$(Configuration)/moc_predefs.h
Moc'ing %(Identity)...
@@ -455,6 +455,7 @@
+
diff --git a/millgame.vcxproj.filters b/millgame.vcxproj.filters
index 660fb981..ae34016c 100644
--- a/millgame.vcxproj.filters
+++ b/millgame.vcxproj.filters
@@ -132,6 +132,9 @@
ai
+
+ base
+
diff --git a/src/base/stopwatch.h b/src/base/stopwatch.h
new file mode 100644
index 00000000..b1f40d15
--- /dev/null
+++ b/src/base/stopwatch.h
@@ -0,0 +1,81 @@
+#ifndef STOPWATCH_H_
+#define STOPWATCH_H_
+
+#include
+#include
+#include
+#include
+#include
+#ifdef _WIN32
+#include
+#endif
+
+namespace stopwatch {
+// An implementation of the 'TrivialClock' concept using the rdtscp instruction.
+struct rdtscp_clock {
+ using rep = std::uint64_t;
+ using period = std::ratio<1>;
+ using duration = std::chrono::duration;
+ using time_point = std::chrono::time_point;
+
+ static auto now() noexcept -> time_point
+ {
+#ifdef _WIN32
+ unsigned int ui;
+ return time_point(duration((static_cast(__rdtscp(&ui)))));
+#else
+ std::uint32_t hi, lo;
+ __asm__ __volatile__("rdtscp" : "=d"(hi), "=a"(lo));
+ return time_point(duration((static_cast(hi) << 32) | lo));
+#endif // WIN32
+ }
+};
+
+// A timer using the specified clock.
+template
+struct timer {
+ using time_point = typename Clock::time_point;
+ using duration = typename Clock::duration;
+
+ timer(const duration duration) noexcept : expiry(Clock::now() + duration) {}
+ timer(const time_point expiry) noexcept : expiry(expiry) {}
+
+ bool done(time_point now = Clock::now()) const noexcept {
+ return now >= expiry;
+ }
+
+ auto remaining(time_point now = Clock::now()) const noexcept -> duration {
+ return expiry - now;
+ }
+
+ const time_point expiry;
+};
+
+template
+constexpr auto make_timer(typename Clock::duration duration) -> timer {
+ return timer(duration);
+}
+
+// Times how long it takes a function to execute using the specified clock.
+template
+auto time(Func&& function) -> typename Clock::duration {
+ const auto start = Clock::now();
+ function();
+ return Clock::now() - start;
+}
+
+// Samples the given function N times using the specified clock.
+template
+auto sample(Func&& function) -> std::array {
+ std::array samples;
+
+ for (std::size_t i = 0u; i < N; ++i) {
+ samples[i] = time(function);
+ }
+
+ std::sort(samples.begin(), samples.end());
+ return samples;
+}
+} /* namespace stopwatch */
+
+#endif // STOPWATCH_H_