910e62b5创建于 1月15日历史提交
// 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