blob: c00c09be6e610382c3889d25accb110d2c2effbc [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/time/time.h"
#include <windows.h>
#include <stdint.h>
#include <algorithm>
#include <cstdio>
#include "base/bit_cast.h"
#include "base/strings/stringprintf.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/perf/perf_result_reporter.h"
#include "third_party/google_benchmark/src/include/benchmark/benchmark.h"
namespace base {
namespace {
constexpr char kCountDelta[] = ".count_time_imprecise_precise";
constexpr char kAvgDelta[] = ".avg_time_precise_imprecise";
constexpr char kMinDelta[] = ".min_time_precise_imprecise";
constexpr char kMaxDelta[] = ".max_time_precise_imprecise";
// Copied from base/time_win.cc.
// From MSDN, FILETIME "Contains a 64-bit value representing the number of
// 100-nanosecond intervals since January 1, 1601 (UTC)."
int64_t FileTimeToMicroseconds(const FILETIME& ft) {
// Need to bit_cast to fix alignment, then divide by 10 to convert
// 100-nanoseconds to microseconds. This only works on little-endian
// machines.
return bit_cast<int64_t, FILETIME>(ft) / 10;
}
int64_t CurrentTimePrecise() {
FILETIME ft;
::GetSystemTimePreciseAsFileTime(&ft);
return FileTimeToMicroseconds(ft);
}
int64_t CurrentTimeImprecise() {
FILETIME ft;
::GetSystemTimeAsFileTime(&ft);
return FileTimeToMicroseconds(ft);
}
} // namespace
// This test case compares the performances of CurrentWallclockMicroseconds()
// implemented with using GetSystemTimeAsFileTime() or
// GetSystemTimePreciseAsFileTime().
TEST(WinTimePerfTest, Precise) {
// The time interval that likely grabs a hardware timer interruption.
static constexpr TimeDelta kInterval = Milliseconds(50);
// The loop amount of calling the wall clock, it guaranties non zero amount of
// time ticks.
static constexpr int kLoop = 1000;
int precise_counter = 0;
TimeDelta precise_max_time;
TimeDelta precise_min_time = TimeDelta::Max();
TimeTicks begin = TimeTicks::Now();
TimeTicks end = begin + kInterval;
for (TimeTicks start = begin; start < end; start = TimeTicks::Now()) {
for (int i = 0; i < kLoop; ++i) {
int64_t current = CurrentTimePrecise();
::benchmark::DoNotOptimize(current);
}
const TimeDelta delta = TimeTicks::Now() - start;
precise_min_time = std::min(precise_min_time, delta);
precise_max_time = std::max(precise_max_time, delta);
precise_counter += kLoop;
}
const TimeDelta precise_duration = TimeTicks::Now() - begin;
int imprecise_counter = 0;
TimeDelta imprecise_max_time;
TimeDelta imprecise_min_time = TimeDelta::Max();
begin = TimeTicks::Now();
end = begin + kInterval;
for (TimeTicks start = begin; start < end; start = TimeTicks::Now()) {
for (int i = 0; i < kLoop; ++i) {
int64_t current = CurrentTimeImprecise();
::benchmark::DoNotOptimize(current);
}
const TimeDelta delta = TimeTicks::Now() - start;
imprecise_min_time = std::min(imprecise_min_time, delta);
imprecise_max_time = std::max(imprecise_max_time, delta);
imprecise_counter += kLoop;
}
const TimeDelta imprecise_duration = TimeTicks::Now() - begin;
ASSERT_GT(precise_counter, 0);
ASSERT_GT(imprecise_counter, 0);
// Format output like in Google Benchmark.
std::printf("----------------------------------------------------------\n");
std::printf(" Min Time Avg Time Max Time Iterations\n");
std::printf("----------------------------------------------------------\n");
std::printf("Precise %8lld ns %8lld ns %8lld ns %12d\n",
precise_min_time.InNanoseconds() / kLoop,
precise_duration.InNanoseconds() / precise_counter,
precise_max_time.InNanoseconds() / kLoop, precise_counter);
std::printf("Imprecise %8lld ns %8lld ns %8lld ns %12d\n",
imprecise_min_time.InNanoseconds() / kLoop,
imprecise_duration.InNanoseconds() / imprecise_counter,
imprecise_max_time.InNanoseconds() / kLoop, imprecise_counter);
// Negative values mean the function ::GetSystemTimePreciseAsFileTime() wins.
// Count of calls in kInterval (50) ms.
const double count_delta = imprecise_counter - precise_counter;
const double avg_delta = kInterval.InNanoseconds() / precise_counter -
kInterval.InNanoseconds() / imprecise_counter;
const double min_delta =
(precise_min_time.InNanoseconds() - imprecise_min_time.InNanoseconds()) /
kLoop;
const double max_delta =
(precise_max_time.InNanoseconds() - imprecise_max_time.InNanoseconds()) /
kLoop;
perf_test::PerfResultReporter reporter("WinTime", "delta");
reporter.RegisterFyiMetric(
kCountDelta, StringPrintf("/%lldms", kInterval.InMilliseconds()));
reporter.RegisterFyiMetric(kAvgDelta, "ns");
reporter.RegisterFyiMetric(kMinDelta, "ns");
reporter.RegisterFyiMetric(kMaxDelta, "ns");
reporter.AddResult(kCountDelta, count_delta);
reporter.AddResult(kAvgDelta, avg_delta);
reporter.AddResult(kMinDelta, min_delta);
reporter.AddResult(kMaxDelta, max_delta);
}
} // namespace base