Home | History | Annotate | Download | only in benchmarks
      1 /*
      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 <err.h>
     18 #include <getopt.h>
     19 #include <inttypes.h>
     20 #include <math.h>
     21 #include <sys/resource.h>
     22 
     23 #include <map>
     24 #include <mutex>
     25 #include <sstream>
     26 #include <string>
     27 #include <utility>
     28 #include <vector>
     29 
     30 #include <android-base/file.h>
     31 #include <android-base/stringprintf.h>
     32 #include <android-base/strings.h>
     33 #include <benchmark/benchmark.h>
     34 #include <tinyxml2.h>
     35 #include "util.h"
     36 
     37 #define _STR(x) #x
     38 #define STRINGFY(x) _STR(x)
     39 
     40 static const std::vector<int> kCommonSizes{
     41   8,
     42   64,
     43   512,
     44   1 * KB,
     45   8 * KB,
     46   16 * KB,
     47   32 * KB,
     48   64 * KB,
     49   128 * KB,
     50 };
     51 
     52 static const std::vector<int> kSmallSizes{
     53   // Increment by 1
     54   1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
     55   // Increment by 8
     56   24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 112, 120, 128, 136, 144,
     57   // Increment by 16
     58   160, 176, 192, 208, 224, 240, 256,
     59 };
     60 
     61 static const std::vector<int> kMediumSizes{
     62   512,
     63   1 * KB,
     64   8 * KB,
     65   16 * KB,
     66   32 * KB,
     67   64 * KB,
     68   128 * KB,
     69 };
     70 
     71 static const std::vector<int> kLargeSizes{
     72   256 * KB,
     73   512 * KB,
     74   1024 * KB,
     75   2048 * KB,
     76 };
     77 
     78 static std::map<std::string, const std::vector<int> &> kSizes{
     79   { "SMALL",  kSmallSizes },
     80   { "MEDIUM", kMediumSizes },
     81   { "LARGE",  kLargeSizes },
     82 };
     83 
     84 std::map<std::string, std::pair<benchmark_func_t, std::string>> g_str_to_func;
     85 
     86 std::mutex g_map_lock;
     87 
     88 static struct option g_long_options[] =
     89 {
     90   {"bionic_cpu", required_argument, nullptr, 'c'},
     91   {"bionic_xml", required_argument, nullptr, 'x'},
     92   {"bionic_iterations", required_argument, nullptr, 'i'},
     93   {"bionic_extra", required_argument, nullptr, 'a'},
     94   {"help", no_argument, nullptr, 'h'},
     95   {nullptr, 0, nullptr, 0},
     96 };
     97 
     98 typedef std::vector<std::vector<int64_t>> args_vector_t;
     99 
    100 void Usage() {
    101   printf("Usage:\n");
    102   printf("bionic_benchmarks [--bionic_cpu=<cpu_to_isolate>]\n");
    103   printf("                  [--bionic_xml=<path_to_xml>]\n");
    104   printf("                  [--bionic_iterations=<num_iter>]\n");
    105   printf("                  [--bionic_extra=\"<fn_name> <arg1> <arg 2> ...\"]\n");
    106   printf("                  [<Google benchmark flags>]\n");
    107   printf("Google benchmark flags:\n");
    108 
    109   int fake_argc = 2;
    110   char argv0[] = "bionic_benchmarks";
    111   char argv1[] = "--help";
    112   char* fake_argv[3] {argv0, argv1, nullptr};
    113   benchmark::Initialize(&fake_argc, fake_argv);
    114   exit(1);
    115 }
    116 
    117 // This function removes any bionic benchmarks command line arguments by checking them
    118 // against g_long_options. It fills new_argv with the filtered args.
    119 void SanitizeOpts(int argc, char** argv, std::vector<char*>* new_argv) {
    120   // TO THOSE ADDING OPTIONS: This currently doesn't support optional arguments.
    121   (*new_argv)[0] = argv[0];
    122   for (int i = 1; i < argc; ++i) {
    123     char* optarg = argv[i];
    124     size_t opt_idx = 0;
    125 
    126     // Iterate through g_long_options until either we hit the end or we have a match.
    127     for (opt_idx = 0; g_long_options[opt_idx].name &&
    128                       strncmp(g_long_options[opt_idx].name, optarg + 2,
    129                               strlen(g_long_options[opt_idx].name)); ++opt_idx) {
    130     }
    131 
    132     if (!g_long_options[opt_idx].name) {
    133       new_argv->push_back(optarg);
    134     } else {
    135       if (g_long_options[opt_idx].has_arg == required_argument) {
    136         // If the arg was passed in with an =, it spans one char *.
    137         // Otherwise, we skip a spot for the argument.
    138         if (!strchr(optarg, '=')) {
    139           i++;
    140         }
    141       }
    142     }
    143   }
    144   new_argv->push_back(nullptr);
    145 }
    146 
    147 bench_opts_t ParseOpts(int argc, char** argv) {
    148   bench_opts_t opts;
    149   int opt;
    150   int option_index = 0;
    151 
    152   // To make this parser handle the benchmark options silently:
    153   extern int opterr;
    154   opterr = 0;
    155 
    156   while ((opt = getopt_long(argc, argv, "c:x:i:a:h", g_long_options, &option_index)) != -1) {
    157     if (opt == -1) {
    158       break;
    159     }
    160     switch (opt) {
    161       case 'c':
    162         if (*optarg) {
    163           char* check_null;
    164           opts.cpu_to_lock = strtol(optarg, &check_null, 10);
    165           if (*check_null) {
    166             errx(1, "ERROR: Args %s is not a valid integer.", optarg);
    167           }
    168         } else {
    169           printf("ERROR: no argument specified for bionic_cpu\n");
    170           Usage();
    171         }
    172         break;
    173       case 'x':
    174         if (*optarg) {
    175           opts.xmlpath = optarg;
    176         } else {
    177           printf("ERROR: no argument specified for bionic_xml\n");
    178           Usage();
    179         }
    180         break;
    181       case 'a':
    182         if (*optarg) {
    183           opts.extra_benchmarks.push_back(optarg);
    184         } else {
    185           printf("ERROR: no argument specified for bionic_extra\n");
    186           Usage();
    187         }
    188         break;
    189       case 'i':
    190         if (*optarg){
    191           char* check_null;
    192           opts.num_iterations = strtol(optarg, &check_null, 10);
    193           if (*check_null != '\0' or opts.num_iterations < 0) {
    194             errx(1, "ERROR: Args %s is not a valid number of iterations.", optarg);
    195           }
    196         } else {
    197           printf("ERROR: no argument specified for bionic_iterations\n");
    198           Usage();
    199         }
    200         break;
    201       case 'h':
    202         Usage();
    203         break;
    204       case '?':
    205         break;
    206       default:
    207         exit(1);
    208     }
    209   }
    210   return opts;
    211 }
    212 
    213 // This is a wrapper for every function call for per-benchmark cpu pinning.
    214 void LockAndRun(benchmark::State& state, benchmark_func_t func_to_bench, int cpu_to_lock) {
    215   if (cpu_to_lock >= 0) LockToCPU(cpu_to_lock);
    216 
    217   // To avoid having to link against Google benchmarks in libutil,
    218   // benchmarks are kept without parameter information, necessitating this cast.
    219   reinterpret_cast<void(*) (benchmark::State&)>(func_to_bench)(state);
    220 }
    221 
    222 static constexpr char kOnebufManualStr[] = "AT_ONEBUF_MANUAL_ALIGN_";
    223 static constexpr char kTwobufManualStr[] = "AT_TWOBUF_MANUAL_ALIGN1_";
    224 
    225 static bool ParseOnebufManualStr(std::string& arg, args_vector_t* to_populate) {
    226   // The format of this is:
    227   //   AT_ONEBUF_MANUAL_ALIGN_XX_SIZE_YY
    228   // Where:
    229   //   XX is the alignment
    230   //   YY is the size
    231   // The YY size can be either a number or a string representing the pre-defined
    232   // sets of values:
    233   //   SMALL (for values between 1 and 256)
    234   //   MEDIUM (for values between 512 and 128KB)
    235   //   LARGE (for values between 256KB and 2048KB)
    236   int64_t align;
    237   int64_t size;
    238   char sizes[32] = { 0 };
    239   int ret;
    240 
    241   ret = sscanf(arg.c_str(), "AT_ONEBUF_MANUAL_ALIGN_%" SCNd64 "_SIZE_%" SCNd64,
    242                &align, &size);
    243   if (ret == 1) {
    244     ret = sscanf(arg.c_str(), "AT_ONEBUF_MANUAL_ALIGN_%" SCNd64 "_SIZE_"
    245                               "%" STRINGFY(sizeof(sizes)-1) "s", &align, sizes);
    246   }
    247   if (ret != 2) {
    248     return false;
    249   }
    250 
    251   // Verify the alignment is powers of 2.
    252   if (align != 0 && (align & (align - 1)) != 0) {
    253     return false;
    254   }
    255 
    256   auto sit = kSizes.find(sizes);
    257   if (sit == kSizes.cend()) {
    258     to_populate->push_back({size, align});
    259   } else {
    260     for (auto ssize : sit->second) {
    261       to_populate->push_back({ssize, align});
    262     }
    263   }
    264   return true;
    265 }
    266 
    267 static bool ParseTwobufManualStr(std::string& arg, args_vector_t* to_populate) {
    268   // The format of this is:
    269   //   AT_TWOBUF_MANUAL_ALIGN1_XX_ALIGN2_YY_SIZE_ZZ
    270   // Where:
    271   //   XX is the alignment of the first argument
    272   //   YY is the alignment of the second argument
    273   //   ZZ is the size
    274   // The ZZ size can be either a number or a string representing the pre-defined
    275   // sets of values:
    276   //   SMALL (for values between 1 and 256)
    277   //   MEDIUM (for values between 512 and 128KB)
    278   //   LARGE (for values between 256KB and 2048KB)
    279   int64_t align1;
    280   int64_t align2;
    281   int64_t size;
    282   char sizes[32] = { 0 };
    283   int ret;
    284 
    285   ret = sscanf(arg.c_str(), "AT_TWOBUF_MANUAL_ALIGN1_%" SCNd64 "_ALIGN2_%" SCNd64 "_SIZE_%" SCNd64,
    286                             &align1, &align2, &size);
    287   if (ret == 2) {
    288     ret = sscanf(arg.c_str(), "AT_TWOBUF_MANUAL_ALIGN1_%" SCNd64 "_ALIGN2_%" SCNd64 "_SIZE_"
    289                                "%" STRINGFY(sizeof(sizes)-1) "s",
    290                                &align1, &align2, sizes);
    291   }
    292   if (ret != 3) {
    293     return false;
    294   }
    295 
    296   // Verify the alignments are powers of 2.
    297   if ((align1 != 0 && (align1 & (align1 - 1)) != 0)
    298       || (align2 != 0 && (align2 & (align2 - 1)) != 0)) {
    299     return false;
    300   }
    301 
    302   auto sit = kSizes.find(sizes);
    303   if (sit == kSizes.cend()) {
    304     to_populate->push_back({size, align1, align2});
    305   } else {
    306     for (auto ssize : sit->second) {
    307       to_populate->push_back({ssize, align1, align2});
    308     }
    309   }
    310   return true;
    311 }
    312 
    313 args_vector_t* ResolveArgs(args_vector_t* to_populate, std::string args,
    314                            std::map<std::string, args_vector_t>& args_shorthand) {
    315   // args is either a space-separated list of ints, a macro name, or
    316   // special free form macro.
    317   // To ease formatting in XML files, args is left and right trimmed.
    318   if (args_shorthand.count(args)) {
    319     return &args_shorthand[args];
    320   }
    321   // Check for free form macro.
    322   if (android::base::StartsWith(args, kOnebufManualStr)) {
    323     if (!ParseOnebufManualStr(args, to_populate)) {
    324       errx(1, "ERROR: Bad format of macro %s, should be AT_ONEBUF_MANUAL_ALIGN_XX_SIZE_YY",
    325            args.c_str());
    326     }
    327     return to_populate;
    328   } else if (android::base::StartsWith(args, kTwobufManualStr)) {
    329     if (!ParseTwobufManualStr(args, to_populate)) {
    330       errx(1,
    331            "ERROR: Bad format of macro %s, should be AT_TWOBUF_MANUAL_ALIGN1_XX_ALIGNE2_YY_SIZE_ZZ",
    332            args.c_str());
    333     }
    334     return to_populate;
    335   }
    336 
    337   to_populate->push_back(std::vector<int64_t>());
    338   std::stringstream sstream(args);
    339   std::string argstr;
    340   while (sstream >> argstr) {
    341     char* check_null;
    342     int converted = static_cast<int>(strtol(argstr.c_str(), &check_null, 10));
    343     if (*check_null) {
    344       errx(1, "ERROR: Args str %s contains an invalid macro or int.", args.c_str());
    345     }
    346     (*to_populate)[0].push_back(converted);
    347   }
    348   return to_populate;
    349 }
    350 
    351 void RegisterGoogleBenchmarks(bench_opts_t primary_opts, bench_opts_t secondary_opts,
    352                               const std::string& fn_name, args_vector_t* run_args) {
    353   if (g_str_to_func.find(fn_name) == g_str_to_func.end()) {
    354     errx(1, "ERROR: No benchmark for function %s", fn_name.c_str());
    355   }
    356   long iterations_to_use = primary_opts.num_iterations ? primary_opts.num_iterations :
    357                                                          secondary_opts.num_iterations;
    358   int cpu_to_use = -1;
    359   if (primary_opts.cpu_to_lock >= 0) {
    360     cpu_to_use = primary_opts.cpu_to_lock;
    361 
    362   } else if (secondary_opts.cpu_to_lock >= 0) {
    363     cpu_to_use = secondary_opts.cpu_to_lock;
    364   }
    365 
    366   benchmark_func_t benchmark_function = g_str_to_func.at(fn_name).first;
    367   for (const std::vector<int64_t>& args : (*run_args)) {
    368     auto registration = benchmark::RegisterBenchmark(fn_name.c_str(), LockAndRun,
    369                                                      benchmark_function,
    370                                                      cpu_to_use)->Args(args);
    371     if (iterations_to_use > 0) {
    372       registration->Iterations(iterations_to_use);
    373     }
    374   }
    375 }
    376 
    377 void RegisterCliBenchmarks(bench_opts_t cmdline_opts,
    378                            std::map<std::string, args_vector_t>& args_shorthand) {
    379   // Register any of the extra benchmarks that were specified in the options.
    380   args_vector_t arg_vector;
    381   args_vector_t* run_args = &arg_vector;
    382   for (const std::string& extra_fn : cmdline_opts.extra_benchmarks) {
    383     android::base::Trim(extra_fn);
    384     size_t first_space_pos = extra_fn.find(' ');
    385     std::string fn_name = extra_fn.substr(0, first_space_pos);
    386     std::string cmd_args;
    387     if (first_space_pos != std::string::npos) {
    388       cmd_args = extra_fn.substr(extra_fn.find(' ') + 1);
    389     } else {
    390       cmd_args = "";
    391     }
    392     run_args = ResolveArgs(run_args, cmd_args, args_shorthand);
    393     RegisterGoogleBenchmarks(bench_opts_t(), cmdline_opts, fn_name, run_args);
    394 
    395     run_args = &arg_vector;
    396     arg_vector.clear();
    397   }
    398 }
    399 
    400 int RegisterXmlBenchmarks(bench_opts_t cmdline_opts,
    401                           std::map<std::string, args_vector_t>& args_shorthand) {
    402   // Structure of the XML file:
    403   // - Element "fn"           Function to benchmark.
    404   // - - Element "iterations" Number of iterations to run. Leaving this blank uses
    405   //                          Google benchmarks' convergence heuristics.
    406   // - - Element "cpu"        CPU to isolate to, if any.
    407   // - - Element "args"       Whitespace-separated list of per-function integer arguments, or
    408   //                          one of the macros defined in util.h.
    409   tinyxml2::XMLDocument doc;
    410   if (doc.LoadFile(cmdline_opts.xmlpath.c_str()) != tinyxml2::XML_SUCCESS) {
    411     doc.PrintError();
    412     return doc.ErrorID();
    413   }
    414 
    415   // Read and register the functions.
    416   tinyxml2::XMLNode* fn = doc.FirstChildElement("fn");
    417   while (fn) {
    418     if (fn == fn->ToComment()) {
    419       // Skip comments.
    420       fn = fn->NextSibling();
    421       continue;
    422     }
    423 
    424     auto fn_elem = fn->FirstChildElement("name");
    425     if (!fn_elem) {
    426       errx(1, "ERROR: Malformed XML entry: missing name element.");
    427     }
    428     std::string fn_name = fn_elem->GetText();
    429     if (fn_name.empty()) {
    430       errx(1, "ERROR: Malformed XML entry: error parsing name text.");
    431     }
    432     auto* xml_args = fn->FirstChildElement("args");
    433     args_vector_t arg_vector;
    434     args_vector_t* run_args = ResolveArgs(&arg_vector,
    435                                           xml_args ? android::base::Trim(xml_args->GetText()) : "",
    436                                           args_shorthand);
    437 
    438     // XML values for CPU and iterations take precedence over those passed in via CLI.
    439     bench_opts_t xml_opts{};
    440     auto* num_iterations_elem = fn->FirstChildElement("iterations");
    441     if (num_iterations_elem) {
    442       int temp;
    443       num_iterations_elem->QueryIntText(&temp);
    444       xml_opts.num_iterations = temp;
    445     }
    446     auto* cpu_to_lock_elem = fn->FirstChildElement("cpu");
    447     if (cpu_to_lock_elem) {
    448       int temp;
    449       cpu_to_lock_elem->QueryIntText(&temp);
    450       xml_opts.cpu_to_lock = temp;
    451     }
    452 
    453     RegisterGoogleBenchmarks(xml_opts, cmdline_opts, fn_name, run_args);
    454 
    455     fn = fn->NextSibling();
    456   }
    457   return 0;
    458 }
    459 
    460 static void SetArgs(const std::vector<int>& sizes, args_vector_t* args) {
    461   for (int size : sizes) {
    462     args->push_back({size});
    463   }
    464 }
    465 
    466 static void SetArgs(const std::vector<int>& sizes, int align, args_vector_t* args) {
    467   for (int size : sizes) {
    468     args->push_back({size, align});
    469   }
    470 }
    471 
    472 
    473 static void SetArgs(const std::vector<int>& sizes, int align1, int align2, args_vector_t* args) {
    474   for (int size : sizes) {
    475     args->push_back({size, align1, align2});
    476   }
    477 }
    478 
    479 static args_vector_t GetArgs(const std::vector<int>& sizes) {
    480   args_vector_t args;
    481   SetArgs(sizes, &args);
    482   return args;
    483 }
    484 
    485 static args_vector_t GetArgs(const std::vector<int>& sizes, int align) {
    486   args_vector_t args;
    487   SetArgs(sizes, align, &args);
    488   return args;
    489 }
    490 
    491 static args_vector_t GetArgs(const std::vector<int>& sizes, int align1, int align2) {
    492   args_vector_t args;
    493   SetArgs(sizes, align1, align2, &args);
    494   return args;
    495 }
    496 
    497 std::map<std::string, args_vector_t> GetShorthand() {
    498   std::vector<int> all_sizes(kSmallSizes);
    499   all_sizes.insert(all_sizes.end(), kMediumSizes.begin(), kMediumSizes.end());
    500   all_sizes.insert(all_sizes.end(), kLargeSizes.begin(), kLargeSizes.end());
    501 
    502   std::map<std::string, args_vector_t> args_shorthand {
    503     {"AT_COMMON_SIZES", GetArgs(kCommonSizes)},
    504     {"AT_SMALL_SIZES", GetArgs(kSmallSizes)},
    505     {"AT_MEDIUM_SIZES", GetArgs(kMediumSizes)},
    506     {"AT_LARGE_SIZES", GetArgs(kLargeSizes)},
    507     {"AT_ALL_SIZES", GetArgs(all_sizes)},
    508 
    509     {"AT_ALIGNED_ONEBUF", GetArgs(kCommonSizes, 0)},
    510     {"AT_ALIGNED_ONEBUF_SMALL", GetArgs(kSmallSizes, 0)},
    511     {"AT_ALIGNED_ONEBUF_MEDIUM", GetArgs(kMediumSizes, 0)},
    512     {"AT_ALIGNED_ONEBUF_LARGE", GetArgs(kLargeSizes, 0)},
    513     {"AT_ALIGNED_ONEBUF_ALL", GetArgs(all_sizes, 0)},
    514 
    515     {"AT_ALIGNED_TWOBUF", GetArgs(kCommonSizes, 0, 0)},
    516     {"AT_ALIGNED_TWOBUF_SMALL", GetArgs(kSmallSizes, 0, 0)},
    517     {"AT_ALIGNED_TWOBUF_MEDIUM", GetArgs(kMediumSizes, 0, 0)},
    518     {"AT_ALIGNED_TWOBUF_LARGE", GetArgs(kLargeSizes, 0, 0)},
    519     {"AT_ALIGNED_TWOBUF_ALL", GetArgs(all_sizes, 0, 0)},
    520 
    521     // Do not exceed 512. that is about the largest number of properties
    522     // that can be created with the current property area size.
    523     {"NUM_PROPS", args_vector_t{ {1}, {4}, {16}, {64}, {128}, {256}, {512} }},
    524 
    525     {"MATH_COMMON", args_vector_t{ {0}, {1}, {2}, {3} }},
    526     {"MATH_SINCOS_COMMON", args_vector_t{ {0}, {1}, {2}, {3}, {4}, {5}, {6}, {7} }},
    527   };
    528 
    529   args_vector_t args_onebuf;
    530   args_vector_t args_twobuf;
    531   for (int size : all_sizes) {
    532     args_onebuf.push_back({size, 0});
    533     args_twobuf.push_back({size, 0, 0});
    534     // Skip alignments on zero sizes.
    535     if (size == 0) {
    536       continue;
    537     }
    538     for (int align1 = 1; align1 <= 32; align1 <<= 1) {
    539       args_onebuf.push_back({size, align1});
    540       for (int align2 = 1; align2 <= 32; align2 <<= 1) {
    541         args_twobuf.push_back({size, align1, align2});
    542       }
    543     }
    544   }
    545   args_shorthand.emplace("AT_MANY_ALIGNED_ONEBUF", args_onebuf);
    546   args_shorthand.emplace("AT_MANY_ALIGNED_TWOBUF", args_twobuf);
    547 
    548   return args_shorthand;
    549 }
    550 
    551 static bool FileExists(const std::string& file) {
    552   struct stat st;
    553   return stat(file.c_str(), &st) != -1 && S_ISREG(st.st_mode);
    554 }
    555 
    556 void RegisterAllBenchmarks(const bench_opts_t& opts,
    557                            std::map<std::string, args_vector_t>& args_shorthand) {
    558   for (auto& entry : g_str_to_func) {
    559     auto& function_info = entry.second;
    560     args_vector_t arg_vector;
    561     args_vector_t* run_args = ResolveArgs(&arg_vector, function_info.second,
    562                                           args_shorthand);
    563     RegisterGoogleBenchmarks(bench_opts_t(), opts, entry.first, run_args);
    564   }
    565 }
    566 
    567 int main(int argc, char** argv) {
    568   std::map<std::string, args_vector_t> args_shorthand = GetShorthand();
    569   bench_opts_t opts = ParseOpts(argc, argv);
    570   std::vector<char*> new_argv(argc);
    571   SanitizeOpts(argc, argv, &new_argv);
    572 
    573   if (opts.xmlpath.empty()) {
    574     // Don't add the default xml file if a user is specifying the tests to run.
    575     if (opts.extra_benchmarks.empty()) {
    576       RegisterAllBenchmarks(opts, args_shorthand);
    577     }
    578   } else if (!FileExists(opts.xmlpath)) {
    579     // See if this is a file in the suites directory.
    580     std::string file(android::base::GetExecutableDirectory() + "/suites/" + opts.xmlpath);
    581     if (opts.xmlpath[0] == '/' || !FileExists(file)) {
    582       printf("Cannot find xml file %s: does not exist or is not a file.\n", opts.xmlpath.c_str());
    583       return 1;
    584     }
    585     opts.xmlpath = file;
    586   }
    587 
    588   if (!opts.xmlpath.empty()) {
    589     if (int err = RegisterXmlBenchmarks(opts, args_shorthand)) {
    590       return err;
    591     }
    592   }
    593   RegisterCliBenchmarks(opts, args_shorthand);
    594 
    595   // Set the thread priority to the maximum.
    596   if (setpriority(PRIO_PROCESS, 0, -20)) {
    597     perror("Failed to raise priority of process. Are you root?\n");
    598   }
    599 
    600   int new_argc = new_argv.size();
    601   benchmark::Initialize(&new_argc, new_argv.data());
    602   benchmark::RunSpecifiedBenchmarks();
    603 }
    604