1 // Copyright 2024 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <windows.h>
6
7 #include <stdint.h>
8
9 #include <algorithm>
10 #include <cstdio>
11
12 #include "base/bit_cast.h"
13 #include "base/strings/stringprintf.h"
14 #include "base/time/time.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16 #include "testing/perf/perf_result_reporter.h"
17 #include "third_party/google_benchmark/src/include/benchmark/benchmark.h"
18
19 namespace base {
20 namespace {
21
22 constexpr char kCountDelta[] = ".count_time_imprecise_precise";
23 constexpr char kAvgDelta[] = ".avg_time_precise_imprecise";
24 constexpr char kMinDelta[] = ".min_time_precise_imprecise";
25 constexpr char kMaxDelta[] = ".max_time_precise_imprecise";
26
27 // Copied from base/time_win.cc.
28 // From MSDN, FILETIME "Contains a 64-bit value representing the number of
29 // 100-nanosecond intervals since January 1, 1601 (UTC)."
FileTimeToMicroseconds(const FILETIME & ft)30 int64_t FileTimeToMicroseconds(const FILETIME& ft) {
31 // Need to bit_cast to fix alignment, then divide by 10 to convert
32 // 100-nanoseconds to microseconds. This only works on little-endian
33 // machines.
34 return bit_cast<int64_t, FILETIME>(ft) / 10;
35 }
36
CurrentTimePrecise()37 int64_t CurrentTimePrecise() {
38 FILETIME ft;
39 ::GetSystemTimePreciseAsFileTime(&ft);
40 return FileTimeToMicroseconds(ft);
41 }
42
CurrentTimeImprecise()43 int64_t CurrentTimeImprecise() {
44 FILETIME ft;
45 ::GetSystemTimeAsFileTime(&ft);
46 return FileTimeToMicroseconds(ft);
47 }
48
49 } // namespace
50
51 // This test case compares the performances of CurrentWallclockMicroseconds()
52 // implemented with using GetSystemTimeAsFileTime() or
53 // GetSystemTimePreciseAsFileTime().
TEST(WinTimePerfTest,Precise)54 TEST(WinTimePerfTest, Precise) {
55 // The time interval that likely grabs a hardware timer interruption.
56 static constexpr TimeDelta kInterval = Milliseconds(50);
57 // The loop amount of calling the wall clock, it guaranties non zero amount of
58 // time ticks.
59 static constexpr int kLoop = 1000;
60
61 int precise_counter = 0;
62 TimeDelta precise_max_time;
63 TimeDelta precise_min_time = TimeDelta::Max();
64
65 TimeTicks begin = TimeTicks::Now();
66 TimeTicks end = begin + kInterval;
67 for (TimeTicks start = begin; start < end; start = TimeTicks::Now()) {
68 for (int i = 0; i < kLoop; ++i) {
69 int64_t current = CurrentTimePrecise();
70 ::benchmark::DoNotOptimize(current);
71 }
72
73 const TimeDelta delta = TimeTicks::Now() - start;
74 precise_min_time = std::min(precise_min_time, delta);
75 precise_max_time = std::max(precise_max_time, delta);
76 precise_counter += kLoop;
77 }
78 const TimeDelta precise_duration = TimeTicks::Now() - begin;
79
80 int imprecise_counter = 0;
81 TimeDelta imprecise_max_time;
82 TimeDelta imprecise_min_time = TimeDelta::Max();
83
84 begin = TimeTicks::Now();
85 end = begin + kInterval;
86 for (TimeTicks start = begin; start < end; start = TimeTicks::Now()) {
87 for (int i = 0; i < kLoop; ++i) {
88 int64_t current = CurrentTimeImprecise();
89 ::benchmark::DoNotOptimize(current);
90 }
91
92 const TimeDelta delta = TimeTicks::Now() - start;
93 imprecise_min_time = std::min(imprecise_min_time, delta);
94 imprecise_max_time = std::max(imprecise_max_time, delta);
95 imprecise_counter += kLoop;
96 }
97 const TimeDelta imprecise_duration = TimeTicks::Now() - begin;
98
99 ASSERT_GT(precise_counter, 0);
100 ASSERT_GT(imprecise_counter, 0);
101
102 // Format output like in Google Benchmark.
103 std::printf("----------------------------------------------------------\n");
104 std::printf(" Min Time Avg Time Max Time Iterations\n");
105 std::printf("----------------------------------------------------------\n");
106 std::printf("Precise %8lld ns %8lld ns %8lld ns %12d\n",
107 precise_min_time.InNanoseconds() / kLoop,
108 precise_duration.InNanoseconds() / precise_counter,
109 precise_max_time.InNanoseconds() / kLoop, precise_counter);
110 std::printf("Imprecise %8lld ns %8lld ns %8lld ns %12d\n",
111 imprecise_min_time.InNanoseconds() / kLoop,
112 imprecise_duration.InNanoseconds() / imprecise_counter,
113 imprecise_max_time.InNanoseconds() / kLoop, imprecise_counter);
114
115 // Negative values mean the function ::GetSystemTimePreciseAsFileTime() wins.
116
117 // Count of calls in kInterval (50) ms.
118 const double count_delta = imprecise_counter - precise_counter;
119 const double avg_delta = kInterval.InNanoseconds() / precise_counter -
120 kInterval.InNanoseconds() / imprecise_counter;
121 const double min_delta =
122 (precise_min_time.InNanoseconds() - imprecise_min_time.InNanoseconds()) /
123 kLoop;
124 const double max_delta =
125 (precise_max_time.InNanoseconds() - imprecise_max_time.InNanoseconds()) /
126 kLoop;
127
128 perf_test::PerfResultReporter reporter("WinTime", "delta");
129 reporter.RegisterFyiMetric(
130 kCountDelta, StringPrintf("/%lldms", kInterval.InMilliseconds()));
131 reporter.RegisterFyiMetric(kAvgDelta, "ns");
132 reporter.RegisterFyiMetric(kMinDelta, "ns");
133 reporter.RegisterFyiMetric(kMaxDelta, "ns");
134
135 reporter.AddResult(kCountDelta, count_delta);
136 reporter.AddResult(kAvgDelta, avg_delta);
137 reporter.AddResult(kMinDelta, min_delta);
138 reporter.AddResult(kMaxDelta, max_delta);
139 }
140
141 } // namespace base
142