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 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 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 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