1 /** @file kmp_stats_timing.cpp 2 * Timing functions 3 */ 4 5 6 //===----------------------------------------------------------------------===// 7 // 8 // The LLVM Compiler Infrastructure 9 // 10 // This file is dual licensed under the MIT and the University of Illinois Open 11 // Source Licenses. See LICENSE.txt for details. 12 // 13 //===----------------------------------------------------------------------===// 14 15 16 #include <stdlib.h> 17 #include <unistd.h> 18 19 #include <iostream> 20 #include <iomanip> 21 #include <sstream> 22 23 #include "kmp.h" 24 #include "kmp_stats_timing.h" 25 26 using namespace std; 27 28 #if KMP_HAVE_TICK_TIME 29 # if KMP_MIC 30 double tsc_tick_count::tick_time() 31 { 32 // pretty bad assumption of 1GHz clock for MIC 33 return 1/((double)1000*1.e6); 34 } 35 # elif KMP_ARCH_X86 || KMP_ARCH_X86_64 36 # include <string.h> 37 // Extract the value from the CPUID information 38 double tsc_tick_count::tick_time() 39 { 40 static double result = 0.0; 41 42 if (result == 0.0) 43 { 44 kmp_cpuid_t cpuinfo; 45 char brand[256]; 46 47 __kmp_x86_cpuid(0x80000000, 0, &cpuinfo); 48 memset(brand, 0, sizeof(brand)); 49 int ids = cpuinfo.eax; 50 51 for (unsigned int i=2; i<(ids^0x80000000)+2; i++) 52 __kmp_x86_cpuid(i | 0x80000000, 0, (kmp_cpuid_t*)(brand+(i-2)*sizeof(kmp_cpuid_t))); 53 54 char * start = &brand[0]; 55 for (;*start == ' '; start++) 56 ; 57 58 char * end = brand + KMP_STRLEN(brand) - 3; 59 uint64_t multiplier; 60 61 if (*end == 'M') multiplier = 1000LL*1000LL; 62 else if (*end == 'G') multiplier = 1000LL*1000LL*1000LL; 63 else if (*end == 'T') multiplier = 1000LL*1000LL*1000LL*1000LL; 64 else 65 { 66 cout << "Error determining multiplier '" << *end << "'\n"; 67 exit (-1); 68 } 69 *end = 0; 70 while (*end != ' ') end--; 71 end++; 72 73 double freq = strtod(end, &start); 74 if (freq == 0.0) 75 { 76 cout << "Error calculating frequency " << end << "\n"; 77 exit (-1); 78 } 79 80 result = ((double)1.0)/(freq * multiplier); 81 } 82 return result; 83 } 84 # endif 85 #endif 86 87 static bool useSI = true; 88 89 // Return a formatted string after normalising the value into 90 // engineering style and using a suitable unit prefix (e.g. ms, us, ns). 91 std::string formatSI(double interval, int width, char unit) 92 { 93 std::stringstream os; 94 95 if (useSI) 96 { 97 // Preserve accuracy for small numbers, since we only multiply and the positive powers 98 // of ten are precisely representable. 99 static struct { double scale; char prefix; } ranges[] = { 100 {1.e12,'f'}, 101 {1.e9, 'p'}, 102 {1.e6, 'n'}, 103 {1.e3, 'u'}, 104 {1.0, 'm'}, 105 {1.e-3,' '}, 106 {1.e-6,'k'}, 107 {1.e-9,'M'}, 108 {1.e-12,'G'}, 109 {1.e-15,'T'}, 110 {1.e-18,'P'}, 111 {1.e-21,'E'}, 112 {1.e-24,'Z'}, 113 {1.e-27,'Y'} 114 }; 115 116 if (interval == 0.0) 117 { 118 os << std::setw(width-3) << std::right << "0.00" << std::setw(3) << unit; 119 return os.str(); 120 } 121 122 bool negative = false; 123 if (interval < 0.0) 124 { 125 negative = true; 126 interval = -interval; 127 } 128 129 for (int i=0; i<(int)(sizeof(ranges)/sizeof(ranges[0])); i++) 130 { 131 if (interval*ranges[i].scale < 1.e0) 132 { 133 interval = interval * 1000.e0 * ranges[i].scale; 134 os << std::fixed << std::setprecision(2) << std::setw(width-3) << std::right << 135 (negative ? -interval : interval) << std::setw(2) << ranges[i].prefix << std::setw(1) << unit; 136 137 return os.str(); 138 } 139 } 140 } 141 os << std::setprecision(2) << std::fixed << std::right << std::setw(width-3) << interval << std::setw(3) << unit; 142 143 return os.str(); 144 } 145