13 extern "C" void icache_test(long count, long step);
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;
21 using namespace utils;
23 static cpu_set_t g_cpu_set;
25 static void printUsage(char* name) {
26 std::string exec_name(name);
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"
35 " print this message\n\n"
36 " --affinity=N, -a N\n"
37 " Specify which CPU the test should run on.\n\n"
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);
43 printf("%s", usage.c_str());
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
55 while ((opt = getopt_long(argc, argv, OPTSTR, OPTIONS, &option_index)) >= 0) {
56 std::string arg(optarg ? optarg : "");
64 size_t cpu = std::stoi(arg);
65 if (cpu < std::thread::hardware_concurrency()) {
66 CPU_SET(cpu, &g_cpu_set);
68 std::cerr << "N must be < " << std::thread::hardware_concurrency() << std::endl;
77 int main(int argc, char* argv[]) {
80 [[maybe_unused]] int option_index = handleCommandLineArgments(argc, argv);
81 [[maybe_unused]] int num_args = argc - option_index;
83 if (CPU_COUNT(&g_cpu_set)) {
84 sched_setaffinity(gettid(), sizeof(g_cpu_set), &g_cpu_set);
87 Profiler& profiler = Profiler::get();
88 profiler.resetEvents(Profiler::EV_CPU_CYCLES | Profiler::EV_L1I_RATES);
90 if (!profiler.isValid()) {
91 fprintf(stderr, "performance counters not enabled. try \"setprop security.perf_harden 0\"\n");
95 size_t const stepInBytes = 1024; // 1 KiB steps
96 size_t const step = stepInBytes / CACHE_LINE_SIZE;
98 std::cout << std::fixed << std::setprecision(2);
100 printf("[KiB]\t[cyc]\t[refs]\t[MPKI]\t[ns]\n");
102 Profiler::Counters counters;
104 for (size_t i=step ; i <= MAX_ITERATIONS_COUNT ; i += step) {
107 auto now = std::chrono::steady_clock::now();
109 icache_test(REPETITIONS, i);
111 auto duration = std::chrono::steady_clock::now() - now;
113 profiler.readCounters(&counters);
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"