1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2023 Google LLC
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker
8*c8dee2aaSAndroid Build Coastguard Worker #include "bench/Benchmark.h"
9*c8dee2aaSAndroid Build Coastguard Worker #include "tools/flags/CommandLineFlags.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "tools/testrunners/benchmark/target/BenchmarkTarget.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "tools/testrunners/common/TestRunner.h"
12*c8dee2aaSAndroid Build Coastguard Worker
13*c8dee2aaSAndroid Build Coastguard Worker static DEFINE_int(maxCalibrationAttempts,
14*c8dee2aaSAndroid Build Coastguard Worker 3,
15*c8dee2aaSAndroid Build Coastguard Worker "Try up to this many times to guess loops for a benchmark, or skip the "
16*c8dee2aaSAndroid Build Coastguard Worker "benchmark.");
17*c8dee2aaSAndroid Build Coastguard Worker static DEFINE_double(overheadGoal,
18*c8dee2aaSAndroid Build Coastguard Worker 0.0001,
19*c8dee2aaSAndroid Build Coastguard Worker "Loop until timer overhead is at most this fraction of our measurements.");
20*c8dee2aaSAndroid Build Coastguard Worker static DEFINE_int(overheadLoops, 100000, "Loops to estimate timer overhead.");
21*c8dee2aaSAndroid Build Coastguard Worker
22*c8dee2aaSAndroid Build Coastguard Worker // Defined in BazelBenchmarkTestRunner.cpp.
23*c8dee2aaSAndroid Build Coastguard Worker SkString humanize(double ms);
24*c8dee2aaSAndroid Build Coastguard Worker
printGlobalStats()25*c8dee2aaSAndroid Build Coastguard Worker void BenchmarkTarget::printGlobalStats() {}
26*c8dee2aaSAndroid Build Coastguard Worker
27*c8dee2aaSAndroid Build Coastguard Worker class RasterBenchmarkTarget : public BenchmarkTarget {
28*c8dee2aaSAndroid Build Coastguard Worker public:
RasterBenchmarkTarget(std::unique_ptr<SurfaceManager> surfaceManager,Benchmark * benchmark)29*c8dee2aaSAndroid Build Coastguard Worker RasterBenchmarkTarget(std::unique_ptr<SurfaceManager> surfaceManager, Benchmark* benchmark)
30*c8dee2aaSAndroid Build Coastguard Worker : BenchmarkTarget(std::move(surfaceManager), benchmark) {}
31*c8dee2aaSAndroid Build Coastguard Worker
getBackend() const32*c8dee2aaSAndroid Build Coastguard Worker Benchmark::Backend getBackend() const override { return Benchmark::Backend::kRaster; }
33*c8dee2aaSAndroid Build Coastguard Worker
34*c8dee2aaSAndroid Build Coastguard Worker // Based on nanobench's setup_cpu_bench():
35*c8dee2aaSAndroid Build Coastguard Worker // https://skia.googlesource.com/skia/+/a063eaeaf1e09e4d6f42e0f44a5723622a46d21c/bench/nanobench.cpp#466.
autoTuneLoops() const36*c8dee2aaSAndroid Build Coastguard Worker std::tuple<int, bool> autoTuneLoops() const override {
37*c8dee2aaSAndroid Build Coastguard Worker // Estimate timer overhead. Based on:
38*c8dee2aaSAndroid Build Coastguard Worker // https://skia.googlesource.com/skia/+/a063eaeaf1e09e4d6f42e0f44a5723622a46d21c/bench/nanobench.cpp#402.
39*c8dee2aaSAndroid Build Coastguard Worker double overhead = 0;
40*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < FLAGS_overheadLoops; i++) {
41*c8dee2aaSAndroid Build Coastguard Worker double start = nowMs();
42*c8dee2aaSAndroid Build Coastguard Worker overhead += nowMs() - start;
43*c8dee2aaSAndroid Build Coastguard Worker }
44*c8dee2aaSAndroid Build Coastguard Worker overhead /= FLAGS_overheadLoops;
45*c8dee2aaSAndroid Build Coastguard Worker
46*c8dee2aaSAndroid Build Coastguard Worker // First figure out approximately how many loops of bench it takes to make overhead
47*c8dee2aaSAndroid Build Coastguard Worker // negligible.
48*c8dee2aaSAndroid Build Coastguard Worker double bench_plus_overhead = 0.0;
49*c8dee2aaSAndroid Build Coastguard Worker int round = 0;
50*c8dee2aaSAndroid Build Coastguard Worker while (bench_plus_overhead < overhead) {
51*c8dee2aaSAndroid Build Coastguard Worker if (round++ == FLAGS_maxCalibrationAttempts) {
52*c8dee2aaSAndroid Build Coastguard Worker TestRunner::Log("Warning: Cannot estimate loops for %s (%s vs. %s); skipping.",
53*c8dee2aaSAndroid Build Coastguard Worker fBenchmark->getUniqueName(),
54*c8dee2aaSAndroid Build Coastguard Worker humanize(bench_plus_overhead).c_str(),
55*c8dee2aaSAndroid Build Coastguard Worker humanize(overhead).c_str());
56*c8dee2aaSAndroid Build Coastguard Worker return std::make_tuple(0, false);
57*c8dee2aaSAndroid Build Coastguard Worker }
58*c8dee2aaSAndroid Build Coastguard Worker bench_plus_overhead = time(1);
59*c8dee2aaSAndroid Build Coastguard Worker }
60*c8dee2aaSAndroid Build Coastguard Worker
61*c8dee2aaSAndroid Build Coastguard Worker // Later we'll just start and stop the timer once but loop N times.
62*c8dee2aaSAndroid Build Coastguard Worker // We'll pick N to make timer overhead negligible:
63*c8dee2aaSAndroid Build Coastguard Worker //
64*c8dee2aaSAndroid Build Coastguard Worker // overhead
65*c8dee2aaSAndroid Build Coastguard Worker // ------------------------- < FLAGS_overheadGoal
66*c8dee2aaSAndroid Build Coastguard Worker // overhead + N * Bench Time
67*c8dee2aaSAndroid Build Coastguard Worker //
68*c8dee2aaSAndroid Build Coastguard Worker // where bench_plus_overhead ~=~ overhead + Bench Time.
69*c8dee2aaSAndroid Build Coastguard Worker //
70*c8dee2aaSAndroid Build Coastguard Worker // Doing some math, we get:
71*c8dee2aaSAndroid Build Coastguard Worker //
72*c8dee2aaSAndroid Build Coastguard Worker // (overhead / FLAGS_overheadGoal) - overhead
73*c8dee2aaSAndroid Build Coastguard Worker // ------------------------------------------ < N
74*c8dee2aaSAndroid Build Coastguard Worker // bench_plus_overhead - overhead)
75*c8dee2aaSAndroid Build Coastguard Worker //
76*c8dee2aaSAndroid Build Coastguard Worker // Luckily, this also works well in practice. :)
77*c8dee2aaSAndroid Build Coastguard Worker const double numer = overhead / FLAGS_overheadGoal - overhead;
78*c8dee2aaSAndroid Build Coastguard Worker const double denom = bench_plus_overhead - overhead;
79*c8dee2aaSAndroid Build Coastguard Worker int loops = (int)ceil(numer / denom);
80*c8dee2aaSAndroid Build Coastguard Worker
81*c8dee2aaSAndroid Build Coastguard Worker return std::make_tuple(loops, true);
82*c8dee2aaSAndroid Build Coastguard Worker }
83*c8dee2aaSAndroid Build Coastguard Worker };
84*c8dee2aaSAndroid Build Coastguard Worker
85*c8dee2aaSAndroid Build Coastguard Worker class NonRenderingBenchmarkTarget : public RasterBenchmarkTarget {
86*c8dee2aaSAndroid Build Coastguard Worker public:
NonRenderingBenchmarkTarget(Benchmark * benchmark)87*c8dee2aaSAndroid Build Coastguard Worker NonRenderingBenchmarkTarget(Benchmark* benchmark) : RasterBenchmarkTarget(nullptr, benchmark) {}
88*c8dee2aaSAndroid Build Coastguard Worker
getBackend() const89*c8dee2aaSAndroid Build Coastguard Worker Benchmark::Backend getBackend() const override { return Benchmark::Backend::kNonRendering; }
90*c8dee2aaSAndroid Build Coastguard Worker
isCpuOrGpuBound() const91*c8dee2aaSAndroid Build Coastguard Worker SurfaceManager::CpuOrGpu isCpuOrGpuBound() const override {
92*c8dee2aaSAndroid Build Coastguard Worker return SurfaceManager::CpuOrGpu::kCPU;
93*c8dee2aaSAndroid Build Coastguard Worker }
94*c8dee2aaSAndroid Build Coastguard Worker
getKeyValuePairs(std::string cpuName,std::string gpuName) const95*c8dee2aaSAndroid Build Coastguard Worker std::map<std::string, std::string> getKeyValuePairs(std::string cpuName,
96*c8dee2aaSAndroid Build Coastguard Worker std::string gpuName) const override {
97*c8dee2aaSAndroid Build Coastguard Worker if (cpuName == "") {
98*c8dee2aaSAndroid Build Coastguard Worker return std::map<std::string, std::string>();
99*c8dee2aaSAndroid Build Coastguard Worker }
100*c8dee2aaSAndroid Build Coastguard Worker return {
101*c8dee2aaSAndroid Build Coastguard Worker {"cpu_or_gpu", "CPU"},
102*c8dee2aaSAndroid Build Coastguard Worker {"cpu_or_gpu_value", cpuName},
103*c8dee2aaSAndroid Build Coastguard Worker };
104*c8dee2aaSAndroid Build Coastguard Worker }
105*c8dee2aaSAndroid Build Coastguard Worker };
106*c8dee2aaSAndroid Build Coastguard Worker
FromConfig(std::string surfaceConfig,Benchmark * benchmark)107*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<BenchmarkTarget> BenchmarkTarget::FromConfig(std::string surfaceConfig,
108*c8dee2aaSAndroid Build Coastguard Worker Benchmark* benchmark) {
109*c8dee2aaSAndroid Build Coastguard Worker if (surfaceConfig == "nonrendering") {
110*c8dee2aaSAndroid Build Coastguard Worker return std::make_unique<NonRenderingBenchmarkTarget>(benchmark);
111*c8dee2aaSAndroid Build Coastguard Worker }
112*c8dee2aaSAndroid Build Coastguard Worker
113*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<SurfaceManager> surfaceManager = SurfaceManager::FromConfig(
114*c8dee2aaSAndroid Build Coastguard Worker surfaceConfig, {benchmark->getSize().width(), benchmark->getSize().height()});
115*c8dee2aaSAndroid Build Coastguard Worker if (surfaceManager == nullptr) {
116*c8dee2aaSAndroid Build Coastguard Worker SK_ABORT("Unknown --surfaceConfig flag value: %s.", surfaceConfig.c_str());
117*c8dee2aaSAndroid Build Coastguard Worker }
118*c8dee2aaSAndroid Build Coastguard Worker
119*c8dee2aaSAndroid Build Coastguard Worker return std::make_unique<RasterBenchmarkTarget>(std::move(surfaceManager), benchmark);
120*c8dee2aaSAndroid Build Coastguard Worker }
121