1 #include <stdio.h>
2 #include <sys/time.h>
3 #include <getopt.h>
4 
5 #include <thread>
6 #include <iostream>
7 #include <iomanip>
8 
9 #include <sched.h>
10 
11 #include "Profiler.h"
12 
13 extern "C" void icache_test(long count, long step);
14 
15 static constexpr size_t MAX_CODE_SIZE = 128*1024;
16 static constexpr size_t CACHE_LINE_SIZE = 64;
17 static constexpr size_t MAX_ITERATIONS_COUNT = MAX_CODE_SIZE / CACHE_LINE_SIZE;
18 static constexpr size_t REPETITIONS = 0x800000L;
19 
20 
21 using namespace utils;
22 
23 static cpu_set_t g_cpu_set;
24 
printUsage(char * name)25 static void printUsage(char* name) {
26     std::string exec_name(name);
27     std::string usage(
28             "ICACHE is a command-line tool for testing the L1 instruction cache performance.\n"
29             "(Make sure security.perf_harden is set to 0)\n\n"
30             "Usages:\n"
31             "    ICACHE [options]\n"
32             "\n"
33             "Options:\n"
34             "   --help, -h\n"
35             "       print this message\n\n"
36             "   --affinity=N, -a N\n"
37             "       Specify which CPU the test should run on.\n\n"
38     );
39     const std::string from("ICACHE");
40     for (size_t pos = usage.find(from); pos != std::string::npos; pos = usage.find(from, pos)) {
41          usage.replace(pos, from.length(), exec_name);
42     }
43     printf("%s", usage.c_str());
44 }
45 
handleCommandLineArgments(int argc,char * argv[])46 static int handleCommandLineArgments(int argc, char* argv[]) {
47     static constexpr const char* OPTSTR = "ha:";
48     static const struct option OPTIONS[] = {
49             { "help",                 no_argument, 0, 'h' },
50             { "affinity",       required_argument, 0, 'a' },
51             { 0, 0, 0, 0 }  // termination of the option list
52     };
53     int opt;
54     int option_index = 0;
55     while ((opt = getopt_long(argc, argv, OPTSTR, OPTIONS, &option_index)) >= 0) {
56         std::string arg(optarg ? optarg : "");
57         switch (opt) {
58             default:
59             case 'h':
60                 printUsage(argv[0]);
61                 exit(0);
62                 break;
63             case 'a':
64                 size_t cpu = std::stoi(arg);
65                 if (cpu < std::thread::hardware_concurrency()) {
66                     CPU_SET(cpu, &g_cpu_set);
67                 } else {
68                     std::cerr << "N must be < " << std::thread::hardware_concurrency() << std::endl;
69                     exit(0);
70                 }
71                 break;
72         }
73     }
74     return optind;
75 }
76 
main(int argc,char * argv[])77 int main(int argc, char* argv[]) {
78     CPU_ZERO(&g_cpu_set);
79 
80     [[maybe_unused]] int option_index = handleCommandLineArgments(argc, argv);
81     [[maybe_unused]] int num_args = argc - option_index;
82 
83     if (CPU_COUNT(&g_cpu_set)) {
84         sched_setaffinity(gettid(), sizeof(g_cpu_set), &g_cpu_set);
85     }
86 
87     Profiler& profiler = Profiler::get();
88     profiler.resetEvents(Profiler::EV_CPU_CYCLES | Profiler::EV_L1I_RATES);
89 
90     if (!profiler.isValid()) {
91         fprintf(stderr, "performance counters not enabled. try \"setprop security.perf_harden 0\"\n");
92         exit(0);
93     }
94 
95     size_t const stepInBytes = 1024;    // 1 KiB steps
96     size_t const step = stepInBytes / CACHE_LINE_SIZE;
97 
98     std::cout << std::fixed << std::setprecision(2);
99 
100     printf("[KiB]\t[cyc]\t[refs]\t[MPKI]\t[ns]\n");
101 
102     Profiler::Counters counters;
103 
104     for (size_t i=step ; i <= MAX_ITERATIONS_COUNT ; i += step) {
105         profiler.reset();
106 
107         auto now = std::chrono::steady_clock::now();
108         profiler.start();
109         icache_test(REPETITIONS, i);
110         profiler.stop();
111         auto duration = std::chrono::steady_clock::now() - now;
112 
113         profiler.readCounters(&counters);
114 
115         std::cout << ((i*CACHE_LINE_SIZE)/1024) << "\t"
116             << counters.getCpuCycles()/double(REPETITIONS) << "\t"
117             << counters.getL1IReferences()/double(REPETITIONS) << "\t"
118             << counters.getMPKI(counters.getL1IMisses()) << "\t"
119             << duration.count()/double(REPETITIONS) << "\t"
120             << std::endl;
121     }
122 
123     return 0;
124 }
125