1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141

base / time / time_win_perftest.cc [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 <windows.h>

#include <stdint.h>

#include <algorithm>
#include <cstdio>

#include "base/bit_cast.h"
#include "base/strings/stringprintf.h"
#include "base/time/time.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