# SMC CPU power rails vs IOReport CPU Energy calibration (Mac17,8, macOS 27)

Can the SMC's CPU power rails stand in for IOReport `CPU Energy` on macOS 27, where the
IOReport counter only publishes in batches? This capture reads both together and compares
them over each publication span. On the Mac17,8 the six-rail sum read 0.896 to 0.997 of the
IOReport figure over four complete spans: slightly low, never high.

Page: https://plottypus.com/data/smc-rails-calibration ·
Write-up: https://plottypus.com/blog/macos-27-cpu-power-0w · License: CC BY 4.0

## Result

| Span | IOReport `CPU Energy` | SMC rail sum (mean over the span) | Ratio | Rail samples |
|---:|---:|---:|---:|---:|
| 94.264 s | 31.30 W | 30.94 W | 0.989 | 353 |
| 27.313 s | 6.45 W | 6.11 W | 0.947 | 102 |
| 12.781 s | 15.06 W | 13.50 W | 0.896 | 48 |
| 2.226 s | 44.73 W | 44.58 W | 0.997 | 8 |

At two decimals the ratios are 0.99, 0.95, 0.90 and 1.00. plottypus states this as "within 10%
of IOReport". Unrounded, the 12.8 s span is 10.4% low (0.896). plottypus's own regression test
accepts 0.85 to 1.10.

A fifth span is in `spans.csv` with `complete_span = false`. The capture began 14.2 s into
that 25.9 s span, so the rails cover less than half of it, and its ratio (0.370) means
nothing. The last 54 s of the capture fall after the final publication and belong to no span.

Four spans on one machine is a small sample. It is enough to use the rails as a live
reading on this model. It is not enough to call them exact, and it says nothing about any
other Mac: the rail names are undocumented and differ between models.

## Machine and capture

MacBook Pro Mac17,8, Apple M5 Pro (6 Super + 12 Performance cores, 20-core GPU, 24 GB),
macOS 27.0 (build 26A428). No sudo. Captured on 2026-09-23 from 18:00:08 to 18:03:31 IST
(+05:30), 760 rows over 202.5 s (one about every 0.27 s), through idle and an 18-thread load.

The probe (`calib`, from the plottypus IOReport spike) subscribes to the IOReport
"Energy Model" group and opens `AppleSMC` (the 80-byte struct, selector 2). Each row:
`IOReportCreateSamples`, `mach_absolute_time()`, the `CPU Energy` running total and its
driver timestamp (byte 24 of `RawElements`), the `GPU Energy` total, then eleven SMC keys,
each decoded from `flt` to watts. A failed key read would print `-1`; none did. The published
single-file SMC reader is https://plottypus.com/media/post/smc-rails.c.

## Files

| File | Rows | One row is |
|---|---:|---|
| `spans.csv` | 5 | one `CPU Energy` publication span (4 complete, 1 partial) |
| `samples.csv` | 760 | one probe sample |

### `spans.csv` columns

| Column | Unit | Meaning |
|---|---|---|
| `span` | | 0-based span index |
| `start_driver_ts_ticks`, `end_driver_ts_ticks` | mach ticks | the two `CPU Energy` publications that bound the span |
| `span_s` | s | `(end − start) × 125/3 ns` |
| `ioreport_cpu_energy_j` | J | `CPU Energy` at the end minus at the start |
| `ioreport_cpu_w` | W | `ioreport_cpu_energy_j / span_s` |
| `rail_samples` | | rows whose sample time falls in `(start, end]` |
| `smc_rail_sum_mean_w` | W | mean of `cpu_rail_sum_w` over those rows |
| `ratio_rails_to_ioreport` | | `smc_rail_sum_mean_w / ioreport_cpu_w` |
| `smc_pstr_max_w` | W | highest `PSTR` (system power) over those rows |
| `complete_span` | bool | `false` when the span began before the capture |
| `span_start_rel_capture_s` | s | span start minus the first sample time (negative: before the capture) |

### `samples.csv` columns

| Column | Unit | Meaning |
|---|---|---|
| `sample` | | 0-based index |
| `elapsed_s` | s | since the first sample, from `mach_ticks` |
| `unix_time_s` | s | UTC epoch. Anchored on the capture file's last write (the final row), good to about ±0.1 s |
| `mach_ticks` | mach ticks | `mach_absolute_time()` right after the IOReport sample |
| `cpu_energy_raw_mj` | mJ | IOReport `CPU Energy` running total |
| `cpu_energy_j` | J | the same in joules |
| `cpu_energy_driver_ts_ticks` | mach ticks | driver timestamp of that total |
| `gpu_energy_raw_nj` | nJ | IOReport `GPU Energy` running total |
| `smc_ppmc_w`, `smc_phpc_w`, `smc_ppsc_w`, `smc_p3f2_w` | W | other SMC power keys, recorded but not used (meaning unknown) |
| `smc_pp0b_w`, `smc_pp4b_w` | W | CPU rails. From load tests, they follow the Super cluster |
| `smc_pp1b_w`, `smc_pp7b_w` | W | CPU rails that follow the first Performance cluster |
| `smc_pr0b_w`, `smc_pr7b_w` | W | CPU rails that follow the second Performance cluster |
| `smc_pstr_w` | W | `PSTR`, total system power |
| `cpu_rail_sum_w` | W | `PP0b + PP4b + PP1b + PP7b + PR0b + PR7b` (exact sum of the printed values) |

The cluster mapping of the rails is inferred from load tests. Apple documents none of these keys.

## Things to know when you use the rails

- The SMC values can repeat for a read or two (see the first four rows of `samples.csv`). Average
  a few reads.
- At a load step the rails can lead `PSTR` for a read or two. Clamping CPU power against the
  current `PSTR` would cut real readings; clamp against a recent maximum instead.
- The first read after opening the SMC can return 0.0 for every rail. Treat that as "not
  populated yet", not 0 W.

## License and citation

CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/). Use it for anything, with credit.
This license covers the data files in this folder only, not the plottypus software.

> Sheth, L. (2026). SMC CPU power rails vs IOReport CPU Energy calibration (Mac17,8, macOS 27)
> (Version 1.0) [Data set]. plottypus.com. https://plottypus.com/data/smc-rails-calibration

Contact: Lay Sheth, hello@plottypus.com.

## Checksums (SHA-256)

```text
87517a5021330afa546ae010efbdab53489750d2c8ad4fc257b641cc9909ff5d  samples.csv
6953e5e8c8fc20102128e0f9dc649fe4ad0d6d311ad425347d5b9516e41d3ac7  spans.csv
```

Verify with `shasum -a 256 -c` against these lines, or against https://plottypus.com/data/SHA256SUMS.
