aboutsummaryrefslogtreecommitdiff
path: root/runtime/src/kmp_stats_timing.cpp
blob: 2fcbaab756818952abfbe85cd9cb681210802d68 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/** @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 <iomanip>
#include <iostream>
#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.e21, 'y'},  {1.e18, 'z'},  {1.e15, 'a'},  {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();
}