1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 // This program provides processor power estimates. It does this by reading
8 // model-specific registers (MSRs) that are part Intel's Running Average Power
9 // Limit (RAPL) interface. These MSRs provide good quality estimates of the
10 // energy consumption of up to four system components:
11 // - PKG: the entire processor package;
12 // - PP0: the cores (a subset of the package);
13 // - PP1: the GPU (a subset of the package);
14 // - DRAM: main memory.
16 // For more details about RAPL, see section 14.9 of Volume 3 of the "Intel 64
17 // and IA-32 Architecture's Software Developer's Manual", Order Number 325384.
19 // This program exists because there are no existing tools on Mac that can
20 // obtain all four RAPL estimates. (|powermetrics| can obtain the package
21 // estimate, but not the others. Intel Power Gadget can obtain the package and
24 // On Linux |perf| can obtain all four estimates (as Joules, which are easily
25 // converted to Watts), but this program is implemented for Linux because it's
26 // not too hard to do, and that gives us multi-platform consistency.
28 // This program does not support Windows, unfortunately. It's not obvious how
29 // to access the RAPL MSRs on Windows.
31 // This program deliberately uses only standard libraries and avoids
32 // Mozilla-specific code, to make it easy to compile and test on different
51 #ifdef MOZ_CLANG_PLUGIN
52 # define MOZ_RUNINIT __attribute__((annotate("moz_global_var")))
57 //---------------------------------------------------------------------------
59 //---------------------------------------------------------------------------
61 // The value of argv[0] passed to main(). Used in error messages.
62 static const char* gArgv0
;
64 static void Abort(const char* aFormat
, ...) {
66 va_start(vargs
, aFormat
);
67 fprintf(stderr
, "%s: ", gArgv0
);
68 vfprintf(stderr
, aFormat
, vargs
);
69 fprintf(stderr
, "\n");
75 static void CmdLineAbort(const char* aMsg
) {
77 fprintf(stderr
, "%s: %s\n", gArgv0
, aMsg
);
79 fprintf(stderr
, "Use --help for more information.\n");
83 // A special value that represents an estimate from an unsupported RAPL domain.
84 static const double kUnsupported_j
= -1.0;
86 // Print to stdout and flush it, so that the output appears immediately even if
87 // being redirected through |tee| or anything like that.
88 static void PrintAndFlush(const char* aFormat
, ...) {
90 va_start(vargs
, aFormat
);
91 vfprintf(stdout
, aFormat
, vargs
);
97 //---------------------------------------------------------------------------
99 //---------------------------------------------------------------------------
101 #if defined(__APPLE__)
103 // Because of the pkg_energy_statistics_t::pkes_version check below, the
104 // earliest OS X version this code will work with is 10.9.0 (xnu-2422.1.72).
106 # include <sys/types.h>
107 # include <sys/sysctl.h>
109 // OS X has four kinds of system calls:
112 // 2. UNIX system calls;
113 // 3. machine-dependent calls;
114 // 4. diagnostic calls.
116 // (See "Mac OS X and iOS Internals" by Jonathan Levin for more details.)
118 // The last category has a single call named diagCall() or diagCall64(). Its
119 // mode is controlled by its first argument, and one of the modes allows access
120 // to the Intel RAPL MSRs.
122 // The interface to diagCall64() is not exported, so we have to import some
123 // definitions from the XNU kernel. All imported definitions are annotated with
124 // the XNU source file they come from, and information about what XNU versions
125 // they were introduced in and (if relevant) modified.
127 // The diagCall64() mode.
128 // From osfmk/i386/Diagnostics.h
129 // - In 10.8.4 (xnu-2050.24.15) this value was introduced. (In 10.8.3 the value
130 // 17 was used for dgGzallocTest.)
131 # define dgPowerStat 17
133 // From osfmk/i386/cpu_data.h
134 // - In 10.8.5 these values were introduced, along with core_energy_stat_t.
135 # define CPU_RTIME_BINS (12)
136 # define CPU_ITIME_BINS (CPU_RTIME_BINS)
138 // core_energy_stat_t and pkg_energy_statistics_t are both from
139 // osfmk/i386/Diagnostics.c.
140 // - In 10.8.4 (xnu-2050.24.15) both structs were introduced, but with many
142 // - In 10.8.5 (xnu-2050.48.11) both structs were substantially expanded, with
143 // numerous new fields.
144 // - In 10.9.0 (xnu-2422.1.72) pkg_energy_statistics_t::pkes_version was added.
145 // diagCall64(dgPowerStat) fills it with '1' in all versions since (up to
146 // 10.10.2 at time of writing).
147 // - in 10.10.2 (xnu-2782.10.72) core_energy_stat_t::gpmcs was conditionally
148 // added, if DIAG_ALL_PMCS is true. (DIAG_ALL_PMCS is not even defined in the
149 // source code, but it could be defined at compile-time via compiler flags.)
150 // pkg_energy_statistics_t::pkes_version did not change, though.
156 uint64_t crtimes
[CPU_RTIME_BINS
];
157 uint64_t citimes
[CPU_ITIME_BINS
];
158 uint64_t crtime_total
;
159 uint64_t citime_total
;
160 uint64_t cpu_idle_exits
;
164 # if DIAG_ALL_PMCS // Added in 10.10.2 (xnu-2782.10.72).
165 uint64_t gpmcs
[4]; // Added in 10.10.2 (xnu-2782.10.72).
166 # endif /* DIAG_ALL_PMCS */ // Added in 10.10.2 (xnu-2782.10.72).
167 } core_energy_stat_t
;
170 uint64_t pkes_version
; // Added in 10.9.0 (xnu-2422.1.72).
171 uint64_t pkg_cres
[2][7];
173 // This is read from MSR 0x606, which Intel calls MSR_RAPL_POWER_UNIT
174 // and XNU calls MSR_IA32_PKG_POWER_SKU_UNIT.
175 uint64_t pkg_power_unit
;
177 // These are the four fields for the four RAPL domains. For each field
180 // - the corresponding MSR number;
181 // - Intel's name for that MSR;
182 // - XNU's name for that MSR;
183 // - which Intel processors the MSR is supported on.
185 // The last of these is determined from chapter 35 of Volume 3 of the
186 // "Intel 64 and IA-32 Architecture's Software Developer's Manual",
187 // Order Number 325384. (Note that chapter 35 contradicts section 14.9
190 // 0x611 == MSR_PKG_ENERGY_STATUS == MSR_IA32_PKG_ENERGY_STATUS
191 // Atom (various), Sandy Bridge, Next Gen Xeon Phi (model 0x57).
194 // 0x639 == MSR_PP0_ENERGY_STATUS == MSR_IA32_PP0_ENERGY_STATUS
195 // Atom (various), Sandy Bridge, Next Gen Xeon Phi (model 0x57).
198 // 0x641 == MSR_PP1_ENERGY_STATUS == MSR_PP1_ENERGY_STATUS
199 // Sandy Bridge, Haswell.
202 // 0x619 == MSR_DRAM_ENERGY_STATUS == MSR_IA32_DDR_ENERGY_STATUS
203 // Xeon E5, Xeon E5 v2, Haswell/Haswell-E, Next Gen Xeon Phi (model
207 uint64_t llc_flushed_cycles
;
208 uint64_t ring_ratio_instantaneous
;
209 uint64_t IA_frequency_clipping_cause
;
210 uint64_t GT_frequency_clipping_cause
;
211 uint64_t pkg_idle_exits
;
212 uint64_t pkg_rtimes
[CPU_RTIME_BINS
];
213 uint64_t pkg_itimes
[CPU_ITIME_BINS
];
214 uint64_t mbus_delay_time
;
215 uint64_t mint_delay_time
;
217 core_energy_stat_t cest
[];
218 } pkg_energy_statistics_t
;
220 static int diagCall64(uint64_t aMode
, void* aBuf
) {
221 // We cannot use syscall() here because it doesn't work with diagnostic
222 // system calls -- it raises SIGSYS if you try. So we have to use asm.
225 // The 0x40000 prefix indicates it's a diagnostic system call. The 0x01
226 // suffix indicates the syscall number is 1, which also happens to be the
227 // only diagnostic system call. See osfmk/mach/i386/syscall_sw.h for more
229 static const uint64_t diagCallNum
= 0x4000001;
232 __asm__
__volatile__(
235 // Return value goes in "a" (%rax).
236 : /* outputs */ "=a"(rv
)
238 // The syscall number goes in "0", a synonym (from outputs) for "a"
239 // (%rax). The syscall arguments go in "D" (%rdi) and "S" (%rsi).
240 : /* inputs */ "0"(diagCallNum
), "D"(aMode
), "S"(aBuf
)
242 // The |syscall| instruction clobbers %rcx, %r11, and %rflags ("cc"). And
243 // this particular syscall also writes memory (aBuf).
244 : /* clobbers */ "rcx", "r11", "cc", "memory");
247 # error Sorry, only x86-64 is supported
251 static void diagCall64_dgPowerStat(pkg_energy_statistics_t
* aPkes
) {
252 static const uint64_t supported_version
= 1;
254 // Write an unsupported version number into pkes_version so that the check
255 // below cannot succeed by dumb luck.
256 aPkes
->pkes_version
= supported_version
- 1;
258 // diagCall64() returns 1 on success, and 0 on failure (which can only happen
259 // if the mode is unrecognized, e.g. in 10.7.x or earlier versions).
260 if (diagCall64(dgPowerStat
, aPkes
) != 1) {
261 Abort("diagCall64() failed");
264 if (aPkes
->pkes_version
!= 1) {
265 Abort("unexpected pkes_version: %llu", aPkes
->pkes_version
);
270 bool mIsGpuSupported
; // Is the GPU domain supported by the processor?
271 bool mIsRamSupported
; // Is the RAM domain supported by the processor?
273 // The DRAM domain on Haswell servers has a fixed energy unit (1/65536 J ==
274 // 15.3 microJoules) which is different to the power unit MSR. (See the
275 // "Intel Xeon Processor E5-1600 and E5-2600 v3 Product Families, Volume 2 of
276 // 2, Registers" datasheet, September 2014, Reference Number: 330784-001.)
277 // This field records whether the quirk is present.
278 bool mHasRamUnitsQuirk
;
280 // The abovementioned 15.3 microJoules value.
281 static const double kQuirkyRamJoulesPerTick
;
283 // The previous sample's MSR values.
284 uint64_t mPrevPkgTicks
;
285 uint64_t mPrevPp0Ticks
;
286 uint64_t mPrevPp1Ticks
;
287 uint64_t mPrevDdrTicks
;
289 // The struct passed to diagCall64().
290 pkg_energy_statistics_t
* mPkes
;
293 RAPL() : mHasRamUnitsQuirk(false) {
294 // Work out which RAPL MSRs this CPU model supports.
296 size_t size
= sizeof(cpuModel
);
297 if (sysctlbyname("machdep.cpu.model", &cpuModel
, &size
, NULL
, 0) != 0) {
298 Abort("sysctlbyname(\"machdep.cpu.model\") failed");
301 // This is similar to arch/x86/kernel/cpu/perf_event_intel_rapl.c in
304 // By linux-5.6.14/, this stuff had moved into
305 // arch/x86/events/intel/rapl.c, which references processor families in
306 // arch/x86/include/asm/intel-family.h.
308 case 0x2a: // Sandy Bridge
309 case 0x3a: // Ivy Bridge
310 // Supports package, cores, GPU.
311 mIsGpuSupported
= true;
312 mIsRamSupported
= false;
315 case 0x3f: // Haswell X
316 case 0x4f: // Broadwell X
317 case 0x55: // Skylake X
318 case 0x56: // Broadwell D
319 // Supports package, cores, RAM. Has the units quirk.
320 mIsGpuSupported
= false;
321 mIsRamSupported
= true;
322 mHasRamUnitsQuirk
= true;
325 case 0x2d: // Sandy Bridge X
326 case 0x3e: // Ivy Bridge X
327 // Supports package, cores, RAM.
328 mIsGpuSupported
= false;
329 mIsRamSupported
= true;
332 case 0x3c: // Haswell
333 case 0x3d: // Broadwell
334 case 0x45: // Haswell L
335 case 0x46: // Haswell G
336 case 0x47: // Broadwell G
337 // Supports package, cores, GPU, RAM.
338 mIsGpuSupported
= true;
339 mIsRamSupported
= true;
342 case 0x4e: // Skylake L
343 case 0x5e: // Skylake
344 case 0x8e: // Kaby Lake L
345 case 0x9e: // Kaby Lake
346 case 0x66: // Cannon Lake L
347 case 0x7d: // Ice Lake
348 case 0x7e: // Ice Lake L
349 case 0xa5: // Comet Lake
350 case 0xa6: // Comet Lake L
351 // Supports package, cores, GPU, RAM, PSYS.
352 // XXX: this tool currently doesn't measure PSYS.
353 mIsGpuSupported
= true;
354 mIsRamSupported
= true;
358 Abort("unknown CPU model: %d", cpuModel
);
362 // Get the maximum number of logical CPUs so that we know how big to make
365 size
= sizeof(logicalcpu_max
);
366 if (sysctlbyname("hw.logicalcpu_max", &logicalcpu_max
, &size
, NULL
, 0) !=
368 Abort("sysctlbyname(\"hw.logicalcpu_max\") failed");
371 // Over-allocate by 1024 bytes per CPU to allow for the uncertainty around
372 // core_energy_stat_t::gpmcs and for any other future extensions to that
373 // struct. (The fields we read all come before the core_energy_stat_t
374 // array, so it won't matter to us whether gpmcs is present or not.)
375 size_t pkesSize
= sizeof(pkg_energy_statistics_t
) +
376 logicalcpu_max
* sizeof(core_energy_stat_t
) +
377 logicalcpu_max
* 1024;
378 mPkes
= (pkg_energy_statistics_t
*)malloc(pkesSize
);
380 Abort("malloc() failed");
383 // Do an initial measurement so that the first sample's diffs are sensible.
384 double dummy1
, dummy2
, dummy3
, dummy4
;
385 EnergyEstimates(dummy1
, dummy2
, dummy3
, dummy4
);
388 ~RAPL() { free(mPkes
); }
390 static double Joules(uint64_t aTicks
, double aJoulesPerTick
) {
391 return double(aTicks
) * aJoulesPerTick
;
394 void EnergyEstimates(double& aPkg_J
, double& aCores_J
, double& aGpu_J
,
396 diagCall64_dgPowerStat(mPkes
);
398 // Bits 12:8 are the ESU.
399 // Energy measurements come in multiples of 1/(2^ESU).
400 uint32_t energyStatusUnits
= (mPkes
->pkg_power_unit
>> 8) & 0x1f;
401 double joulesPerTick
= ((double)1 / (1 << energyStatusUnits
));
403 aPkg_J
= Joules(mPkes
->pkg_energy
- mPrevPkgTicks
, joulesPerTick
);
404 aCores_J
= Joules(mPkes
->pp0_energy
- mPrevPp0Ticks
, joulesPerTick
);
405 aGpu_J
= mIsGpuSupported
406 ? Joules(mPkes
->pp1_energy
- mPrevPp1Ticks
, joulesPerTick
)
408 aRam_J
= mIsRamSupported
409 ? Joules(mPkes
->ddr_energy
- mPrevDdrTicks
,
410 mHasRamUnitsQuirk
? kQuirkyRamJoulesPerTick
414 mPrevPkgTicks
= mPkes
->pkg_energy
;
415 mPrevPp0Ticks
= mPkes
->pp0_energy
;
416 if (mIsGpuSupported
) {
417 mPrevPp1Ticks
= mPkes
->pp1_energy
;
419 if (mIsRamSupported
) {
420 mPrevDdrTicks
= mPkes
->ddr_energy
;
425 /* static */ const double RAPL::kQuirkyRamJoulesPerTick
= (double)1 / 65536;
427 //---------------------------------------------------------------------------
428 // Linux-specific code
429 //---------------------------------------------------------------------------
431 #elif defined(__linux__)
433 # include <linux/perf_event.h>
434 # include <sys/syscall.h>
436 // There is no glibc wrapper for this system call so we provide our own.
437 static int perf_event_open(struct perf_event_attr
* aAttr
, pid_t aPid
, int aCpu
,
438 int aGroupFd
, unsigned long aFlags
) {
439 return syscall(__NR_perf_event_open
, aAttr
, aPid
, aCpu
, aGroupFd
, aFlags
);
442 // Returns false if the file cannot be opened.
443 template <typename T
>
444 static bool ReadValueFromPowerFile(const char* aStr1
, const char* aStr2
,
445 const char* aStr3
, const char* aScanfString
,
447 // The filenames going into this buffer are under our control and the longest
448 // one is "/sys/bus/event_source/devices/power/events/energy-cores.scale".
449 // So 256 chars is plenty.
452 sprintf(filename
, "/sys/bus/event_source/devices/power/%s%s%s", aStr1
, aStr2
,
454 FILE* fp
= fopen(filename
, "r");
458 if (fscanf(fp
, aScanfString
, aOut
) != 1) {
459 Abort("fscanf() failed");
466 // This class encapsulates the reading of a single RAPL domain.
468 bool mIsSupported
; // Is the domain supported by the processor?
470 // These three are only set if |mIsSupported| is true.
471 double mJoulesPerTick
; // How many Joules each tick of the MSR represents.
472 int mFd
; // The fd through which the MSR is read.
473 double mPrevTicks
; // The previous sample's MSR value.
476 enum IsOptional
{ Optional
, NonOptional
};
478 Domain(const char* aName
, uint32_t aType
,
479 IsOptional aOptional
= NonOptional
) {
481 if (!ReadValueFromPowerFile("events/energy-", aName
, "", "event=%llx",
483 // Failure is allowed for optional domains.
484 if (aOptional
== NonOptional
) {
486 "failed to open file for non-optional domain '%s'\n"
487 "- Is your kernel version 3.14 or later, as required? "
488 "Run |uname -r| to see.",
491 mIsSupported
= false;
497 if (!ReadValueFromPowerFile("events/energy-", aName
, ".scale", "%lf",
499 Abort("failed to read from .scale file");
502 // The unit should be "Joules", so 128 chars should be plenty.
504 if (!ReadValueFromPowerFile("events/energy-", aName
, ".unit", "%127s",
506 Abort("failed to read from .unit file");
508 if (strcmp(unit
, "Joules") != 0) {
509 Abort("unexpected unit '%s' in .unit file", unit
);
512 struct perf_event_attr attr
;
513 memset(&attr
, 0, sizeof(attr
));
515 attr
.size
= uint32_t(sizeof(attr
));
516 attr
.config
= config
;
518 // Measure all processes/threads. The specified CPU doesn't matter.
519 mFd
= perf_event_open(&attr
, /* aPid = */ -1, /* aCpu = */ 0,
520 /* aGroupFd = */ -1, /* aFlags = */ 0);
523 "perf_event_open() failed\n"
524 "- Did you run as root (e.g. with |sudo|) or set\n"
525 " /proc/sys/kernel/perf_event_paranoid to 0, as required?");
537 double EnergyEstimate() {
539 return kUnsupported_j
;
543 if (read(mFd
, &thisTicks
, sizeof(uint64_t)) != sizeof(uint64_t)) {
544 Abort("read() failed");
547 uint64_t ticks
= thisTicks
- mPrevTicks
;
548 mPrevTicks
= thisTicks
;
549 double joules
= ticks
* mJoulesPerTick
;
563 if (!ReadValueFromPowerFile("type", "", "", "%u", &type
)) {
564 Abort("failed to read from type file");
567 mPkg
= new Domain("pkg", type
);
568 mCores
= new Domain("cores", type
);
569 mGpu
= new Domain("gpu", type
, Domain::Optional
);
570 mRam
= new Domain("ram", type
, Domain::Optional
);
571 if (!mPkg
|| !mCores
|| !mGpu
|| !mRam
) {
572 Abort("new Domain() failed");
583 void EnergyEstimates(double& aPkg_J
, double& aCores_J
, double& aGpu_J
,
585 aPkg_J
= mPkg
->EnergyEstimate();
586 aCores_J
= mCores
->EnergyEstimate();
587 aGpu_J
= mGpu
->EnergyEstimate();
588 aRam_J
= mRam
->EnergyEstimate();
594 //---------------------------------------------------------------------------
595 // Unsupported platforms
596 //---------------------------------------------------------------------------
598 # error Sorry, this platform is not supported
602 //---------------------------------------------------------------------------
604 //---------------------------------------------------------------------------
606 // The sample interval, measured in seconds.
607 static double gSampleInterval_sec
;
609 // The platform-specific RAPL-reading machinery.
612 // All the sampled "total" values, in Watts.
613 MOZ_RUNINIT
static std::vector
<double> gTotals_W
;
615 // Power = Energy / Time, where power is measured in Watts, Energy is measured
616 // in Joules, and Time is measured in seconds.
617 static double JoulesToWatts(double aJoules
) {
618 return aJoules
/ gSampleInterval_sec
;
621 // "Normalize" here means convert kUnsupported_j to zero so it can be used in
622 // additive expressions. All printed values are 5 or maybe 6 chars (though 6
623 // chars would require a value > 100 W, which is unlikely). Values above 1000 W
624 // are normalized to " n/a ", so 6 chars is the longest that may be printed.
625 static void NormalizeAndPrintAsWatts(char* aBuf
, double& aValue_J
) {
626 if (aValue_J
== kUnsupported_j
|| aValue_J
>= 1000) {
628 sprintf(aBuf
, "%s", " n/a ");
630 sprintf(aBuf
, "%5.2f", JoulesToWatts(aValue_J
));
634 static void SigAlrmHandler(int aSigNum
, siginfo_t
* aInfo
, void* aContext
) {
635 static int sampleNumber
= 1;
637 double pkg_J
, cores_J
, gpu_J
, ram_J
;
638 gRapl
->EnergyEstimates(pkg_J
, cores_J
, gpu_J
, ram_J
);
640 // We should have pkg and cores estimates, but might not have gpu and ram
642 assert(pkg_J
!= kUnsupported_j
);
643 assert(cores_J
!= kUnsupported_j
);
645 // This needs to be big enough to print watt values to two decimal places. 16
647 static const size_t kNumStrLen
= 16;
649 static char pkgStr
[kNumStrLen
], coresStr
[kNumStrLen
], gpuStr
[kNumStrLen
],
651 NormalizeAndPrintAsWatts(pkgStr
, pkg_J
);
652 NormalizeAndPrintAsWatts(coresStr
, cores_J
);
653 NormalizeAndPrintAsWatts(gpuStr
, gpu_J
);
654 NormalizeAndPrintAsWatts(ramStr
, ram_J
);
656 // Core and GPU power are a subset of the package power.
657 assert(pkg_J
>= cores_J
+ gpu_J
);
659 // Compute "other" (i.e. rest of the package) and "total" only after the
660 // other values have been normalized.
662 char otherStr
[kNumStrLen
];
663 double other_J
= pkg_J
- cores_J
- gpu_J
;
664 NormalizeAndPrintAsWatts(otherStr
, other_J
);
666 char totalStr
[kNumStrLen
];
667 double total_J
= pkg_J
+ ram_J
;
668 NormalizeAndPrintAsWatts(totalStr
, total_J
);
670 gTotals_W
.push_back(JoulesToWatts(total_J
));
672 // Print and flush so that the output appears immediately even if being
673 // redirected through |tee| or anything like that.
674 PrintAndFlush("#%02d %s W = %s (%s + %s + %s) + %s W\n", sampleNumber
++,
675 totalStr
, pkgStr
, coresStr
, gpuStr
, otherStr
, ramStr
);
678 static void Finish() {
679 size_t n
= gTotals_W
.size();
681 // This time calculation assumes that the timers are perfectly accurate which
682 // is not true but the inaccuracy should be small in practice.
683 double time
= n
* gSampleInterval_sec
;
686 printf("%d sample%s taken over a period of %.3f second%s\n", int(n
),
687 n
== 1 ? "" : "s", n
* gSampleInterval_sec
, time
== 1.0 ? "" : "s");
689 if (n
== 0 || n
== 1) {
694 double sum
= std::accumulate(gTotals_W
.begin(), gTotals_W
.end(), 0.0);
695 double mean
= sum
/ n
;
697 // Compute the *population* standard deviation:
699 // popStdDev = sqrt(Sigma(x - m)^2 / n)
701 // where |x| is the sum variable, |m| is the mean, and |n| is the
704 // This is different from the *sample* standard deviation, which divides by
705 // |n - 1|, and would be appropriate if we were using a random sample of a
706 // larger population.
707 double sumOfSquaredDeviations
= 0;
708 for (double& iter
: gTotals_W
) {
709 double deviation
= (iter
- mean
);
710 sumOfSquaredDeviations
+= deviation
* deviation
;
712 double popStdDev
= sqrt(sumOfSquaredDeviations
/ n
);
714 // Sort so that percentiles can be determined. We use the "Nearest Rank"
715 // method of determining percentiles, which is simplest to compute and which
716 // chooses values from those that appear in the input set.
717 std::sort(gTotals_W
.begin(), gTotals_W
.end());
720 printf("Distribution of 'total' values:\n");
721 printf(" mean = %5.2f W\n", mean
);
722 printf(" std dev = %5.2f W\n", popStdDev
);
723 printf(" 0th percentile = %5.2f W (min)\n", gTotals_W
[0]);
724 printf(" 5th percentile = %5.2f W\n", gTotals_W
[ceil(0.05 * n
) - 1]);
725 printf(" 25th percentile = %5.2f W\n", gTotals_W
[ceil(0.25 * n
) - 1]);
726 printf(" 50th percentile = %5.2f W\n", gTotals_W
[ceil(0.50 * n
) - 1]);
727 printf(" 75th percentile = %5.2f W\n", gTotals_W
[ceil(0.75 * n
) - 1]);
728 printf(" 95th percentile = %5.2f W\n", gTotals_W
[ceil(0.95 * n
) - 1]);
729 printf("100th percentile = %5.2f W (max)\n", gTotals_W
[n
- 1]);
734 static void SigIntHandler(int aSigNum
, siginfo_t
* aInfo
, void* aContext
) {
738 static void PrintUsage() {
740 "usage: rapl [options]\n"
744 " -h --help show this message\n"
745 " -i --sample-interval <N> sample every N ms [default=1000]\n"
746 " -n --sample-count <N> get N samples (0 means unlimited) "
749 #if defined(__APPLE__)
750 "On Mac this program can be run by any user.\n"
751 #elif defined(__linux__)
752 "On Linux this program can only be run by the super-user unless the "
754 "of /proc/sys/kernel/perf_event_paranoid is set to 0 or lower.\n"
756 # error Sorry, this platform is not supported
761 int main(int argc
, char** argv
) {
762 // Process command line options.
767 int sampleInterval_msec
= 1000;
770 struct option longOptions
[] = {
771 {"help", no_argument
, NULL
, 'h'},
772 {"sample-interval", required_argument
, NULL
, 'i'},
773 {"sample-count", required_argument
, NULL
, 'n'},
775 const char* shortOptions
= "hi:n:";
779 while ((c
= getopt_long(argc
, argv
, shortOptions
, longOptions
, NULL
)) != -1) {
786 sampleInterval_msec
= strtol(optarg
, &endPtr
, /* base = */ 10);
788 CmdLineAbort("sample interval is not an integer");
790 if (sampleInterval_msec
< 1 || sampleInterval_msec
> 3600000) {
791 CmdLineAbort("sample interval must be in the range 1..3600000 ms");
796 sampleCount
= strtol(optarg
, &endPtr
, /* base = */ 10);
798 CmdLineAbort("sample count is not an integer");
800 if (sampleCount
< 0 || sampleCount
> 1000000) {
801 CmdLineAbort("sample count must be in the range 0..1000000");
810 // The RAPL MSRs update every ~1 ms, but the measurement period isn't exactly
811 // 1 ms, which means the sample periods are not exact. "Power Measurement
812 // Techniques on Standard Compute Nodes: A Quantitative Comparison" by
813 // Hackenberg et al. suggests the following.
815 // "RAPL provides energy (and not power) consumption data without
816 // timestamps associated to each counter update. This makes sampling rates
817 // above 20 Samples/s unfeasible if the systematic error should be below
818 // 5%... Constantly polling the RAPL registers will both occupy a processor
819 // core and distort the measurement itself."
821 // So warn about this case.
822 if (sampleInterval_msec
< 50) {
824 "\nWARNING: sample intervals < 50 ms are likely to produce "
825 "inaccurate estimates\n\n");
827 gSampleInterval_sec
= double(sampleInterval_msec
) / 1000;
829 // Initialize the platform-specific RAPL reading machinery.
832 Abort("new RAPL() failed");
835 // Install the signal handlers.
838 memset(&sa
, 0, sizeof(sa
));
839 sa
.sa_flags
= SA_RESTART
| SA_SIGINFO
;
840 // The extra parens around (0) suppress a -Wunreachable-code warning on OS X
841 // where sigemptyset() is a macro that can never fail and always returns 0.
842 if (sigemptyset(&sa
.sa_mask
) < (0)) {
843 Abort("sigemptyset() failed");
845 sa
.sa_sigaction
= SigAlrmHandler
;
846 if (sigaction(SIGALRM
, &sa
, NULL
) < 0) {
847 Abort("sigaction(SIGALRM) failed");
849 sa
.sa_sigaction
= SigIntHandler
;
850 if (sigaction(SIGINT
, &sa
, NULL
) < 0) {
851 Abort("sigaction(SIGINT) failed");
855 struct itimerval timer
;
856 timer
.it_interval
.tv_sec
= sampleInterval_msec
/ 1000;
857 timer
.it_interval
.tv_usec
= (sampleInterval_msec
% 1000) * 1000;
858 timer
.it_value
= timer
.it_interval
;
859 if (setitimer(ITIMER_REAL
, &timer
, NULL
) < 0) {
860 Abort("setitimer() failed");
864 PrintAndFlush(" total W = _pkg_ (cores + _gpu_ + other) + _ram_ W\n");
867 if (sampleCount
== 0) {
872 for (int i
= 0; i
< sampleCount
; i
++) {