blob: f1ae4ff7e3e12d96a70fa7718b11fc493100efe0 [file] [log] [blame]
Anders Lewisa7b0f882017-07-24 20:01:13 -07001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <getopt.h>
18#include <math.h>
19#include <sys/resource.h>
20
21#include <map>
22#include <mutex>
23#include <sstream>
24#include <string>
25#include <vector>
26
27#include "android-base/strings.h"
28#include <benchmark/benchmark.h>
29#include <tinyxml2.h>
30#include "util.h"
31
32std::map<std::string, benchmark_func_t> g_str_to_func;
33
34std::mutex g_map_lock;
35
36static struct option g_long_options[] =
37{
38 {"bionic_cpu", required_argument, 0, 'c'},
39 {"bionic_xml", required_argument, 0, 'x'},
40 {"bionic_iterations", required_argument, 0, 'i'},
41 {"bionic_extra", required_argument, 0, 'a'},
42 {"help", no_argument, 0, 'h'},
43 {0, 0, 0, 0},
44};
45
46typedef std::vector<std::vector<int>> args_vector_t;
47
48void Usage() {
49 printf("Usage:\n");
50 printf("bionic_benchmarks [--bionic_cpu=<cpu_to_isolate>] [--bionic_xml=<path_to_xml>]\n");
51 printf(" [--bionic_iterations=<num_iter>]\n");
52 printf(" [--bionic_extra=\"<fn_name> <arg1> <arg 2> ... \n]");
53 printf(" [<Google benchmark flags>]\n");
54 printf("Google benchmark flags:\n");
55
56 int fake_argc = 2;
57 char argv0[] = "bionic_benchmarks";
58 char argv1[] = "--help";
59 char* fake_argv[3] {argv0, argv1, NULL};
60 benchmark::Initialize(&fake_argc, fake_argv);
61 abort();
62}
63
64// This function removes any bionic benchmarks command line arguments by checking them
65// against g_long_options. It fills new_argv with the filtered args.
66void SanitizeOpts(int argc, char** argv, std::vector<char*>* new_argv) {
67 // TO THOSE ADDING OPTIONS: This currently doesn't support optional arguments.
68 (*new_argv)[0] = argv[0];
69 for (int i = 1; i < argc; ++i) {
70 char* optarg = argv[i];
71 size_t opt_idx = 0;
72
73 // Iterate through g_long_options until either we hit the end or we have a match.
74 for (opt_idx = 0; g_long_options[opt_idx].name &&
75 strncmp(g_long_options[opt_idx].name, optarg + 2,
76 strlen(g_long_options[opt_idx].name)); ++opt_idx) {
77 }
78
79 if (!g_long_options[opt_idx].name) {
80 new_argv->push_back(optarg);
81 } else {
82 if (g_long_options[opt_idx].has_arg == required_argument) {
83 // If the arg was passed in with an =, it spans one char *.
84 // Otherwise, we skip a spot for the argument.
85 if (!strchr(optarg, '=')) {
86 i++;
87 }
88 }
89 }
90 }
91 new_argv->push_back(0);
92}
93
94bench_opts_t ParseOpts(int argc, char** argv) {
95 bench_opts_t opts;
96 int opt;
97 int option_index = 0;
98
99 opts.cpu_to_lock = LONG_MAX;
100 opts.num_iterations = 0;
101
102 // To make this parser handle the benchmark options silently:
103 extern int opterr;
104 opterr = 0;
105
106 while ((opt = getopt_long(argc, argv, "c:x:i:a:h", g_long_options, &option_index)) != -1) {
107 if (opt == -1) {
108 break;
109 }
110 switch (opt) {
111 case 'c':
112 if (*optarg) {
113 char* check_null;
114 opts.cpu_to_lock = strtol(optarg, &check_null, 10);
115 if (*check_null) {
116 printf("ERROR: Args %s is not a valid integer.\n", optarg);
117 abort();
118 }
119 } else {
120 printf("ERROR: no argument specified for bionic_cpu\n");
121 Usage();
122 }
123 break;
124 case 'x':
125 if (*optarg) {
126 opts.xmlpath = optarg;
127 } else {
128 printf("ERROR: no argument specified for bionic_xml\n");
129 Usage();
130 }
131 break;
132 case 'a':
133 if (*optarg) {
134 opts.extra_benchmarks.push_back(optarg);
135 } else {
136 printf("ERROR: no argument specified for bionic_extra\n");
137 Usage();
138 }
139 break;
140 case 'i':
141 if (*optarg){
142 char* check_null;
143 opts.num_iterations = strtol(optarg, &check_null, 10);
144 if (*check_null != '\0') {
145 printf("ERROR: Args %s is not a valid integer.\n", optarg);
146 abort();
147 }
148 } else {
149 printf("ERROR: no argument specified for bionic_iterations\n");
150 Usage();
151 }
152 break;
153 case 'h':
154 Usage();
155 break;
156 case '?':
157 break;
158 default:
159 abort();
160 }
161 }
162 return opts;
163}
164
165// This is a wrapper for every function call for per-benchmark cpu pinning.
166void LockAndRun(benchmark::State& state, benchmark_func_t func_to_bench, long cpu_to_lock) {
167 if (cpu_to_lock != LONG_MAX) LockToCPU(cpu_to_lock);
168 // To avoid having to link against Google benchmarks in libutil,
169 // benchmarks are kept without parameter information, necessitating this cast.
170 reinterpret_cast<void(*) (benchmark::State&)>(func_to_bench)(state);
171}
172
173args_vector_t* ResolveArgs(args_vector_t* to_populate, std::string args,
174 std::map<std::string, args_vector_t>& args_shorthand) {
175 // args is either a space-separated list of ints or a macro name.
176 // To ease formatting in XML files, args is left and right trimmed.
177 if (args_shorthand.count(args)) {
178 return &args_shorthand[args];
179 }
180 to_populate->push_back(std::vector<int>());
181 std::stringstream sstream(args);
182 std::string argstr;
183 while (sstream >> argstr) {
184 char* check_null;
185 int converted = static_cast<int>(strtol(argstr.c_str(), &check_null, 10));
186 if (*check_null) {
187 printf("ERROR: Args str %s contains an invalid macro or int.\n", args.c_str());
188 abort();
189 }
190 (*to_populate)[0].push_back(converted);
191 }
192 return to_populate;
193}
194
195void RegisterGoogleBenchmarks(bench_opts_t primary_opts, bench_opts_t secondary_opts,
196 std::string fn_name, args_vector_t* run_args) {
197 if (g_str_to_func.find(fn_name) == g_str_to_func.end()) {
198 printf("ERROR:No benchmark for function %s\n", fn_name.c_str());
199 abort();
200 }
201 long iterations_to_use = primary_opts.num_iterations ? primary_opts.num_iterations :
202 secondary_opts.num_iterations;
203 int cpu_to_use = INT_MAX;
204 if (primary_opts.cpu_to_lock != INT_MAX) {
205 cpu_to_use = primary_opts.cpu_to_lock;
206
207 } else if (secondary_opts.cpu_to_lock != INT_MAX) {
208 cpu_to_use = secondary_opts.cpu_to_lock;
209 }
210
211 for (std::vector<int> args : (*run_args)) {
212 auto registration = benchmark::RegisterBenchmark(fn_name.c_str(), LockAndRun,
213 g_str_to_func.at(fn_name),
214 cpu_to_use)->Args(args);
215 if (iterations_to_use > 0) {
216 registration->Iterations(iterations_to_use);
217 }
218 }
219}
220
221void RegisterCliBenchmarks(bench_opts_t cmdline_opts,
222 std::map<std::string, args_vector_t>& args_shorthand) {
223 // Register any of the extra benchmarks that were specified in the options.
224 args_vector_t arg_vector;
225 args_vector_t* run_args = &arg_vector;
226 for (std::string extra_fn : cmdline_opts.extra_benchmarks) {
227 android::base::Trim(extra_fn);
228 size_t first_space_pos = extra_fn.find(" ");
229 std::string fn_name = extra_fn.substr(0, first_space_pos);
230 std::string cmd_args;
231 if (first_space_pos != std::string::npos) {
232 cmd_args = extra_fn.substr(extra_fn.find(" ") + 1);
233 } else {
234 cmd_args = "";
235 }
236 run_args = ResolveArgs(run_args, cmd_args, args_shorthand);
237 RegisterGoogleBenchmarks(bench_opts_t(), cmdline_opts, fn_name, run_args);
238
239 run_args = &arg_vector;
240 arg_vector.clear();
241 }
242}
243
244int RegisterXmlBenchmarks(bench_opts_t cmdline_opts,
245 std::map<std::string, args_vector_t>& args_shorthand) {
246 // Structure of the XML file:
247 // - Element "fn" Function to benchmark.
248 // - - Element "iterations" Number of iterations to run. Leaving this blank uses
249 // Google benchmarks' convergence heuristics.
250 // - - Element "cpu" CPU to isolate to, if any.
251 // - - Element "args" Whitespace-separated list of per-function integer arguments, or
252 // one of the macros defined in util.h.
253 tinyxml2::XMLDocument doc;
Elliott Hughesc2223f72017-08-08 11:23:27 -0700254 if (doc.LoadFile(cmdline_opts.xmlpath.c_str()) != tinyxml2::XML_SUCCESS) {
Anders Lewisa7b0f882017-07-24 20:01:13 -0700255 doc.PrintError();
256 return doc.ErrorID();
257 }
258
259 // Read and register the functions.
260 tinyxml2::XMLNode* fn = doc.FirstChildElement("fn");
261 args_vector_t arg_vector;
262 args_vector_t* run_args = &arg_vector;
263 while (fn) {
264 auto fn_elem = fn->FirstChildElement("name");
265 if (!fn_elem) {
266 printf("Error: Malformed XML entry: missing name element.\n");
267 abort();
268 }
269 std::string fn_name = fn_elem->GetText();
270 if (fn_name.empty()) {
271 printf("Error: Malformed XML entry: error parsing name text.");
272 abort();
273 }
274 auto* xml_args = fn->FirstChildElement("args");
275 run_args = ResolveArgs(run_args, xml_args ? android::base::Trim(xml_args->GetText()) : "",
276 args_shorthand);
277
278 // XML values for CPU and iterations take precedence over those passed in via CLI.
279 bench_opts_t xml_opts{};
280 auto* num_iterations_elem = fn->FirstChildElement("iterations");
281 if (num_iterations_elem) {
282 int temp;
283 num_iterations_elem->QueryIntText(&temp);
284 xml_opts.num_iterations = temp;
285 } else {
286 xml_opts.num_iterations = 0;
287 }
288 auto* cpu_to_lock_elem = fn->FirstChildElement("cpu");
289 if (cpu_to_lock_elem) {
290 int temp;
291 cpu_to_lock_elem->QueryIntText(&temp);
292 xml_opts.cpu_to_lock = temp;
293 } else {
294 xml_opts.cpu_to_lock = INT_MAX;
295 }
296
297 RegisterGoogleBenchmarks(xml_opts, cmdline_opts, fn_name, run_args);
298
299 fn = fn->NextSibling();
300 run_args = &arg_vector;
301 arg_vector.clear();
302 }
303 return 0;
304}
305
306std::map<std::string, args_vector_t> GetShorthand() {
307 std::map<std::string, args_vector_t> args_shorthand {
308 {"AT_ALIGNED_TWOBUF", args_vector_t{ {8, 0, 0},
309 {64, 0, 0},
310 {512, 0, 0},
311 {1 * KB, 0, 0},
312 {8 * KB, 0, 0},
313 {16 * KB, 0, 0},
314 {32 * KB, 0, 0},
315 {64 * KB, 0, 0} }},
316 {"AT_ALIGNED_ONEBUF", args_vector_t{ {(8), 0},
317 {(64), 0},
318 {(512), 0},
319 {(1*KB), 0},
320 {(8*KB), 0},
321 {(16*KB), 0},
322 {(32*KB), 0},
323 {(64*KB), 0}}},
324
325 {"AT_COMMON_SIZES", args_vector_t{ {8}, {64}, {512}, {1*KB}, {8*KB}, {16*KB},
326 {32*KB}, {64*KB}}},
327
328 // Do not exceed 512. that is about the largest number of properties
329 // that can be created with the current property area size.
330 {"NUM_PROPS", args_vector_t{ {1}, {4}, {16}, {64}, {128}, {256}, {512} }},
331
332 {"MATH_COMMON", args_vector_t{ {0}, {1}, {2}, {3} }}
333 };
334 for (int i = 1; i < 15; i++) {
335 int align = pow(2, i);
336 std::stringstream sstream;
337 sstream << "AT_" << align << "_ALIGN_TWOBUF";
338 args_shorthand.emplace(sstream.str(),
339 args_vector_t{ {8, align, align},
340 {64, align, align},
341 {512, align, align},
342 {1 * KB, align, align},
343 {8 * KB, align, align},
344 {16 * KB, align, align},
345 {32 * KB, align, align},
346 {64 * KB, align, align} });
347 sstream.str("");
348 sstream << "AT_" << align << "_ALIGN_ONEBUF";
349 args_shorthand.emplace(sstream.str(),
350 args_vector_t{ {(8), align},
351 {(64), align},
352 {(512), align},
353 {(1*KB), align},
354 {(8*KB), align},
355 {(16*KB), align},
356 {(32*KB), align},
357 {(64*KB), align} });
358 sstream.str("");
359 }
360 return args_shorthand;
361}
362
363
364int main(int argc, char** argv) {
365 std::map<std::string, args_vector_t> args_shorthand = GetShorthand();
366 bench_opts_t opts = ParseOpts(argc, argv);
367 std::vector<char*> new_argv(argc);
368 SanitizeOpts(argc, argv, &new_argv);
369
370 if (!opts.xmlpath.empty()) {
371 if (int err = RegisterXmlBenchmarks(opts, args_shorthand)) {
372 return err;
373 }
374 }
375 RegisterCliBenchmarks(opts, args_shorthand);
376
377 // Set the thread priority to the maximum.
378 if (setpriority(PRIO_PROCESS, 0, -20)) {
379 perror("Failed to raise priority of process. Are you root?\n");
380 }
381
382 int new_argc = new_argv.size();
383 benchmark::Initialize(&new_argc, new_argv.data());
384 benchmark::RunSpecifiedBenchmarks();
385}