xref: /aosp_15_r20/external/skia/tools/testrunners/benchmark/BazelBenchmarkTestRunner.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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 "include/core/SkBitmap.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkCanvas.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColorType.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkGraphics.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkStream.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkString.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSurface.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "include/encode/SkPngEncoder.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkAssert.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTArray.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkTime.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "src/utils/SkJSONWriter.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "src/utils/SkOSPath.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "tools/AutoreleasePool.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "tools/ProcStats.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "tools/Stats.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "tools/flags/CommandLineFlags.h"
26*c8dee2aaSAndroid Build Coastguard Worker #include "tools/testrunners/benchmark/target/BenchmarkTarget.h"
27*c8dee2aaSAndroid Build Coastguard Worker #include "tools/testrunners/common/TestRunner.h"
28*c8dee2aaSAndroid Build Coastguard Worker #include "tools/testrunners/common/compilation_mode_keys/CompilationModeKeys.h"
29*c8dee2aaSAndroid Build Coastguard Worker #include "tools/testrunners/common/surface_manager/SurfaceManager.h"
30*c8dee2aaSAndroid Build Coastguard Worker #include "tools/timer/Timer.h"
31*c8dee2aaSAndroid Build Coastguard Worker 
32*c8dee2aaSAndroid Build Coastguard Worker #include <cinttypes>
33*c8dee2aaSAndroid Build Coastguard Worker #include <sstream>
34*c8dee2aaSAndroid Build Coastguard Worker 
35*c8dee2aaSAndroid Build Coastguard Worker static DEFINE_string(skip, "", "Space-separated list of test cases (regexps) to skip.");
36*c8dee2aaSAndroid Build Coastguard Worker static DEFINE_string(
37*c8dee2aaSAndroid Build Coastguard Worker         match,
38*c8dee2aaSAndroid Build Coastguard Worker         "",
39*c8dee2aaSAndroid Build Coastguard Worker         "Space-separated list of test cases (regexps) to run. Will run all tests if omitted.");
40*c8dee2aaSAndroid Build Coastguard Worker 
41*c8dee2aaSAndroid Build Coastguard Worker // TODO(lovisolo): Should we check that this is a valid Git hash?
42*c8dee2aaSAndroid Build Coastguard Worker static DEFINE_string(
43*c8dee2aaSAndroid Build Coastguard Worker         gitHash,
44*c8dee2aaSAndroid Build Coastguard Worker         "",
45*c8dee2aaSAndroid Build Coastguard Worker         "Git hash to include in the results.json output file, which can be ingested by Perf.");
46*c8dee2aaSAndroid Build Coastguard Worker 
47*c8dee2aaSAndroid Build Coastguard Worker static DEFINE_string(issue,
48*c8dee2aaSAndroid Build Coastguard Worker                      "",
49*c8dee2aaSAndroid Build Coastguard Worker                      "Changelist ID (e.g. a Gerrit changelist number) to include in the "
50*c8dee2aaSAndroid Build Coastguard Worker                      "results.json output file, which can be ingested by Perf.");
51*c8dee2aaSAndroid Build Coastguard Worker 
52*c8dee2aaSAndroid Build Coastguard Worker static DEFINE_string(patchset,
53*c8dee2aaSAndroid Build Coastguard Worker                      "",
54*c8dee2aaSAndroid Build Coastguard Worker                      "Patchset ID (e.g. a Gerrit patchset number) to include in the results.json "
55*c8dee2aaSAndroid Build Coastguard Worker                      "output file, which can be ingested by Perf.");
56*c8dee2aaSAndroid Build Coastguard Worker 
57*c8dee2aaSAndroid Build Coastguard Worker static DEFINE_string(key,
58*c8dee2aaSAndroid Build Coastguard Worker                      "",
59*c8dee2aaSAndroid Build Coastguard Worker                      "Space-separated key/value pairs common to all benchmarks. These will be "
60*c8dee2aaSAndroid Build Coastguard Worker                      "included in the results.json output file, which can be ingested by Perf.");
61*c8dee2aaSAndroid Build Coastguard Worker 
62*c8dee2aaSAndroid Build Coastguard Worker static DEFINE_string(
63*c8dee2aaSAndroid Build Coastguard Worker         links,
64*c8dee2aaSAndroid Build Coastguard Worker         "",
65*c8dee2aaSAndroid Build Coastguard Worker         "Space-separated name/URL pairs with additional information about the benchmark execution, "
66*c8dee2aaSAndroid Build Coastguard Worker         "for example links to the Swarming bot and task pages, named \"swarming_bot\" and "
67*c8dee2aaSAndroid Build Coastguard Worker         "\"swarming_task\", respectively. These links are included in the "
68*c8dee2aaSAndroid Build Coastguard Worker         "results.json output file, which can be ingested by Perf.");
69*c8dee2aaSAndroid Build Coastguard Worker 
70*c8dee2aaSAndroid Build Coastguard Worker // When running under Bazel and overriding the output directory, you might encounter errors
71*c8dee2aaSAndroid Build Coastguard Worker // such as "No such file or directory" and "Read-only file system". The former can happen
72*c8dee2aaSAndroid Build Coastguard Worker // when running on RBE because the passed in output dir might not exist on the remote
73*c8dee2aaSAndroid Build Coastguard Worker // worker, whereas the latter can happen when running locally in sandboxed mode, which is
74*c8dee2aaSAndroid Build Coastguard Worker // the default strategy when running outside of RBE. One possible workaround is to run the
75*c8dee2aaSAndroid Build Coastguard Worker // test as a local subprocess, which can be done by passing flag --strategy=TestRunner=local
76*c8dee2aaSAndroid Build Coastguard Worker // to Bazel.
77*c8dee2aaSAndroid Build Coastguard Worker //
78*c8dee2aaSAndroid Build Coastguard Worker // Reference: https://bazel.build/docs/user-manual#execution-strategy.
79*c8dee2aaSAndroid Build Coastguard Worker static DEFINE_string(outputDir,
80*c8dee2aaSAndroid Build Coastguard Worker                      "",
81*c8dee2aaSAndroid Build Coastguard Worker                      "Directory where to write any output JSON and PNG files. "
82*c8dee2aaSAndroid Build Coastguard Worker                      "Optional when running under Bazel "
83*c8dee2aaSAndroid Build Coastguard Worker                      "(e.g. \"bazel test //path/to:test\") as it defaults to "
84*c8dee2aaSAndroid Build Coastguard Worker                      "$TEST_UNDECLARED_OUTPUTS_DIR.");
85*c8dee2aaSAndroid Build Coastguard Worker 
86*c8dee2aaSAndroid Build Coastguard Worker static DEFINE_string(surfaceConfig,
87*c8dee2aaSAndroid Build Coastguard Worker                      "",
88*c8dee2aaSAndroid Build Coastguard Worker                      "Name of the Surface configuration to use (e.g. \"8888\"). This determines "
89*c8dee2aaSAndroid Build Coastguard Worker                      "how we construct the SkSurface from which we get the SkCanvas that "
90*c8dee2aaSAndroid Build Coastguard Worker                      "benchmarks will draw on. See file "
91*c8dee2aaSAndroid Build Coastguard Worker                      "//tools/testrunners/common/surface_manager/SurfaceManager.h for details.");
92*c8dee2aaSAndroid Build Coastguard Worker 
93*c8dee2aaSAndroid Build Coastguard Worker static DEFINE_string(
94*c8dee2aaSAndroid Build Coastguard Worker         cpuName,
95*c8dee2aaSAndroid Build Coastguard Worker         "",
96*c8dee2aaSAndroid Build Coastguard Worker         "Contents of the \"cpu_or_gpu_value\" dimension for CPU-bound traces (e.g. \"AVX512\").");
97*c8dee2aaSAndroid Build Coastguard Worker 
98*c8dee2aaSAndroid Build Coastguard Worker static DEFINE_string(
99*c8dee2aaSAndroid Build Coastguard Worker         gpuName,
100*c8dee2aaSAndroid Build Coastguard Worker         "",
101*c8dee2aaSAndroid Build Coastguard Worker         "Contents of the \"cpu_or_gpu_value\" dimension for GPU-bound traces (e.g. \"RTX3060\").");
102*c8dee2aaSAndroid Build Coastguard Worker 
103*c8dee2aaSAndroid Build Coastguard Worker static DEFINE_bool(
104*c8dee2aaSAndroid Build Coastguard Worker         writePNGs,
105*c8dee2aaSAndroid Build Coastguard Worker         false,
106*c8dee2aaSAndroid Build Coastguard Worker         "Whether or not to write to the output directory any bitmaps produced by benchmarks.");
107*c8dee2aaSAndroid Build Coastguard Worker 
108*c8dee2aaSAndroid Build Coastguard Worker // Mutually exclusive with --autoTuneLoops.
109*c8dee2aaSAndroid Build Coastguard Worker static DEFINE_int(loops, 0, "The number of benchmark runs that constitutes a single sample.");
110*c8dee2aaSAndroid Build Coastguard Worker 
111*c8dee2aaSAndroid Build Coastguard Worker // Mutually exclusive with --loops.
112*c8dee2aaSAndroid Build Coastguard Worker static DEFINE_bool(autoTuneLoops,
113*c8dee2aaSAndroid Build Coastguard Worker                    false,
114*c8dee2aaSAndroid Build Coastguard Worker                    "Auto-tune (automatically determine) the number of benchmark runs that "
115*c8dee2aaSAndroid Build Coastguard Worker                    "constitutes a single sample. Timings are only reported when auto-tuning.");
116*c8dee2aaSAndroid Build Coastguard Worker 
117*c8dee2aaSAndroid Build Coastguard Worker static DEFINE_int(
118*c8dee2aaSAndroid Build Coastguard Worker         autoTuneLoopsMax,
119*c8dee2aaSAndroid Build Coastguard Worker         1000000,
120*c8dee2aaSAndroid Build Coastguard Worker         "Maximum number of benchmark runs per single sample when auto-tuning. Ignored unless flag "
121*c8dee2aaSAndroid Build Coastguard Worker         "--autoTuneLoops is set.");
122*c8dee2aaSAndroid Build Coastguard Worker 
123*c8dee2aaSAndroid Build Coastguard Worker // Mutually exclusive with --ms.
124*c8dee2aaSAndroid Build Coastguard Worker static DEFINE_int(samples, 10, "Number of samples to collect for each benchmark.");
125*c8dee2aaSAndroid Build Coastguard Worker 
126*c8dee2aaSAndroid Build Coastguard Worker // Mutually exclusive with --samples.
127*c8dee2aaSAndroid Build Coastguard Worker static DEFINE_int(ms, 0, "For each benchmark, collect samples for this many milliseconds.");
128*c8dee2aaSAndroid Build Coastguard Worker 
129*c8dee2aaSAndroid Build Coastguard Worker static DEFINE_int(flushEvery,
130*c8dee2aaSAndroid Build Coastguard Worker                   10,
131*c8dee2aaSAndroid Build Coastguard Worker                   "Flush the results.json output file every n-th run. This file "
132*c8dee2aaSAndroid Build Coastguard Worker                   "can be ingested by Perf.");
133*c8dee2aaSAndroid Build Coastguard Worker 
134*c8dee2aaSAndroid Build Coastguard Worker static DEFINE_bool(csv, false, "Print status in CSV format.");
135*c8dee2aaSAndroid Build Coastguard Worker 
136*c8dee2aaSAndroid Build Coastguard Worker static DEFINE_bool2(quiet, q, false, "if true, do not print status updates.");
137*c8dee2aaSAndroid Build Coastguard Worker 
138*c8dee2aaSAndroid Build Coastguard Worker static DEFINE_bool2(verbose, v, false, "Enable verbose output from the test runner.");
139*c8dee2aaSAndroid Build Coastguard Worker 
140*c8dee2aaSAndroid Build Coastguard Worker // Set in //bazel/devicesrc but only consumed by adb_test_runner.go. We cannot use the
141*c8dee2aaSAndroid Build Coastguard Worker // DEFINE_string macro because the flag name includes dashes.
142*c8dee2aaSAndroid Build Coastguard Worker [[maybe_unused]] static bool unused =
143*c8dee2aaSAndroid Build Coastguard Worker         SkFlagInfo::CreateStringFlag("device-specific-bazel-config",
144*c8dee2aaSAndroid Build Coastguard Worker                                      nullptr,
145*c8dee2aaSAndroid Build Coastguard Worker                                      new CommandLineFlags::StringArray(),
146*c8dee2aaSAndroid Build Coastguard Worker                                      nullptr,
147*c8dee2aaSAndroid Build Coastguard Worker                                      "Ignored by this test runner.",
148*c8dee2aaSAndroid Build Coastguard Worker                                      nullptr);
149*c8dee2aaSAndroid Build Coastguard Worker 
validate_flags(bool isBazelTest)150*c8dee2aaSAndroid Build Coastguard Worker static void validate_flags(bool isBazelTest) {
151*c8dee2aaSAndroid Build Coastguard Worker     TestRunner::FlagValidators::AllOrNone(
152*c8dee2aaSAndroid Build Coastguard Worker             {{"--issue", FLAGS_issue.size() > 0}, {"--patchset", FLAGS_patchset.size() > 0}});
153*c8dee2aaSAndroid Build Coastguard Worker     TestRunner::FlagValidators::StringAtMostOne("--issue", FLAGS_issue);
154*c8dee2aaSAndroid Build Coastguard Worker     TestRunner::FlagValidators::StringAtMostOne("--patchset", FLAGS_patchset);
155*c8dee2aaSAndroid Build Coastguard Worker     TestRunner::FlagValidators::StringEven("--key", FLAGS_key);
156*c8dee2aaSAndroid Build Coastguard Worker     TestRunner::FlagValidators::StringEven("--links", FLAGS_links);
157*c8dee2aaSAndroid Build Coastguard Worker 
158*c8dee2aaSAndroid Build Coastguard Worker     if (!isBazelTest) {
159*c8dee2aaSAndroid Build Coastguard Worker         TestRunner::FlagValidators::StringNonEmpty("--outputDir", FLAGS_outputDir);
160*c8dee2aaSAndroid Build Coastguard Worker     }
161*c8dee2aaSAndroid Build Coastguard Worker     TestRunner::FlagValidators::StringAtMostOne("--outputDir", FLAGS_outputDir);
162*c8dee2aaSAndroid Build Coastguard Worker 
163*c8dee2aaSAndroid Build Coastguard Worker     TestRunner::FlagValidators::StringNonEmpty("--surfaceConfig", FLAGS_surfaceConfig);
164*c8dee2aaSAndroid Build Coastguard Worker     TestRunner::FlagValidators::StringAtMostOne("--surfaceConfig", FLAGS_surfaceConfig);
165*c8dee2aaSAndroid Build Coastguard Worker 
166*c8dee2aaSAndroid Build Coastguard Worker     TestRunner::FlagValidators::StringAtMostOne("--cpuName", FLAGS_cpuName);
167*c8dee2aaSAndroid Build Coastguard Worker     TestRunner::FlagValidators::StringAtMostOne("--gpuName", FLAGS_gpuName);
168*c8dee2aaSAndroid Build Coastguard Worker 
169*c8dee2aaSAndroid Build Coastguard Worker     TestRunner::FlagValidators::ExactlyOne(
170*c8dee2aaSAndroid Build Coastguard Worker             {{"--loops", FLAGS_loops != 0}, {"--autoTuneLoops", FLAGS_autoTuneLoops}});
171*c8dee2aaSAndroid Build Coastguard Worker     if (!FLAGS_autoTuneLoops) {
172*c8dee2aaSAndroid Build Coastguard Worker         TestRunner::FlagValidators::IntGreaterOrEqual("--loops", FLAGS_loops, 1);
173*c8dee2aaSAndroid Build Coastguard Worker     }
174*c8dee2aaSAndroid Build Coastguard Worker 
175*c8dee2aaSAndroid Build Coastguard Worker     TestRunner::FlagValidators::IntGreaterOrEqual("--autoTuneLoopsMax", FLAGS_autoTuneLoopsMax, 1);
176*c8dee2aaSAndroid Build Coastguard Worker 
177*c8dee2aaSAndroid Build Coastguard Worker     TestRunner::FlagValidators::ExactlyOne(
178*c8dee2aaSAndroid Build Coastguard Worker             {{"--samples", FLAGS_samples != 0}, {"--ms", FLAGS_ms != 0}});
179*c8dee2aaSAndroid Build Coastguard Worker     if (FLAGS_ms == 0) {
180*c8dee2aaSAndroid Build Coastguard Worker         TestRunner::FlagValidators::IntGreaterOrEqual("--samples", FLAGS_samples, 1);
181*c8dee2aaSAndroid Build Coastguard Worker     }
182*c8dee2aaSAndroid Build Coastguard Worker     if (FLAGS_samples == 0) {
183*c8dee2aaSAndroid Build Coastguard Worker         TestRunner::FlagValidators::IntGreaterOrEqual("--ms", FLAGS_ms, 1);
184*c8dee2aaSAndroid Build Coastguard Worker     }
185*c8dee2aaSAndroid Build Coastguard Worker }
186*c8dee2aaSAndroid Build Coastguard Worker 
187*c8dee2aaSAndroid Build Coastguard Worker // Helper class to produce JSON files in Perf ingestion format. The format is determined by Perf's
188*c8dee2aaSAndroid Build Coastguard Worker // format.Format Go struct:
189*c8dee2aaSAndroid Build Coastguard Worker //
190*c8dee2aaSAndroid Build Coastguard Worker // https://skia.googlesource.com/buildbot/+/e12f70e0a3249af6dd7754d55958ee64a22e0957/perf/go/ingest/format/format.go#168
191*c8dee2aaSAndroid Build Coastguard Worker //
192*c8dee2aaSAndroid Build Coastguard Worker // Note that the JSON format produced by this class differs from Nanobench. The latter follows
193*c8dee2aaSAndroid Build Coastguard Worker // Perf's legacy format, which is determined by the format.BenchData Go struct:
194*c8dee2aaSAndroid Build Coastguard Worker //
195*c8dee2aaSAndroid Build Coastguard Worker // https://skia.googlesource.com/buildbot/+/e12f70e0a3249af6dd7754d55958ee64a22e0957/perf/go/ingest/format/leagacyformat.go#26
196*c8dee2aaSAndroid Build Coastguard Worker class ResultsJSONWriter {
197*c8dee2aaSAndroid Build Coastguard Worker public:
198*c8dee2aaSAndroid Build Coastguard Worker     // This struct mirrors Perf's format.SingleMeasurement Go struct:
199*c8dee2aaSAndroid Build Coastguard Worker     // https://skia.googlesource.com/buildbot/+/e12f70e0a3249af6dd7754d55958ee64a22e0957/perf/go/ingest/format/format.go#31.
200*c8dee2aaSAndroid Build Coastguard Worker     struct SingleMeasurement {
201*c8dee2aaSAndroid Build Coastguard Worker         std::string value;
202*c8dee2aaSAndroid Build Coastguard Worker         double measurement;
203*c8dee2aaSAndroid Build Coastguard Worker     };
204*c8dee2aaSAndroid Build Coastguard Worker 
205*c8dee2aaSAndroid Build Coastguard Worker     // This struct mirrors Perf's format.Result Go struct:
206*c8dee2aaSAndroid Build Coastguard Worker     // https://skia.googlesource.com/buildbot/+/e12f70e0a3249af6dd7754d55958ee64a22e0957/perf/go/ingest/format/format.go#69.
207*c8dee2aaSAndroid Build Coastguard Worker     //
208*c8dee2aaSAndroid Build Coastguard Worker     // Note that the format.Result Go struct supports either one single measurement, or multiple
209*c8dee2aaSAndroid Build Coastguard Worker     // measurements represented as a dictionary from arbitrary string keys to an array of
210*c8dee2aaSAndroid Build Coastguard Worker     // format.SingleMeasurement Go structs. This class focuses on the latter variant.
211*c8dee2aaSAndroid Build Coastguard Worker     struct Result {
212*c8dee2aaSAndroid Build Coastguard Worker         std::map<std::string, std::string> key;
213*c8dee2aaSAndroid Build Coastguard Worker         std::map<std::string, std::vector<SingleMeasurement>> measurements;
214*c8dee2aaSAndroid Build Coastguard Worker     };
215*c8dee2aaSAndroid Build Coastguard Worker 
ResultsJSONWriter(const char * path)216*c8dee2aaSAndroid Build Coastguard Worker     ResultsJSONWriter(const char* path)
217*c8dee2aaSAndroid Build Coastguard Worker             : fFileWStream(path)
218*c8dee2aaSAndroid Build Coastguard Worker             , fJson(&fFileWStream, SkJSONWriter::Mode::kPretty)
219*c8dee2aaSAndroid Build Coastguard Worker             , fAddingResults(false) {
220*c8dee2aaSAndroid Build Coastguard Worker         fJson.beginObject();  // Root object.
221*c8dee2aaSAndroid Build Coastguard Worker         fJson.appendS32("version", 1);
222*c8dee2aaSAndroid Build Coastguard Worker     }
223*c8dee2aaSAndroid Build Coastguard Worker 
addGitHash(std::string gitHash)224*c8dee2aaSAndroid Build Coastguard Worker     void addGitHash(std::string gitHash) {
225*c8dee2aaSAndroid Build Coastguard Worker         assertNotAddingResults();
226*c8dee2aaSAndroid Build Coastguard Worker         fJson.appendCString("git_hash", gitHash.c_str());
227*c8dee2aaSAndroid Build Coastguard Worker     }
228*c8dee2aaSAndroid Build Coastguard Worker 
addChangelistInfo(std::string issue,std::string patchset)229*c8dee2aaSAndroid Build Coastguard Worker     void addChangelistInfo(std::string issue, std::string patchset) {
230*c8dee2aaSAndroid Build Coastguard Worker         assertNotAddingResults();
231*c8dee2aaSAndroid Build Coastguard Worker         fJson.appendCString("issue", issue.c_str());
232*c8dee2aaSAndroid Build Coastguard Worker         fJson.appendCString("patchset", patchset.c_str());
233*c8dee2aaSAndroid Build Coastguard Worker     }
234*c8dee2aaSAndroid Build Coastguard Worker 
addKey(std::map<std::string,std::string> key)235*c8dee2aaSAndroid Build Coastguard Worker     void addKey(std::map<std::string, std::string> key) {
236*c8dee2aaSAndroid Build Coastguard Worker         assertNotAddingResults();
237*c8dee2aaSAndroid Build Coastguard Worker         fJson.beginObject("key");
238*c8dee2aaSAndroid Build Coastguard Worker         for (auto const& [name, value] : key) {
239*c8dee2aaSAndroid Build Coastguard Worker             fJson.appendCString(name.c_str(), value.c_str());
240*c8dee2aaSAndroid Build Coastguard Worker         }
241*c8dee2aaSAndroid Build Coastguard Worker         fJson.endObject();
242*c8dee2aaSAndroid Build Coastguard Worker     }
243*c8dee2aaSAndroid Build Coastguard Worker 
addLinks(std::map<std::string,std::string> links)244*c8dee2aaSAndroid Build Coastguard Worker     void addLinks(std::map<std::string, std::string> links) {
245*c8dee2aaSAndroid Build Coastguard Worker         assertNotAddingResults();
246*c8dee2aaSAndroid Build Coastguard Worker         fJson.beginObject("links");
247*c8dee2aaSAndroid Build Coastguard Worker         for (auto const& [key, value] : links) {
248*c8dee2aaSAndroid Build Coastguard Worker             fJson.appendCString(key.c_str(), value.c_str());
249*c8dee2aaSAndroid Build Coastguard Worker         }
250*c8dee2aaSAndroid Build Coastguard Worker         fJson.endObject();
251*c8dee2aaSAndroid Build Coastguard Worker     }
252*c8dee2aaSAndroid Build Coastguard Worker 
addResult(Result result)253*c8dee2aaSAndroid Build Coastguard Worker     void addResult(Result result) {
254*c8dee2aaSAndroid Build Coastguard Worker         if (!fAddingResults) {
255*c8dee2aaSAndroid Build Coastguard Worker             fAddingResults = true;
256*c8dee2aaSAndroid Build Coastguard Worker             fJson.beginArray("results");  // "results" array.
257*c8dee2aaSAndroid Build Coastguard Worker         }
258*c8dee2aaSAndroid Build Coastguard Worker 
259*c8dee2aaSAndroid Build Coastguard Worker         fJson.beginObject();  // Result object.
260*c8dee2aaSAndroid Build Coastguard Worker 
261*c8dee2aaSAndroid Build Coastguard Worker         // Key.
262*c8dee2aaSAndroid Build Coastguard Worker         fJson.beginObject("key");  // "key" dictionary.
263*c8dee2aaSAndroid Build Coastguard Worker         for (auto const& [name, value] : result.key) {
264*c8dee2aaSAndroid Build Coastguard Worker             fJson.appendCString(name.c_str(), value.c_str());
265*c8dee2aaSAndroid Build Coastguard Worker         }
266*c8dee2aaSAndroid Build Coastguard Worker         fJson.endObject();  // "key" dictionary.
267*c8dee2aaSAndroid Build Coastguard Worker 
268*c8dee2aaSAndroid Build Coastguard Worker         // Measurements.
269*c8dee2aaSAndroid Build Coastguard Worker         fJson.beginObject("measurements");  // "measurements" dictionary.
270*c8dee2aaSAndroid Build Coastguard Worker         for (auto const& [name, singleMeasurements] : result.measurements) {
271*c8dee2aaSAndroid Build Coastguard Worker             fJson.beginArray(name.c_str());  // <name> array.
272*c8dee2aaSAndroid Build Coastguard Worker             for (const SingleMeasurement& singleMeasurement : singleMeasurements) {
273*c8dee2aaSAndroid Build Coastguard Worker                 // Based on
274*c8dee2aaSAndroid Build Coastguard Worker                 // https://skia.googlesource.com/skia/+/a063eaeaf1e09e4d6f42e0f44a5723622a46d21c/bench/ResultsWriter.h#51.
275*c8dee2aaSAndroid Build Coastguard Worker                 //
276*c8dee2aaSAndroid Build Coastguard Worker                 // Don't record if NaN or Inf.
277*c8dee2aaSAndroid Build Coastguard Worker                 if (SkIsFinite(singleMeasurement.measurement)) {
278*c8dee2aaSAndroid Build Coastguard Worker                     fJson.beginObject();
279*c8dee2aaSAndroid Build Coastguard Worker                     fJson.appendCString("value", singleMeasurement.value.c_str());
280*c8dee2aaSAndroid Build Coastguard Worker                     fJson.appendDoubleDigits("measurement", singleMeasurement.measurement, 16);
281*c8dee2aaSAndroid Build Coastguard Worker                     fJson.endObject();
282*c8dee2aaSAndroid Build Coastguard Worker                 }
283*c8dee2aaSAndroid Build Coastguard Worker             }
284*c8dee2aaSAndroid Build Coastguard Worker             fJson.endArray();  // <name> array.
285*c8dee2aaSAndroid Build Coastguard Worker         }
286*c8dee2aaSAndroid Build Coastguard Worker         fJson.endObject();  // "measurements" dictionary.
287*c8dee2aaSAndroid Build Coastguard Worker 
288*c8dee2aaSAndroid Build Coastguard Worker         fJson.endObject();  // Result object.
289*c8dee2aaSAndroid Build Coastguard Worker     }
290*c8dee2aaSAndroid Build Coastguard Worker 
flush()291*c8dee2aaSAndroid Build Coastguard Worker     void flush() { fJson.flush(); }
292*c8dee2aaSAndroid Build Coastguard Worker 
~ResultsJSONWriter()293*c8dee2aaSAndroid Build Coastguard Worker     ~ResultsJSONWriter() {
294*c8dee2aaSAndroid Build Coastguard Worker         if (fAddingResults) {
295*c8dee2aaSAndroid Build Coastguard Worker             fJson.endArray();  // "results" array;
296*c8dee2aaSAndroid Build Coastguard Worker         }
297*c8dee2aaSAndroid Build Coastguard Worker         fJson.endObject();  // Root object.
298*c8dee2aaSAndroid Build Coastguard Worker     }
299*c8dee2aaSAndroid Build Coastguard Worker 
300*c8dee2aaSAndroid Build Coastguard Worker private:
assertNotAddingResults()301*c8dee2aaSAndroid Build Coastguard Worker     void assertNotAddingResults() {
302*c8dee2aaSAndroid Build Coastguard Worker         if (fAddingResults) {
303*c8dee2aaSAndroid Build Coastguard Worker             SK_ABORT("Cannot perform this operation after addResults() is called.");
304*c8dee2aaSAndroid Build Coastguard Worker         }
305*c8dee2aaSAndroid Build Coastguard Worker     }
306*c8dee2aaSAndroid Build Coastguard Worker 
307*c8dee2aaSAndroid Build Coastguard Worker     SkFILEWStream fFileWStream;
308*c8dee2aaSAndroid Build Coastguard Worker     SkJSONWriter fJson;
309*c8dee2aaSAndroid Build Coastguard Worker     bool fAddingResults;
310*c8dee2aaSAndroid Build Coastguard Worker };
311*c8dee2aaSAndroid Build Coastguard Worker 
312*c8dee2aaSAndroid Build Coastguard Worker // Manages an autorelease pool for Metal. On other platforms, pool.drain() is a no-op.
313*c8dee2aaSAndroid Build Coastguard Worker AutoreleasePool pool;
314*c8dee2aaSAndroid Build Coastguard Worker 
now_ms()315*c8dee2aaSAndroid Build Coastguard Worker static double now_ms() { return SkTime::GetNSecs() * 1e-6; }
316*c8dee2aaSAndroid Build Coastguard Worker 
317*c8dee2aaSAndroid Build Coastguard Worker // Based on
318*c8dee2aaSAndroid Build Coastguard Worker // https://skia.googlesource.com/skia/+/a063eaeaf1e09e4d6f42e0f44a5723622a46d21c/bench/nanobench.cpp#1503.
warm_up_test_runner_once(BenchmarkTarget * target,int loops)319*c8dee2aaSAndroid Build Coastguard Worker static void warm_up_test_runner_once(BenchmarkTarget* target, int loops) {
320*c8dee2aaSAndroid Build Coastguard Worker     static bool warm = false;
321*c8dee2aaSAndroid Build Coastguard Worker     if (warm) {
322*c8dee2aaSAndroid Build Coastguard Worker         return;
323*c8dee2aaSAndroid Build Coastguard Worker     }
324*c8dee2aaSAndroid Build Coastguard Worker     if (FLAGS_ms < 1000) {
325*c8dee2aaSAndroid Build Coastguard Worker         // Run the first bench for 1000ms to warm up the test runner if FLAGS_ms < 1000.
326*c8dee2aaSAndroid Build Coastguard Worker         // Otherwise, the first few benches' measurements will be inaccurate.
327*c8dee2aaSAndroid Build Coastguard Worker         auto stop = now_ms() + 1000;
328*c8dee2aaSAndroid Build Coastguard Worker         do {
329*c8dee2aaSAndroid Build Coastguard Worker             target->time(loops);
330*c8dee2aaSAndroid Build Coastguard Worker             pool.drain();
331*c8dee2aaSAndroid Build Coastguard Worker         } while (now_ms() < stop);
332*c8dee2aaSAndroid Build Coastguard Worker     }
333*c8dee2aaSAndroid Build Coastguard Worker     warm = true;
334*c8dee2aaSAndroid Build Coastguard Worker }
335*c8dee2aaSAndroid Build Coastguard Worker 
336*c8dee2aaSAndroid Build Coastguard Worker // Collects samples for the given benchmark. Returns a boolean indicating success or failure, the
337*c8dee2aaSAndroid Build Coastguard Worker // number of benchmark runs used for each sample, the samples and any statistics produced by the
338*c8dee2aaSAndroid Build Coastguard Worker // benchmark and/or target.
339*c8dee2aaSAndroid Build Coastguard Worker //
340*c8dee2aaSAndroid Build Coastguard Worker // Based on
341*c8dee2aaSAndroid Build Coastguard Worker // https://skia.googlesource.com/skia/+/a063eaeaf1e09e4d6f42e0f44a5723622a46d21c/bench/nanobench.cpp#1489.
sample_benchmark(BenchmarkTarget * target,int * loops,skia_private::TArray<double> * samples,skia_private::TArray<SkString> * statKeys,skia_private::TArray<double> * statValues)342*c8dee2aaSAndroid Build Coastguard Worker static int sample_benchmark(BenchmarkTarget* target,
343*c8dee2aaSAndroid Build Coastguard Worker                             int* loops,
344*c8dee2aaSAndroid Build Coastguard Worker                             skia_private::TArray<double>* samples,
345*c8dee2aaSAndroid Build Coastguard Worker                             skia_private::TArray<SkString>* statKeys,
346*c8dee2aaSAndroid Build Coastguard Worker                             skia_private::TArray<double>* statValues) {
347*c8dee2aaSAndroid Build Coastguard Worker     target->setup();
348*c8dee2aaSAndroid Build Coastguard Worker 
349*c8dee2aaSAndroid Build Coastguard Worker     if (FLAGS_autoTuneLoops) {
350*c8dee2aaSAndroid Build Coastguard Worker         auto [autoTunedLoops, ok] = target->autoTuneLoops();
351*c8dee2aaSAndroid Build Coastguard Worker         if (!ok) {
352*c8dee2aaSAndroid Build Coastguard Worker             // Can't be timed. A warning has already been printed.
353*c8dee2aaSAndroid Build Coastguard Worker             target->tearDown();
354*c8dee2aaSAndroid Build Coastguard Worker             return false;
355*c8dee2aaSAndroid Build Coastguard Worker         }
356*c8dee2aaSAndroid Build Coastguard Worker         *loops = autoTunedLoops;
357*c8dee2aaSAndroid Build Coastguard Worker         if (*loops > FLAGS_autoTuneLoopsMax) {
358*c8dee2aaSAndroid Build Coastguard Worker             TestRunner::Log(
359*c8dee2aaSAndroid Build Coastguard Worker                     "Warning: Clamping loops from %d to %d (per the --autoTuneLoopsMax flag) for "
360*c8dee2aaSAndroid Build Coastguard Worker                     "benchmark \"%s\".",
361*c8dee2aaSAndroid Build Coastguard Worker                     *loops,
362*c8dee2aaSAndroid Build Coastguard Worker                     FLAGS_autoTuneLoopsMax,
363*c8dee2aaSAndroid Build Coastguard Worker                     target->getBenchmark()->getUniqueName());
364*c8dee2aaSAndroid Build Coastguard Worker             *loops = FLAGS_autoTuneLoopsMax;
365*c8dee2aaSAndroid Build Coastguard Worker         }
366*c8dee2aaSAndroid Build Coastguard Worker     } else {
367*c8dee2aaSAndroid Build Coastguard Worker         *loops = FLAGS_loops;
368*c8dee2aaSAndroid Build Coastguard Worker     }
369*c8dee2aaSAndroid Build Coastguard Worker 
370*c8dee2aaSAndroid Build Coastguard Worker     // Warm up the test runner to increase the chances of getting consistent measurements. Only
371*c8dee2aaSAndroid Build Coastguard Worker     // done once for the entire lifecycle of the test runner.
372*c8dee2aaSAndroid Build Coastguard Worker     warm_up_test_runner_once(target, *loops);
373*c8dee2aaSAndroid Build Coastguard Worker 
374*c8dee2aaSAndroid Build Coastguard Worker     // Each individual BenchmarkTarget must also be warmed up.
375*c8dee2aaSAndroid Build Coastguard Worker     target->warmUp(*loops);
376*c8dee2aaSAndroid Build Coastguard Worker 
377*c8dee2aaSAndroid Build Coastguard Worker     if (FLAGS_ms) {
378*c8dee2aaSAndroid Build Coastguard Worker         // Collect as many samples as possible for a specific duration of time.
379*c8dee2aaSAndroid Build Coastguard Worker         auto stop = now_ms() + FLAGS_ms;
380*c8dee2aaSAndroid Build Coastguard Worker         do {
381*c8dee2aaSAndroid Build Coastguard Worker             samples->push_back(target->time(*loops) / *loops);
382*c8dee2aaSAndroid Build Coastguard Worker             pool.drain();
383*c8dee2aaSAndroid Build Coastguard Worker         } while (now_ms() < stop);
384*c8dee2aaSAndroid Build Coastguard Worker     } else {
385*c8dee2aaSAndroid Build Coastguard Worker         // Collect an exact number of samples.
386*c8dee2aaSAndroid Build Coastguard Worker         samples->reset(FLAGS_samples);
387*c8dee2aaSAndroid Build Coastguard Worker         for (int s = 0; s < FLAGS_samples; s++) {
388*c8dee2aaSAndroid Build Coastguard Worker             (*samples)[s] = target->time(*loops) / *loops;
389*c8dee2aaSAndroid Build Coastguard Worker             pool.drain();
390*c8dee2aaSAndroid Build Coastguard Worker         }
391*c8dee2aaSAndroid Build Coastguard Worker     }
392*c8dee2aaSAndroid Build Coastguard Worker 
393*c8dee2aaSAndroid Build Coastguard Worker     // Scale each sample to the benchmark's own units, time/unit.
394*c8dee2aaSAndroid Build Coastguard Worker     for (double& sample : *samples) {
395*c8dee2aaSAndroid Build Coastguard Worker         sample *= (1.0 / target->getBenchmark()->getUnits());
396*c8dee2aaSAndroid Build Coastguard Worker     }
397*c8dee2aaSAndroid Build Coastguard Worker 
398*c8dee2aaSAndroid Build Coastguard Worker     target->dumpStats(statKeys, statValues);
399*c8dee2aaSAndroid Build Coastguard Worker     target->tearDown();
400*c8dee2aaSAndroid Build Coastguard Worker 
401*c8dee2aaSAndroid Build Coastguard Worker     return true;
402*c8dee2aaSAndroid Build Coastguard Worker }
403*c8dee2aaSAndroid Build Coastguard Worker 
404*c8dee2aaSAndroid Build Coastguard Worker // Based on
405*c8dee2aaSAndroid Build Coastguard Worker // https://skia.googlesource.com/skia/+/a063eaeaf1e09e4d6f42e0f44a5723622a46d21c/bench/nanobench.cpp#432.
maybe_write_png(BenchmarkTarget * target,std::string outputDir)406*c8dee2aaSAndroid Build Coastguard Worker static void maybe_write_png(BenchmarkTarget* target, std::string outputDir) {
407*c8dee2aaSAndroid Build Coastguard Worker     if (target->getBackend() == Benchmark::Backend::kNonRendering) {
408*c8dee2aaSAndroid Build Coastguard Worker         return;
409*c8dee2aaSAndroid Build Coastguard Worker     }
410*c8dee2aaSAndroid Build Coastguard Worker 
411*c8dee2aaSAndroid Build Coastguard Worker     SkString filename = SkStringPrintf("%s.png", target->getBenchmark()->getUniqueName());
412*c8dee2aaSAndroid Build Coastguard Worker     filename = SkOSPath::Join(outputDir.c_str(), filename.c_str());
413*c8dee2aaSAndroid Build Coastguard Worker 
414*c8dee2aaSAndroid Build Coastguard Worker     if (!target->getCanvas() ||
415*c8dee2aaSAndroid Build Coastguard Worker         target->getCanvas()->imageInfo().colorType() == kUnknown_SkColorType) {
416*c8dee2aaSAndroid Build Coastguard Worker         return;
417*c8dee2aaSAndroid Build Coastguard Worker     }
418*c8dee2aaSAndroid Build Coastguard Worker 
419*c8dee2aaSAndroid Build Coastguard Worker     SkBitmap bmp;
420*c8dee2aaSAndroid Build Coastguard Worker     bmp.allocPixels(target->getCanvas()->imageInfo());
421*c8dee2aaSAndroid Build Coastguard Worker     if (!target->getCanvas()->readPixels(bmp, 0, 0)) {
422*c8dee2aaSAndroid Build Coastguard Worker         TestRunner::Log("Warning: Could not read canvas pixels for benchmark \"%s\".",
423*c8dee2aaSAndroid Build Coastguard Worker                         target->getBenchmark()->getUniqueName());
424*c8dee2aaSAndroid Build Coastguard Worker         return;
425*c8dee2aaSAndroid Build Coastguard Worker     }
426*c8dee2aaSAndroid Build Coastguard Worker 
427*c8dee2aaSAndroid Build Coastguard Worker     SkFILEWStream stream(filename.c_str());
428*c8dee2aaSAndroid Build Coastguard Worker     if (!stream.isValid()) {
429*c8dee2aaSAndroid Build Coastguard Worker         TestRunner::Log("Warning: Could not write file \"%s\".", filename.c_str());
430*c8dee2aaSAndroid Build Coastguard Worker         return;
431*c8dee2aaSAndroid Build Coastguard Worker     }
432*c8dee2aaSAndroid Build Coastguard Worker     if (!SkPngEncoder::Encode(&stream, bmp.pixmap(), {})) {
433*c8dee2aaSAndroid Build Coastguard Worker         TestRunner::Log("Warning: Could not encode pixels from benchmark \"%s\" as PNG.",
434*c8dee2aaSAndroid Build Coastguard Worker                         target->getBenchmark()->getUniqueName());
435*c8dee2aaSAndroid Build Coastguard Worker         return;
436*c8dee2aaSAndroid Build Coastguard Worker     }
437*c8dee2aaSAndroid Build Coastguard Worker 
438*c8dee2aaSAndroid Build Coastguard Worker     if (FLAGS_verbose) {
439*c8dee2aaSAndroid Build Coastguard Worker         TestRunner::Log("PNG for benchmark \"%s\" written to: %s",
440*c8dee2aaSAndroid Build Coastguard Worker                         target->getBenchmark()->getUniqueName(),
441*c8dee2aaSAndroid Build Coastguard Worker                         filename.c_str());
442*c8dee2aaSAndroid Build Coastguard Worker     }
443*c8dee2aaSAndroid Build Coastguard Worker }
444*c8dee2aaSAndroid Build Coastguard Worker 
445*c8dee2aaSAndroid Build Coastguard Worker // Non-static because it is used from RasterBenchmarkTarget.cpp.
humanize(double ms)446*c8dee2aaSAndroid Build Coastguard Worker SkString humanize(double ms) {
447*c8dee2aaSAndroid Build Coastguard Worker     if (FLAGS_verbose) return SkStringPrintf("%" PRIu64, (uint64_t)(ms * 1e6));
448*c8dee2aaSAndroid Build Coastguard Worker     return HumanizeMs(ms);
449*c8dee2aaSAndroid Build Coastguard Worker }
450*c8dee2aaSAndroid Build Coastguard Worker 
451*c8dee2aaSAndroid Build Coastguard Worker #define HUMANIZE(ms) humanize(ms).c_str()
452*c8dee2aaSAndroid Build Coastguard Worker 
to_string(int n)453*c8dee2aaSAndroid Build Coastguard Worker static SkString to_string(int n) {
454*c8dee2aaSAndroid Build Coastguard Worker     SkString str;
455*c8dee2aaSAndroid Build Coastguard Worker     str.appendS32(n);
456*c8dee2aaSAndroid Build Coastguard Worker     return str;
457*c8dee2aaSAndroid Build Coastguard Worker }
458*c8dee2aaSAndroid Build Coastguard Worker 
459*c8dee2aaSAndroid Build Coastguard Worker // Based on
460*c8dee2aaSAndroid Build Coastguard Worker // https://skia.googlesource.com/skia/+/a063eaeaf1e09e4d6f42e0f44a5723622a46d21c/bench/nanobench.cpp#1593.
print_benchmark_stats(Stats * stats,skia_private::TArray<double> * samples,BenchmarkTarget * target,std::string surfaceConfig,int loops)461*c8dee2aaSAndroid Build Coastguard Worker static void print_benchmark_stats(Stats* stats,
462*c8dee2aaSAndroid Build Coastguard Worker                                   skia_private::TArray<double>* samples,
463*c8dee2aaSAndroid Build Coastguard Worker                                   BenchmarkTarget* target,
464*c8dee2aaSAndroid Build Coastguard Worker                                   std::string surfaceConfig,
465*c8dee2aaSAndroid Build Coastguard Worker                                   int loops) {
466*c8dee2aaSAndroid Build Coastguard Worker     if (!FLAGS_autoTuneLoops) {
467*c8dee2aaSAndroid Build Coastguard Worker         TestRunner::Log("%4d/%-4dMB\t%s\t%s",
468*c8dee2aaSAndroid Build Coastguard Worker                         sk_tools::getCurrResidentSetSizeMB(),
469*c8dee2aaSAndroid Build Coastguard Worker                         sk_tools::getMaxResidentSetSizeMB(),
470*c8dee2aaSAndroid Build Coastguard Worker                         target->getBenchmark()->getUniqueName(),
471*c8dee2aaSAndroid Build Coastguard Worker                         surfaceConfig.c_str());
472*c8dee2aaSAndroid Build Coastguard Worker     } else if (FLAGS_quiet) {
473*c8dee2aaSAndroid Build Coastguard Worker         const char* mark = " ";
474*c8dee2aaSAndroid Build Coastguard Worker         const double stddev_percent = sk_ieee_double_divide(100 * sqrt(stats->var), stats->mean);
475*c8dee2aaSAndroid Build Coastguard Worker         if (stddev_percent > 5) mark = "?";
476*c8dee2aaSAndroid Build Coastguard Worker         if (stddev_percent > 10) mark = "!";
477*c8dee2aaSAndroid Build Coastguard Worker         TestRunner::Log("%10.2f %s\t%s\t%s",
478*c8dee2aaSAndroid Build Coastguard Worker                         stats->median * 1e3,
479*c8dee2aaSAndroid Build Coastguard Worker                         mark,
480*c8dee2aaSAndroid Build Coastguard Worker                         target->getBenchmark()->getUniqueName(),
481*c8dee2aaSAndroid Build Coastguard Worker                         surfaceConfig.c_str());
482*c8dee2aaSAndroid Build Coastguard Worker     } else if (FLAGS_csv) {
483*c8dee2aaSAndroid Build Coastguard Worker         const double stddev_percent = sk_ieee_double_divide(100 * sqrt(stats->var), stats->mean);
484*c8dee2aaSAndroid Build Coastguard Worker         SkDebugf("%g,%g,%g,%g,%g,%s,%s\n",
485*c8dee2aaSAndroid Build Coastguard Worker                  stats->min,
486*c8dee2aaSAndroid Build Coastguard Worker                  stats->median,
487*c8dee2aaSAndroid Build Coastguard Worker                  stats->mean,
488*c8dee2aaSAndroid Build Coastguard Worker                  stats->max,
489*c8dee2aaSAndroid Build Coastguard Worker                  stddev_percent,
490*c8dee2aaSAndroid Build Coastguard Worker                  surfaceConfig.c_str(),
491*c8dee2aaSAndroid Build Coastguard Worker                  target->getBenchmark()->getUniqueName());
492*c8dee2aaSAndroid Build Coastguard Worker     } else {
493*c8dee2aaSAndroid Build Coastguard Worker         const double stddev_percent = sk_ieee_double_divide(100 * sqrt(stats->var), stats->mean);
494*c8dee2aaSAndroid Build Coastguard Worker         TestRunner::Log("%4d/%-4dMB\t%d\t%s\t%s\t%s\t%s\t%.0f%%\t%s\t%s\t%s",
495*c8dee2aaSAndroid Build Coastguard Worker                         sk_tools::getCurrResidentSetSizeMB(),
496*c8dee2aaSAndroid Build Coastguard Worker                         sk_tools::getMaxResidentSetSizeMB(),
497*c8dee2aaSAndroid Build Coastguard Worker                         loops,
498*c8dee2aaSAndroid Build Coastguard Worker                         HUMANIZE(stats->min),
499*c8dee2aaSAndroid Build Coastguard Worker                         HUMANIZE(stats->median),
500*c8dee2aaSAndroid Build Coastguard Worker                         HUMANIZE(stats->mean),
501*c8dee2aaSAndroid Build Coastguard Worker                         HUMANIZE(stats->max),
502*c8dee2aaSAndroid Build Coastguard Worker                         stddev_percent,
503*c8dee2aaSAndroid Build Coastguard Worker                         FLAGS_ms ? to_string(samples->size()).c_str() : stats->plot.c_str(),
504*c8dee2aaSAndroid Build Coastguard Worker                         surfaceConfig.c_str(),
505*c8dee2aaSAndroid Build Coastguard Worker                         target->getBenchmark()->getUniqueName());
506*c8dee2aaSAndroid Build Coastguard Worker     }
507*c8dee2aaSAndroid Build Coastguard Worker 
508*c8dee2aaSAndroid Build Coastguard Worker     target->printStats();
509*c8dee2aaSAndroid Build Coastguard Worker 
510*c8dee2aaSAndroid Build Coastguard Worker     if (FLAGS_verbose) {
511*c8dee2aaSAndroid Build Coastguard Worker         std::ostringstream oss;
512*c8dee2aaSAndroid Build Coastguard Worker         oss << "Samples: ";
513*c8dee2aaSAndroid Build Coastguard Worker         for (int j = 0; j < samples->size(); j++) {
514*c8dee2aaSAndroid Build Coastguard Worker             oss << HUMANIZE((*samples)[j]) << "  ";
515*c8dee2aaSAndroid Build Coastguard Worker         }
516*c8dee2aaSAndroid Build Coastguard Worker         oss << target->getBenchmark()->getUniqueName();
517*c8dee2aaSAndroid Build Coastguard Worker         TestRunner::Log("%s", oss.str().c_str());
518*c8dee2aaSAndroid Build Coastguard Worker     }
519*c8dee2aaSAndroid Build Coastguard Worker }
520*c8dee2aaSAndroid Build Coastguard Worker 
521*c8dee2aaSAndroid Build Coastguard Worker // Based on
522*c8dee2aaSAndroid Build Coastguard Worker // https://skia.googlesource.com/skia/+/a063eaeaf1e09e4d6f42e0f44a5723622a46d21c/bench/nanobench.cpp#1337.
main(int argc,char ** argv)523*c8dee2aaSAndroid Build Coastguard Worker int main(int argc, char** argv) {
524*c8dee2aaSAndroid Build Coastguard Worker     TestRunner::InitAndLogCmdlineArgs(argc, argv);
525*c8dee2aaSAndroid Build Coastguard Worker 
526*c8dee2aaSAndroid Build Coastguard Worker     // When running under Bazel (e.g. "bazel test //path/to:test"), we'll store output files in
527*c8dee2aaSAndroid Build Coastguard Worker     // $TEST_UNDECLARED_OUTPUTS_DIR unless overridden via the --outputDir flag.
528*c8dee2aaSAndroid Build Coastguard Worker     //
529*c8dee2aaSAndroid Build Coastguard Worker     // See https://bazel.build/reference/test-encyclopedia#initial-conditions.
530*c8dee2aaSAndroid Build Coastguard Worker     std::string testUndeclaredOutputsDir;
531*c8dee2aaSAndroid Build Coastguard Worker     if (char* envVar = std::getenv("TEST_UNDECLARED_OUTPUTS_DIR")) {
532*c8dee2aaSAndroid Build Coastguard Worker         testUndeclaredOutputsDir = envVar;
533*c8dee2aaSAndroid Build Coastguard Worker     }
534*c8dee2aaSAndroid Build Coastguard Worker     bool isBazelTest = !testUndeclaredOutputsDir.empty();
535*c8dee2aaSAndroid Build Coastguard Worker 
536*c8dee2aaSAndroid Build Coastguard Worker     // Parse and validate flags.
537*c8dee2aaSAndroid Build Coastguard Worker     CommandLineFlags::Parse(argc, argv);
538*c8dee2aaSAndroid Build Coastguard Worker     validate_flags(isBazelTest);
539*c8dee2aaSAndroid Build Coastguard Worker 
540*c8dee2aaSAndroid Build Coastguard Worker     // TODO(lovisolo): Define an enum for surface configs and turn this flag into an enum value.
541*c8dee2aaSAndroid Build Coastguard Worker     std::string surfaceConfig = FLAGS_surfaceConfig[0];
542*c8dee2aaSAndroid Build Coastguard Worker 
543*c8dee2aaSAndroid Build Coastguard Worker     // Directory where we will write the output JSON file and any PNGs produced by the benchmarks.
544*c8dee2aaSAndroid Build Coastguard Worker     std::string outputDir =
545*c8dee2aaSAndroid Build Coastguard Worker             FLAGS_outputDir.isEmpty() ? testUndeclaredOutputsDir : FLAGS_outputDir[0];
546*c8dee2aaSAndroid Build Coastguard Worker 
547*c8dee2aaSAndroid Build Coastguard Worker     std::string cpuName = FLAGS_cpuName.isEmpty() ? "" : FLAGS_cpuName[0];
548*c8dee2aaSAndroid Build Coastguard Worker     std::string gpuName = FLAGS_gpuName.isEmpty() ? "" : FLAGS_gpuName[0];
549*c8dee2aaSAndroid Build Coastguard Worker 
550*c8dee2aaSAndroid Build Coastguard Worker     // Output JSON file.
551*c8dee2aaSAndroid Build Coastguard Worker     //
552*c8dee2aaSAndroid Build Coastguard Worker     // TODO(lovisolo): Define a constant with the file name, use it here and in flag descriptions.
553*c8dee2aaSAndroid Build Coastguard Worker     SkString jsonPath = SkOSPath::Join(outputDir.c_str(), "results.json");
554*c8dee2aaSAndroid Build Coastguard Worker     ResultsJSONWriter jsonWriter(jsonPath.c_str());
555*c8dee2aaSAndroid Build Coastguard Worker 
556*c8dee2aaSAndroid Build Coastguard Worker     if (FLAGS_gitHash.size() == 1) {
557*c8dee2aaSAndroid Build Coastguard Worker         jsonWriter.addGitHash(FLAGS_gitHash[0]);
558*c8dee2aaSAndroid Build Coastguard Worker     } else {
559*c8dee2aaSAndroid Build Coastguard Worker         TestRunner::Log(
560*c8dee2aaSAndroid Build Coastguard Worker                 "Warning: No --gitHash flag was specified. Perf ingestion ignores JSON files that "
561*c8dee2aaSAndroid Build Coastguard Worker                 "do not specify a Git hash. This is fine for local debugging, but CI tasks should "
562*c8dee2aaSAndroid Build Coastguard Worker                 "always set the --gitHash flag.");
563*c8dee2aaSAndroid Build Coastguard Worker     }
564*c8dee2aaSAndroid Build Coastguard Worker     if (FLAGS_issue.size() == 1 && FLAGS_patchset.size() == 1) {
565*c8dee2aaSAndroid Build Coastguard Worker         jsonWriter.addChangelistInfo(FLAGS_issue[0], FLAGS_patchset[0]);
566*c8dee2aaSAndroid Build Coastguard Worker     }
567*c8dee2aaSAndroid Build Coastguard Worker 
568*c8dee2aaSAndroid Build Coastguard Worker     // Key.
569*c8dee2aaSAndroid Build Coastguard Worker     std::map<std::string, std::string> keyValuePairs = {
570*c8dee2aaSAndroid Build Coastguard Worker             // Add a key/value pair that nanobench will never use in order to avoid accidentally
571*c8dee2aaSAndroid Build Coastguard Worker             // polluting an existing trace.
572*c8dee2aaSAndroid Build Coastguard Worker             {"build_system", "bazel"},
573*c8dee2aaSAndroid Build Coastguard Worker     };
574*c8dee2aaSAndroid Build Coastguard Worker     for (int i = 1; i < FLAGS_key.size(); i += 2) {
575*c8dee2aaSAndroid Build Coastguard Worker         keyValuePairs[FLAGS_key[i - 1]] = FLAGS_key[i];
576*c8dee2aaSAndroid Build Coastguard Worker     }
577*c8dee2aaSAndroid Build Coastguard Worker     keyValuePairs.merge(GetCompilationModeGoldAndPerfKeyValuePairs());
578*c8dee2aaSAndroid Build Coastguard Worker     jsonWriter.addKey(keyValuePairs);
579*c8dee2aaSAndroid Build Coastguard Worker 
580*c8dee2aaSAndroid Build Coastguard Worker     // Links.
581*c8dee2aaSAndroid Build Coastguard Worker     if (FLAGS_links.size()) {
582*c8dee2aaSAndroid Build Coastguard Worker         std::map<std::string, std::string> links;
583*c8dee2aaSAndroid Build Coastguard Worker         for (int i = 1; i < FLAGS_links.size(); i += 2) {
584*c8dee2aaSAndroid Build Coastguard Worker             links[FLAGS_links[i - 1]] = FLAGS_links[i];
585*c8dee2aaSAndroid Build Coastguard Worker         }
586*c8dee2aaSAndroid Build Coastguard Worker         jsonWriter.addLinks(links);
587*c8dee2aaSAndroid Build Coastguard Worker     }
588*c8dee2aaSAndroid Build Coastguard Worker 
589*c8dee2aaSAndroid Build Coastguard Worker     int runs = 0;
590*c8dee2aaSAndroid Build Coastguard Worker     bool missingCpuOrGpuWarningLogged = false;
591*c8dee2aaSAndroid Build Coastguard Worker     for (auto benchmarkFactory : BenchRegistry::Range()) {
592*c8dee2aaSAndroid Build Coastguard Worker         std::unique_ptr<Benchmark> benchmark(benchmarkFactory(nullptr));
593*c8dee2aaSAndroid Build Coastguard Worker 
594*c8dee2aaSAndroid Build Coastguard Worker         if (!TestRunner::ShouldRunTestCase(benchmark->getUniqueName(), FLAGS_match, FLAGS_skip)) {
595*c8dee2aaSAndroid Build Coastguard Worker             TestRunner::Log("Skipping %s", benchmark->getName());
596*c8dee2aaSAndroid Build Coastguard Worker             continue;
597*c8dee2aaSAndroid Build Coastguard Worker         }
598*c8dee2aaSAndroid Build Coastguard Worker 
599*c8dee2aaSAndroid Build Coastguard Worker         benchmark->delayedSetup();
600*c8dee2aaSAndroid Build Coastguard Worker 
601*c8dee2aaSAndroid Build Coastguard Worker         std::unique_ptr<BenchmarkTarget> target =
602*c8dee2aaSAndroid Build Coastguard Worker                 BenchmarkTarget::FromConfig(surfaceConfig, benchmark.get());
603*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT_RELEASE(target);
604*c8dee2aaSAndroid Build Coastguard Worker 
605*c8dee2aaSAndroid Build Coastguard Worker         if (benchmark->isSuitableFor(target->getBackend())) {
606*c8dee2aaSAndroid Build Coastguard Worker             // Print warning about missing cpu_or_gpu key if necessary.
607*c8dee2aaSAndroid Build Coastguard Worker             if (target->isCpuOrGpuBound() == SurfaceManager::CpuOrGpu::kCPU && cpuName == "" &&
608*c8dee2aaSAndroid Build Coastguard Worker                 !missingCpuOrGpuWarningLogged) {
609*c8dee2aaSAndroid Build Coastguard Worker                 TestRunner::Log(
610*c8dee2aaSAndroid Build Coastguard Worker                         "Warning: The surface is CPU-bound, but flag --cpuName was not provided. "
611*c8dee2aaSAndroid Build Coastguard Worker                         "Perf traces will omit keys \"cpu_or_gpu\" and \"cpu_or_gpu_value\".");
612*c8dee2aaSAndroid Build Coastguard Worker                 missingCpuOrGpuWarningLogged = true;
613*c8dee2aaSAndroid Build Coastguard Worker             }
614*c8dee2aaSAndroid Build Coastguard Worker             if (target->isCpuOrGpuBound() == SurfaceManager::CpuOrGpu::kGPU && gpuName == "" &&
615*c8dee2aaSAndroid Build Coastguard Worker                 !missingCpuOrGpuWarningLogged) {
616*c8dee2aaSAndroid Build Coastguard Worker                 TestRunner::Log(
617*c8dee2aaSAndroid Build Coastguard Worker                         "Warning: The surface is GPU-bound, but flag --gpuName was not provided. "
618*c8dee2aaSAndroid Build Coastguard Worker                         "Perf traces will omit keys \"cpu_or_gpu\" and \"cpu_or_gpu_value\".");
619*c8dee2aaSAndroid Build Coastguard Worker                 missingCpuOrGpuWarningLogged = true;
620*c8dee2aaSAndroid Build Coastguard Worker             }
621*c8dee2aaSAndroid Build Coastguard Worker 
622*c8dee2aaSAndroid Build Coastguard Worker             // Run benchmark and collect samples.
623*c8dee2aaSAndroid Build Coastguard Worker             int loops;
624*c8dee2aaSAndroid Build Coastguard Worker             skia_private::TArray<double> samples;
625*c8dee2aaSAndroid Build Coastguard Worker             skia_private::TArray<SkString> statKeys;
626*c8dee2aaSAndroid Build Coastguard Worker             skia_private::TArray<double> statValues;
627*c8dee2aaSAndroid Build Coastguard Worker             if (!sample_benchmark(target.get(), &loops, &samples, &statKeys, &statValues)) {
628*c8dee2aaSAndroid Build Coastguard Worker                 // Sampling failed. A warning has already been printed.
629*c8dee2aaSAndroid Build Coastguard Worker                 pool.drain();
630*c8dee2aaSAndroid Build Coastguard Worker                 continue;
631*c8dee2aaSAndroid Build Coastguard Worker             }
632*c8dee2aaSAndroid Build Coastguard Worker 
633*c8dee2aaSAndroid Build Coastguard Worker             if (FLAGS_writePNGs) {
634*c8dee2aaSAndroid Build Coastguard Worker                 // Save the bitmap produced by the benchmark to disk, if applicable. Not all
635*c8dee2aaSAndroid Build Coastguard Worker                 // benchmarks produce bitmaps, e.g. those that use the "nonrendering" config.
636*c8dee2aaSAndroid Build Coastguard Worker                 maybe_write_png(target.get(), outputDir);
637*c8dee2aaSAndroid Build Coastguard Worker             }
638*c8dee2aaSAndroid Build Coastguard Worker 
639*c8dee2aaSAndroid Build Coastguard Worker             // Building stats.plot often shows up in profiles, so skip building it when we're
640*c8dee2aaSAndroid Build Coastguard Worker             // not going to print it anyway.
641*c8dee2aaSAndroid Build Coastguard Worker             const bool want_plot = !FLAGS_quiet && !FLAGS_ms;
642*c8dee2aaSAndroid Build Coastguard Worker             Stats stats(samples, want_plot);
643*c8dee2aaSAndroid Build Coastguard Worker 
644*c8dee2aaSAndroid Build Coastguard Worker             print_benchmark_stats(&stats, &samples, target.get(), surfaceConfig, loops);
645*c8dee2aaSAndroid Build Coastguard Worker 
646*c8dee2aaSAndroid Build Coastguard Worker             ResultsJSONWriter::Result result;
647*c8dee2aaSAndroid Build Coastguard Worker             result.key = {
648*c8dee2aaSAndroid Build Coastguard Worker                     // Based on
649*c8dee2aaSAndroid Build Coastguard Worker                     // https://skia.googlesource.com/skia/+/a063eaeaf1e09e4d6f42e0f44a5723622a46d21c/bench/nanobench.cpp#1566.
650*c8dee2aaSAndroid Build Coastguard Worker                     {"name", std::string(benchmark->getName())},
651*c8dee2aaSAndroid Build Coastguard Worker 
652*c8dee2aaSAndroid Build Coastguard Worker                     // Replaces the "config" and "extra_config" keys set by nanobench.
653*c8dee2aaSAndroid Build Coastguard Worker                     {"surface_config", surfaceConfig},
654*c8dee2aaSAndroid Build Coastguard Worker 
655*c8dee2aaSAndroid Build Coastguard Worker                     // Based on
656*c8dee2aaSAndroid Build Coastguard Worker                     // https://skia.googlesource.com/skia/+/a063eaeaf1e09e4d6f42e0f44a5723622a46d21c/bench/nanobench.cpp#1578.
657*c8dee2aaSAndroid Build Coastguard Worker                     //
658*c8dee2aaSAndroid Build Coastguard Worker                     // TODO(lovisolo): Determine these dynamically when we add support for GMBench,
659*c8dee2aaSAndroid Build Coastguard Worker                     //                 SKPBench, etc.
660*c8dee2aaSAndroid Build Coastguard Worker                     {"source_type", "bench"},
661*c8dee2aaSAndroid Build Coastguard Worker                     {"bench_type", "micro"},
662*c8dee2aaSAndroid Build Coastguard Worker 
663*c8dee2aaSAndroid Build Coastguard Worker                     // Nanobench adds a "test" key consisting of "<unique name>_<width>_<height>",
664*c8dee2aaSAndroid Build Coastguard Worker                     // presumably with the goal of making the trace ID unique, see
665*c8dee2aaSAndroid Build Coastguard Worker                     // https://skia.googlesource.com/skia/+/a063eaeaf1e09e4d6f42e0f44a5723622a46d21c/bench/nanobench.cpp#1456.
666*c8dee2aaSAndroid Build Coastguard Worker                     //
667*c8dee2aaSAndroid Build Coastguard Worker                     // However, we can accomplish unique trace IDs by expressing "<width>" and
668*c8dee2aaSAndroid Build Coastguard Worker                     // "<height>" as their own keys.
669*c8dee2aaSAndroid Build Coastguard Worker                     //
670*c8dee2aaSAndroid Build Coastguard Worker                     // Regarding the "<unique name>" part of the "test" key:
671*c8dee2aaSAndroid Build Coastguard Worker                     //
672*c8dee2aaSAndroid Build Coastguard Worker                     //  - Nanobench sets "<unique name>" to the result of
673*c8dee2aaSAndroid Build Coastguard Worker                     //    Benchmark::getUniqueName():
674*c8dee2aaSAndroid Build Coastguard Worker                     //    https://skia.googlesource.com/skia/+/a063eaeaf1e09e4d6f42e0f44a5723622a46d21c/bench/Benchmark.h#41.
675*c8dee2aaSAndroid Build Coastguard Worker                     //
676*c8dee2aaSAndroid Build Coastguard Worker                     //  - Benchmark::getUniqueName() returns Benchmark::getName() except for the
677*c8dee2aaSAndroid Build Coastguard Worker                     //    following two cases.
678*c8dee2aaSAndroid Build Coastguard Worker                     //
679*c8dee2aaSAndroid Build Coastguard Worker                     //  - SKPBench::getUniqueName() returns "<name>_<scale>":
680*c8dee2aaSAndroid Build Coastguard Worker                     //    https://skia.googlesource.com/skia/+/a063eaeaf1e09e4d6f42e0f44a5723622a46d21c/bench/SKPBench.cpp#33.
681*c8dee2aaSAndroid Build Coastguard Worker                     //
682*c8dee2aaSAndroid Build Coastguard Worker                     //  - SKPAnimationBench returns "<name>_<tag>":
683*c8dee2aaSAndroid Build Coastguard Worker                     //    https://skia.googlesource.com/skia/+/a063eaeaf1e09e4d6f42e0f44a5723622a46d21c/bench/SKPAnimationBench.cpp#18.
684*c8dee2aaSAndroid Build Coastguard Worker                     //
685*c8dee2aaSAndroid Build Coastguard Worker                     // Therefore it is important that we add "<scale>" and "<tag>" as their own
686*c8dee2aaSAndroid Build Coastguard Worker                     // keys when we eventually add support for these kinds of benchmarks.
687*c8dee2aaSAndroid Build Coastguard Worker                     //
688*c8dee2aaSAndroid Build Coastguard Worker                     // Based on
689*c8dee2aaSAndroid Build Coastguard Worker                     // https://skia.googlesource.com/skia/+/a063eaeaf1e09e4d6f42e0f44a5723622a46d21c/bench/nanobench.cpp#1456.
690*c8dee2aaSAndroid Build Coastguard Worker                     {"width", SkStringPrintf("%d", benchmark->getSize().width()).c_str()},
691*c8dee2aaSAndroid Build Coastguard Worker                     {"height", SkStringPrintf("%d", benchmark->getSize().height()).c_str()},
692*c8dee2aaSAndroid Build Coastguard Worker             };
693*c8dee2aaSAndroid Build Coastguard Worker             result.key.merge(target->getKeyValuePairs(cpuName, gpuName));
694*c8dee2aaSAndroid Build Coastguard Worker             result.measurements["ms"] = {
695*c8dee2aaSAndroid Build Coastguard Worker                     // Based on
696*c8dee2aaSAndroid Build Coastguard Worker                     // https://skia.googlesource.com/skia/+/a063eaeaf1e09e4d6f42e0f44a5723622a46d21c/bench/nanobench.cpp#1571.
697*c8dee2aaSAndroid Build Coastguard Worker                     {.value = "min", .measurement = stats.min},
698*c8dee2aaSAndroid Build Coastguard Worker                     {.value = "ratio",
699*c8dee2aaSAndroid Build Coastguard Worker                      .measurement = sk_ieee_double_divide(stats.median, stats.min)},
700*c8dee2aaSAndroid Build Coastguard Worker             };
701*c8dee2aaSAndroid Build Coastguard Worker             if (!statKeys.empty()) {
702*c8dee2aaSAndroid Build Coastguard Worker                 // Based on
703*c8dee2aaSAndroid Build Coastguard Worker                 // https://skia.googlesource.com/skia/+/a063eaeaf1e09e4d6f42e0f44a5723622a46d21c/bench/nanobench.cpp#1580.
704*c8dee2aaSAndroid Build Coastguard Worker                 //
705*c8dee2aaSAndroid Build Coastguard Worker                 // Only SKPBench currently returns valid key/value pairs.
706*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(statKeys.size() == statValues.size());
707*c8dee2aaSAndroid Build Coastguard Worker                 result.measurements["stats"] = {};
708*c8dee2aaSAndroid Build Coastguard Worker                 for (int i = 0; i < statKeys.size(); i++) {
709*c8dee2aaSAndroid Build Coastguard Worker                     result.measurements["stats"].push_back(
710*c8dee2aaSAndroid Build Coastguard Worker                             {.value = statKeys[i].c_str(), .measurement = statValues[i]});
711*c8dee2aaSAndroid Build Coastguard Worker                 }
712*c8dee2aaSAndroid Build Coastguard Worker             }
713*c8dee2aaSAndroid Build Coastguard Worker             jsonWriter.addResult(result);
714*c8dee2aaSAndroid Build Coastguard Worker 
715*c8dee2aaSAndroid Build Coastguard Worker             runs++;
716*c8dee2aaSAndroid Build Coastguard Worker             if (runs % FLAGS_flushEvery == 0) {
717*c8dee2aaSAndroid Build Coastguard Worker                 jsonWriter.flush();
718*c8dee2aaSAndroid Build Coastguard Worker             }
719*c8dee2aaSAndroid Build Coastguard Worker 
720*c8dee2aaSAndroid Build Coastguard Worker             pool.drain();
721*c8dee2aaSAndroid Build Coastguard Worker         } else {
722*c8dee2aaSAndroid Build Coastguard Worker             if (FLAGS_verbose) {
723*c8dee2aaSAndroid Build Coastguard Worker                 TestRunner::Log("Skipping \"%s\" because backend \"%s\" was unsuitable.\n",
724*c8dee2aaSAndroid Build Coastguard Worker                                 target->getBenchmark()->getUniqueName(),
725*c8dee2aaSAndroid Build Coastguard Worker                                 surfaceConfig.c_str());
726*c8dee2aaSAndroid Build Coastguard Worker             }
727*c8dee2aaSAndroid Build Coastguard Worker         }
728*c8dee2aaSAndroid Build Coastguard Worker     }
729*c8dee2aaSAndroid Build Coastguard Worker 
730*c8dee2aaSAndroid Build Coastguard Worker     BenchmarkTarget::printGlobalStats();
731*c8dee2aaSAndroid Build Coastguard Worker 
732*c8dee2aaSAndroid Build Coastguard Worker     SkGraphics::PurgeAllCaches();
733*c8dee2aaSAndroid Build Coastguard Worker 
734*c8dee2aaSAndroid Build Coastguard Worker     // Based on
735*c8dee2aaSAndroid Build Coastguard Worker     // https://skia.googlesource.com/skia/+/a063eaeaf1e09e4d6f42e0f44a5723622a46d21c/bench/nanobench.cpp#1668.
736*c8dee2aaSAndroid Build Coastguard Worker     jsonWriter.addResult({
737*c8dee2aaSAndroid Build Coastguard Worker             .key =
738*c8dee2aaSAndroid Build Coastguard Worker                     {
739*c8dee2aaSAndroid Build Coastguard Worker                             {"name", "memory_usage"},
740*c8dee2aaSAndroid Build Coastguard Worker                     },
741*c8dee2aaSAndroid Build Coastguard Worker             .measurements =
742*c8dee2aaSAndroid Build Coastguard Worker                     {
743*c8dee2aaSAndroid Build Coastguard Worker                             {"resident_set_size_mb",
744*c8dee2aaSAndroid Build Coastguard Worker                              {{.value = "max",
745*c8dee2aaSAndroid Build Coastguard Worker                                .measurement = double(sk_tools::getMaxResidentSetSizeMB())}}},
746*c8dee2aaSAndroid Build Coastguard Worker                     },
747*c8dee2aaSAndroid Build Coastguard Worker     });
748*c8dee2aaSAndroid Build Coastguard Worker 
749*c8dee2aaSAndroid Build Coastguard Worker     TestRunner::Log("JSON file written to: %s", jsonPath.c_str());
750*c8dee2aaSAndroid Build Coastguard Worker     TestRunner::Log("PNGs (if any) written to: %s", outputDir.c_str());
751*c8dee2aaSAndroid Build Coastguard Worker 
752*c8dee2aaSAndroid Build Coastguard Worker     return 0;
753*c8dee2aaSAndroid Build Coastguard Worker }
754