• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) Huawei Technologies Co., Ltd. 2020-2023. All rights reserved.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include <benchmark/benchmark.h>
17 #include <err.h>
18 #include <getopt.h>
19 #include <cinttypes>
20 #include <cmath>
21 #include <sys/resource.h>
22 #include <sys/stat.h>
23 
24 #include <map>
25 #include <mutex>
26 #include <sstream>
27 #include <string>
28 #include <utility>
29 #include <regex>
30 #include <vector>
31 
32 #include "util.h"
33 
34 extern "C" {
35 #include "cJSON.h"
36 }
37 
38 static const std::vector<int> commonArgs {
39     8,
40     16,
41     32,
42     64,
43     512,
44     1 * K,
45     8 * K,
46     16 * K,
47     32 * K,
48     64 * K,
49     128 * K,
50 };
51 
52 std::map<std::string, std::pair<BenchmarkFunc, std::string>> g_allBenchmarks;
53 std::mutex g_benchmarkLock;
54 std::map<std::string, std::pair<BenchmarkFunc, ApplyBenchmarkFunc>> g_applyBenchmarks;
55 
56 using args_vector = std::vector<std::vector<int64_t>>;
57 
58 static struct option g_benchmarkLongOptions[] = {
59     {"musl_cpu", required_argument, nullptr, 'c'},
60     {"musl_iterations", required_argument, nullptr, 'i'},
61     {"musl_json", required_argument, nullptr, 'j'},
62     {"help", no_argument, nullptr, 'h'},
63     {nullptr, 0, nullptr, 0},
64     };
65 
PrintUsageAndExit()66 void PrintUsageAndExit()
67 {
68     printf("Usage:\n");
69     printf("musl_benchmarks [--musl_cpu=<cpu_to_isolate>]\n");
70     printf("                [--musl_iterations=<num_iter>]\n");
71     printf("                [--musl_json=<path_to_json>]\n");
72     printf("                [<original benchmark flags>]\n");
73     printf("benchmark flags:\n");
74 
75     int argc = 2;
76     char argv0[] = "musl_benchmark";
77     char argv1[] = "--help";
78     char *argv[3]{argv0, argv1, nullptr};
79     benchmark::Initialize(&argc, argv);
80     exit(1);
81 }
82 
ShiftOptions(int argc,char ** argv,std::vector<char * > * argvAfterShift)83 void ShiftOptions(int argc, char **argv, std::vector<char *> *argvAfterShift)
84 {
85     (*argvAfterShift)[0] = argv[0];
86     for (int i = 1; i < argc; ++i) {
87         char *optarg = argv[i];
88         size_t index = 0;
89         // Find if musl defined this arg.
90         while (g_benchmarkLongOptions[index].name && strncmp(g_benchmarkLongOptions[index].name, optarg + 2,
91             strlen(g_benchmarkLongOptions[index].name))) {
92             ++index;
93         }
94         // Not defined.
95         if (!g_benchmarkLongOptions[index].name) {
96             argvAfterShift->push_back(optarg);
97         } else if ((g_benchmarkLongOptions[index].has_arg == required_argument) && !strchr(optarg, '=')) {
98             i++;
99         }
100     }
101     argvAfterShift->push_back(nullptr);
102 }
103 
ParseOptions(int argc,char ** argv)104 bench_opts_t ParseOptions(int argc, char **argv)
105 {
106     bench_opts_t opts;
107     int opt;
108 
109     while ((opt = getopt_long(argc, argv, "c:i:j:h", g_benchmarkLongOptions, nullptr)) != -1) {
110         int decimal = 10;
111         switch (opt) {
112             case 'c':
113                 if (*optarg) {
114                     char *errorCheck;
115                     opts.cpuNum = strtol(optarg, &errorCheck, decimal);
116                     if (*errorCheck) {
117                         errx(1, "ERROR: Args %s is not a valid integer.", optarg);
118                     }
119                 } else {
120                     printf("ERROR: no argument specified for musl_cpu.\n");
121                     PrintUsageAndExit();
122                 }
123                 break;
124             case 'i':
125                 if (*optarg) {
126                     char *errorCheck;
127                     opts.iterNum = strtol(optarg, &errorCheck, decimal);
128                     if (*errorCheck != '\0' or opts.iterNum < 0) {
129                         errx(1, "ERROR: Args %s is not a valid number of iterations.", optarg);
130                     }
131                 } else {
132                     printf("ERROR: no argument specified for musl_iterations.\n");
133                     PrintUsageAndExit();
134                 }
135                 break;
136             case 'j':
137                 if (*optarg) {
138                     opts.jsonPath = optarg;
139                 } else {
140                     printf("ERROR: no argument specified for musl_json\n");
141                     PrintUsageAndExit();
142                 }
143                 break;
144             case 'h':
145                 PrintUsageAndExit();
146                 break;
147             case '?':
148                 break;
149             default:
150                 exit(1);
151         }
152     }
153     return opts;
154 }
155 
LockAndRun(benchmark::State & state,BenchmarkFunc func,int cpuNum)156 void LockAndRun(benchmark::State &state, BenchmarkFunc func, int cpuNum)
157 {
158 #if not defined __APPLE__
159     if (cpuNum >= 0) {
160         cpu_set_t cpuset;
161         CPU_ZERO(&cpuset);
162         CPU_SET(cpuNum, &cpuset);
163 
164         if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0) {
165             printf("lock CPU failed, ERROR:%s\n", strerror(errno));
166         }
167     }
168 #endif
169 
170     reinterpret_cast<void (*)(benchmark::State &)>(func)(state);
171 }
172 
ResolveArgs(args_vector * argsVector,std::string args,std::map<std::string,args_vector> & presetArgs)173 args_vector *ResolveArgs(args_vector *argsVector, std::string args,
174     std::map<std::string, args_vector> &presetArgs)
175 {
176     // Get it from preset args.
177     if (presetArgs.count(args)) {
178         return &presetArgs[args];
179     }
180 
181     // Convert string to int.
182     argsVector->push_back(std::vector<int64_t>());
183     std::stringstream sstream(args);
184     std::string argstr;
185     while (sstream >> argstr) {
186         char *errorCheck;
187         int converted = static_cast<int>(strtol(argstr.c_str(), &errorCheck, 10));
188         if (*errorCheck) {
189             errx(1, "ERROR: Args str %s contains an invalid macro or int.", args.c_str());
190         }
191         (*argsVector)[0].push_back(converted);
192     }
193     return argsVector;
194 }
195 
MatchFuncNameInJson(const std::string & str,const std::string & pattern)196 bool MatchFuncNameInJson(const std::string& str, const std::string& pattern)
197 {
198     size_t jsonwildcard = pattern.find("*");
199     if (jsonwildcard == std::string::npos) {
200         return str == pattern;
201     }
202     std::string prefix = pattern.substr(0, jsonwildcard);
203     std::string jsonFuncName = prefix + ".*";
204     std::regex re(jsonFuncName, std::regex::icase);
205     return std::regex_match(str, re);
206 }
207 
GetArgs(const std::vector<int> & sizes)208 static args_vector GetArgs(const std::vector<int> &sizes)
209 {
210     args_vector args;
211     for (int size : sizes) {
212         args.push_back({size});
213     }
214     return args;
215 }
216 
GetArgs(const std::vector<int> & sizes,int value)217 static args_vector GetArgs(const std::vector<int> &sizes, int value)
218 {
219     args_vector args;
220     for (int size : sizes) {
221         args.push_back({size, value});
222     }
223     return args;
224 }
225 
GetArgs(const std::vector<int> & sizes,int value1,int value2)226 static args_vector GetArgs(const std::vector<int> &sizes, int value1, int value2)
227 {
228     args_vector args;
229     for (int size : sizes) {
230         args.push_back({size, value1, value2});
231     }
232     return args;
233 }
234 
GetPresetArgs()235 std::map<std::string, args_vector> GetPresetArgs()
236 {
237     std::map<std::string, args_vector> presetArgs {
238         {"COMMON_ARGS", GetArgs(commonArgs)},
239         {"ALIGNED_ONEBUF", GetArgs(commonArgs, 0)},
240         {"ALIGNED_TWOBUF", GetArgs(commonArgs, 0, 0)},
241         {"BENCHMARK_5", args_vector{{0}, {1}, {2}, {3}, {4}}},
242         {"BENCHMARK_8", args_vector{{0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}}},
243         {"BENCHMARK_22", args_vector{{0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}, {10}, {11}, {12}, {13},
244                                      {14}, {15}, {16}, {17}, {18}, {19}, {20}, {21}}},
245     };
246 
247     return presetArgs;
248 }
249 
RegisterSingleBenchmark(bench_opts_t optsFromJson,bench_opts_t optsFromCommandLine,const std::string & funcName,args_vector * runArgs)250 void RegisterSingleBenchmark(bench_opts_t optsFromJson, bench_opts_t optsFromCommandLine,
251     const std::string &funcName, args_vector *runArgs)
252 {
253     bool isApplyUseCase = false;
254     if (g_allBenchmarks.find(funcName) != g_allBenchmarks.end()) {
255         isApplyUseCase = false;
256     } else if (g_applyBenchmarks.find(funcName) != g_applyBenchmarks.end()) {
257         isApplyUseCase = true;
258     } else {
259         errx(1, "ERROR: No benchmark for function %s", funcName.c_str());
260     }
261 
262     long iterNum = optsFromCommandLine.iterNum ? optsFromCommandLine.iterNum : optsFromJson.iterNum;
263     int cpuNum = -1;
264     if (optsFromCommandLine.cpuNum >= 0) {
265         cpuNum = optsFromCommandLine.cpuNum;
266     } else if (optsFromJson.cpuNum >= 0) {
267         cpuNum = optsFromJson.cpuNum;
268     }
269 
270     if (isApplyUseCase) {
271         BenchmarkFunc func = g_applyBenchmarks.at(funcName).first;
272         auto registration = benchmark::RegisterBenchmark(funcName.c_str(), LockAndRun, func,
273             cpuNum)->Apply(g_applyBenchmarks.at(funcName).second);
274         if (iterNum > 0) {
275             registration->Iterations(iterNum);
276         }
277     } else {
278         BenchmarkFunc func = g_allBenchmarks.at(funcName).first;
279         for (const std::vector<int64_t> &args : (*runArgs)) {
280             // It will call LockAndRun(func, opts.cpuNum).
281             auto registration = benchmark::RegisterBenchmark(funcName.c_str(), LockAndRun, func, cpuNum)->Args(args);
282             if (iterNum > 0) {
283                 registration->Iterations(iterNum);
284             }
285         }
286     }
287 }
288 
RegisterAllBenchmarks(const bench_opts_t & opts,std::map<std::string,args_vector> & presetArgs)289 void RegisterAllBenchmarks(const bench_opts_t &opts, std::map<std::string, args_vector> &presetArgs)
290 {
291     for (auto &entry : g_allBenchmarks) {
292         auto &funcInfo = entry.second;
293         args_vector argVector;
294         args_vector *runArgs = ResolveArgs(&argVector, funcInfo.second, presetArgs);
295         RegisterSingleBenchmark(bench_opts_t(), opts, entry.first, runArgs);
296     }
297 
298     for (auto &entry : g_applyBenchmarks) {
299         args_vector *runArgs = nullptr;
300         RegisterSingleBenchmark(bench_opts_t(), opts, entry.first, runArgs);
301     }
302 }
303 
Trim(const std::string & str)304 std::string Trim(const std::string& str)
305 {
306     size_t first = str.find_first_not_of(' ');
307     if (std::string::npos == first) {
308         return "";
309     }
310     size_t last = str.find_last_not_of(' ');
311     return str.substr(first, (last - first + 1));
312 }
313 
RegisterJsonBenchmarks(const bench_opts_t & opts,std::map<std::string,args_vector> & presetArgs)314 int RegisterJsonBenchmarks(const bench_opts_t &opts, std::map<std::string, args_vector> &presetArgs)
315 {
316     char *file = nullptr;
317     cJSON *json = nullptr;
318     // Read JSON string from file
319     file = ReadJsonFile(opts.jsonPath.c_str());
320     if (file == nullptr) {
321         printf("fail to read file or no data read.\n");
322         return JOSN_ERROR_FILE_READ_FAILED;
323     }
324 
325     // Load JSON data
326     json = cJSON_Parse(file);
327     if (json == nullptr) {
328         printf("JSON parsing failed, incorrect JSON format.\n");
329         return JOSN_ERROR_JSON_FORMAT;
330     }
331 
332     // Parsing Fields
333     cJSON *item = cJSON_GetObjectItem(json, "InterfaceUsecases");
334     if (item) {
335         int arraySize = cJSON_GetArraySize(item); // Get the size of the array
336 
337         // Parsing each member in an array and register the functions.
338         for (int i = 0; i < arraySize; i++) {
339             cJSON *arrayItem = cJSON_GetArrayItem(item, i); // Extract array subscript object
340             if (arrayItem == nullptr) {
341                 continue;
342             }
343 
344             // Parsing data
345             cJSON *obj = cJSON_GetObjectItem(arrayItem, "name");
346             std::string fnName;
347             if (obj != nullptr) {
348                 fnName = std::string(obj->valuestring);
349             } else {
350                 printf("missing name element or error parsing name text\n");
351             }
352 
353             obj = cJSON_GetObjectItem(arrayItem, "args");
354             std::string jsonArgs;
355             args_vector argVector;
356             args_vector *runArgs = nullptr;
357             if (obj != nullptr) {
358                 jsonArgs = std::string(obj->valuestring);
359                 runArgs = ResolveArgs(&argVector, Trim(jsonArgs), presetArgs);
360             } else {
361                 runArgs = ResolveArgs(&argVector, "", presetArgs);
362             }
363 
364             bench_opts_t jsonOpts{};
365             obj = cJSON_GetObjectItem(arrayItem, "iterations");
366             if (obj != nullptr) {
367                 jsonOpts.iterNum = obj->valueint;
368             }
369 
370             obj = cJSON_GetObjectItem(arrayItem, "cpu");
371             if (obj != nullptr) {
372                 jsonOpts.cpuNum = obj->valueint;
373             }
374 
375             std::vector<std::string> matchedFuncNames;
376             for (const auto& muslbenchmark : g_allBenchmarks) {
377                 if (MatchFuncNameInJson(muslbenchmark.first, fnName)) {
378                     matchedFuncNames.push_back(muslbenchmark.first);
379                 }
380             }
381 
382             for (const auto& useapply : g_applyBenchmarks) {
383                 if (MatchFuncNameInJson(useapply.first, fnName)) {
384                     matchedFuncNames.push_back(useapply.first);
385                 }
386             }
387 
388             if (matchedFuncNames.empty()) {
389                 errx(1, "JSONERROR: No benchmark found for function like %s in matchedFuncNames", fnName.c_str());
390             }
391 
392             for (const auto& matchedFuncName : matchedFuncNames) {
393                 fnName = matchedFuncName;
394                 RegisterSingleBenchmark(jsonOpts, opts, fnName, runArgs);
395             }
396         }
397     }
398 
399     if (json != nullptr) {
400         cJSON_Delete(json);
401     }
402     return JSON_SUCCESS;
403 }
404 
IsRegularFileExists(const std::string & file)405 static bool IsRegularFileExists(const std::string& file)
406 {
407     struct stat st;
408     return stat(file.c_str(), &st) != -1 && S_ISREG(st.st_mode);
409 }
410 
main(int argc,char ** argv)411 int main(int argc, char **argv)
412 {
413     std::map<std::string, args_vector> presetArgs = GetPresetArgs();
414     bench_opts_t opts = ParseOptions(argc, argv);
415     std::vector<char *> argvAfterShift(argc);
416     ShiftOptions(argc, argv, &argvAfterShift);
417 
418     if (opts.jsonPath.empty()) {
419         RegisterAllBenchmarks(opts, presetArgs);
420     } else if (!IsRegularFileExists(opts.jsonPath)) {
421         std::string file("suites" + opts.jsonPath);
422         if (opts.jsonPath[0] == '/' || !IsRegularFileExists(file)) {
423             printf("Cannot find json file %s: does not exist or is not a file.\n", opts.jsonPath.c_str());
424             return 1;
425         }
426         opts.jsonPath = file;
427     }
428 
429     if (!opts.jsonPath.empty()) {
430         if (int err = RegisterJsonBenchmarks(opts, presetArgs)) {
431             return err;
432         }
433     }
434 
435     if (setpriority(PRIO_PROCESS, 0, -20)) {
436         perror("Set priority of process failed.\n");
437     }
438     int argcAfterShift = argvAfterShift.size();
439     benchmark::Initialize(&argcAfterShift, argvAfterShift.data());
440     benchmark::RunSpecifiedBenchmarks();
441 }
442