| /** @file kmp_stats_timing.cpp |
| * Timing functions |
| */ |
| |
| |
| //===----------------------------------------------------------------------===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is dual licensed under the MIT and the University of Illinois Open |
| // Source Licenses. See LICENSE.txt for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| |
| #include <stdlib.h> |
| #include <unistd.h> |
| |
| #include <iostream> |
| #include <iomanip> |
| #include <sstream> |
| |
| #include "kmp.h" |
| #include "kmp_stats_timing.h" |
| |
| using namespace std; |
| |
| #if KMP_HAVE_TICK_TIME |
| # if KMP_MIC |
| double tsc_tick_count::tick_time() |
| { |
| // pretty bad assumption of 1GHz clock for MIC |
| return 1/((double)1000*1.e6); |
| } |
| # elif KMP_ARCH_X86 || KMP_ARCH_X86_64 |
| # include <string.h> |
| // Extract the value from the CPUID information |
| double tsc_tick_count::tick_time() |
| { |
| static double result = 0.0; |
| |
| if (result == 0.0) |
| { |
| kmp_cpuid_t cpuinfo; |
| char brand[256]; |
| |
| __kmp_x86_cpuid(0x80000000, 0, &cpuinfo); |
| memset(brand, 0, sizeof(brand)); |
| int ids = cpuinfo.eax; |
| |
| for (unsigned int i=2; i<(ids^0x80000000)+2; i++) |
| __kmp_x86_cpuid(i | 0x80000000, 0, (kmp_cpuid_t*)(brand+(i-2)*sizeof(kmp_cpuid_t))); |
| |
| char * start = &brand[0]; |
| for (;*start == ' '; start++) |
| ; |
| |
| char * end = brand + KMP_STRLEN(brand) - 3; |
| uint64_t multiplier; |
| |
| if (*end == 'M') multiplier = 1000LL*1000LL; |
| else if (*end == 'G') multiplier = 1000LL*1000LL*1000LL; |
| else if (*end == 'T') multiplier = 1000LL*1000LL*1000LL*1000LL; |
| else |
| { |
| cout << "Error determining multiplier '" << *end << "'\n"; |
| exit (-1); |
| } |
| *end = 0; |
| while (*end != ' ') end--; |
| end++; |
| |
| double freq = strtod(end, &start); |
| if (freq == 0.0) |
| { |
| cout << "Error calculating frequency " << end << "\n"; |
| exit (-1); |
| } |
| |
| result = ((double)1.0)/(freq * multiplier); |
| } |
| return result; |
| } |
| # endif |
| #endif |
| |
| static bool useSI = true; |
| |
| // Return a formatted string after normalising the value into |
| // engineering style and using a suitable unit prefix (e.g. ms, us, ns). |
| std::string formatSI(double interval, int width, char unit) |
| { |
| std::stringstream os; |
| |
| if (useSI) |
| { |
| // Preserve accuracy for small numbers, since we only multiply and the positive powers |
| // of ten are precisely representable. |
| static struct { double scale; char prefix; } ranges[] = { |
| {1.e12,'f'}, |
| {1.e9, 'p'}, |
| {1.e6, 'n'}, |
| {1.e3, 'u'}, |
| {1.0, 'm'}, |
| {1.e-3,' '}, |
| {1.e-6,'k'}, |
| {1.e-9,'M'}, |
| {1.e-12,'G'}, |
| {1.e-15,'T'}, |
| {1.e-18,'P'}, |
| {1.e-21,'E'}, |
| {1.e-24,'Z'}, |
| {1.e-27,'Y'} |
| }; |
| |
| if (interval == 0.0) |
| { |
| os << std::setw(width-3) << std::right << "0.00" << std::setw(3) << unit; |
| return os.str(); |
| } |
| |
| bool negative = false; |
| if (interval < 0.0) |
| { |
| negative = true; |
| interval = -interval; |
| } |
| |
| for (int i=0; i<(int)(sizeof(ranges)/sizeof(ranges[0])); i++) |
| { |
| if (interval*ranges[i].scale < 1.e0) |
| { |
| interval = interval * 1000.e0 * ranges[i].scale; |
| os << std::fixed << std::setprecision(2) << std::setw(width-3) << std::right << |
| (negative ? -interval : interval) << std::setw(2) << ranges[i].prefix << std::setw(1) << unit; |
| |
| return os.str(); |
| } |
| } |
| } |
| os << std::setprecision(2) << std::fixed << std::right << std::setw(width-3) << interval << std::setw(3) << unit; |
| |
| return os.str(); |
| } |