• 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 
275         if (iterNum > 0) {
276             registration->Iterations(iterNum);
277         }
278     } else {
279         BenchmarkFunc func = g_allBenchmarks.at(funcName).first;
280         for (const std::vector<int64_t> &args : (*runArgs)) {
281             // It will call LockAndRun(func, opts.cpuNum).
282             auto registration = benchmark::RegisterBenchmark(funcName.c_str(), LockAndRun, func, cpuNum)->Args(args);
283             if (iterNum > 0) {
284                 registration->Iterations(iterNum);
285             }
286         }
287     }
288 }
289 
RegisterAllBenchmarks(const bench_opts_t & opts,std::map<std::string,args_vector> & presetArgs)290 void RegisterAllBenchmarks(const bench_opts_t &opts, std::map<std::string, args_vector> &presetArgs)
291 {
292     for (auto &entry : g_allBenchmarks) {
293         auto &funcInfo = entry.second;
294         args_vector argVector;
295         args_vector *runArgs = ResolveArgs(&argVector, funcInfo.second, presetArgs);
296         RegisterSingleBenchmark(bench_opts_t(), opts, entry.first, runArgs);
297     }
298 
299     for (auto &entry : g_applyBenchmarks) {
300         args_vector *runArgs = nullptr;
301         RegisterSingleBenchmark(bench_opts_t(), opts, entry.first, runArgs);
302     }
303 }
304 
Trim(const std::string & str)305 std::string Trim(const std::string& str)
306 {
307     size_t first = str.find_first_not_of(' ');
308     if (std::string::npos == first) {
309         return "";
310     }
311     size_t last = str.find_last_not_of(' ');
312     return str.substr(first, (last - first + 1));
313 }
314 
RegisterJsonBenchmarks(const bench_opts_t & opts,std::map<std::string,args_vector> & presetArgs)315 int RegisterJsonBenchmarks(const bench_opts_t &opts, std::map<std::string, args_vector> &presetArgs)
316 {
317     char *file = nullptr;
318     cJSON *json = nullptr;
319     // Read JSON string from file
320     file = ReadJsonFile(opts.jsonPath.c_str());
321     if (file == nullptr) {
322         printf("fail to read file or no data read.\n");
323         return JOSN_ERROR_FILE_READ_FAILED;
324     }
325 
326     // Load JSON data
327     json = cJSON_Parse(file);
328     if (json == nullptr) {
329         printf("JSON parsing failed, incorrect JSON format.\n");
330         return JOSN_ERROR_JSON_FORMAT;
331     }
332 
333     // Parsing Fields
334     cJSON *item = cJSON_GetObjectItem(json, "InterfaceUsecases");
335     if (item) {
336         int arraySize = cJSON_GetArraySize(item); // Get the size of the array
337 
338         // Parsing each member in an array and register the functions.
339         for (int i = 0; i < arraySize; i++) {
340             cJSON *arrayItem = cJSON_GetArrayItem(item, i); // Extract array subscript object
341             if (arrayItem == nullptr) {
342                 continue;
343             }
344 
345             // Parsing data
346             cJSON *obj = cJSON_GetObjectItem(arrayItem, "name");
347             std::string fnName;
348             if (obj != nullptr) {
349                 fnName = std::string(obj->valuestring);
350             } else {
351                 printf("missing name element or error parsing name text\n");
352             }
353 
354             obj = cJSON_GetObjectItem(arrayItem, "args");
355             std::string jsonArgs;
356             args_vector argVector;
357             args_vector *runArgs = nullptr;
358             if (obj != nullptr) {
359                 jsonArgs = std::string(obj->valuestring);
360                 runArgs = ResolveArgs(&argVector, Trim(jsonArgs), presetArgs);
361             } else {
362                 runArgs = ResolveArgs(&argVector, "", presetArgs);
363             }
364 
365             bench_opts_t jsonOpts{};
366             obj = cJSON_GetObjectItem(arrayItem, "iterations");
367             if (obj != nullptr) {
368                 jsonOpts.iterNum = obj->valueint;
369             }
370 
371             obj = cJSON_GetObjectItem(arrayItem, "cpu");
372             if (obj != nullptr) {
373                 jsonOpts.cpuNum = obj->valueint;
374             }
375 
376             std::vector<std::string> matchedFuncNames;
377             for (const auto& muslbenchmark : g_allBenchmarks) {
378                 if (MatchFuncNameInJson(muslbenchmark.first, fnName)) {
379                     matchedFuncNames.push_back(muslbenchmark.first);
380                 }
381             }
382 
383             for (const auto& useapply : g_applyBenchmarks) {
384                 if (MatchFuncNameInJson(useapply.first, fnName)) {
385                     matchedFuncNames.push_back(useapply.first);
386                 }
387             }
388 
389             if (matchedFuncNames.empty()) {
390                 errx(1, "JSONERROR: No benchmark found for function like %s in matchedFuncNames", fnName.c_str());
391             }
392 
393             for (const auto& matchedFuncName : matchedFuncNames) {
394                 fnName = matchedFuncName;
395                 RegisterSingleBenchmark(jsonOpts, opts, fnName, runArgs);
396             }
397         }
398     }
399 
400     if (json != nullptr) {
401         cJSON_Delete(json);
402     }
403     return JSON_SUCCESS;
404 }
405 
IsRegularFileExists(const std::string & file)406 static bool IsRegularFileExists(const std::string& file)
407 {
408     struct stat st;
409     return stat(file.c_str(), &st) != -1 && S_ISREG(st.st_mode);
410 }
411 
main(int argc,char ** argv)412 int main(int argc, char **argv)
413 {
414     std::map<std::string, args_vector> presetArgs = GetPresetArgs();
415     bench_opts_t opts = ParseOptions(argc, argv);
416     std::vector<char *> argvAfterShift(argc);
417     ShiftOptions(argc, argv, &argvAfterShift);
418 
419     if (opts.jsonPath.empty()) {
420         RegisterAllBenchmarks(opts, presetArgs);
421     } else if (!IsRegularFileExists(opts.jsonPath)) {
422         std::string file("suites" + opts.jsonPath);
423         if (opts.jsonPath[0] == '/' || !IsRegularFileExists(file)) {
424             printf("Cannot find json file %s: does not exist or is not a file.\n", opts.jsonPath.c_str());
425             return 1;
426         }
427         opts.jsonPath = file;
428     }
429 
430     if (!opts.jsonPath.empty()) {
431         if (int err = RegisterJsonBenchmarks(opts, presetArgs)) {
432             return err;
433         }
434     }
435 
436     if (setpriority(PRIO_PROCESS, 0, -20)) {
437         perror("Set priority of process failed.\n");
438     }
439     int argcAfterShift = argvAfterShift.size();
440     benchmark::Initialize(&argcAfterShift, argvAfterShift.data());
441     benchmark::RunSpecifiedBenchmarks();
442 }
443