| Copyright | (c) 2026 Jared Tobin |
|---|---|
| License | MIT |
| Maintainer | Jared Tobin <jared@ppad.tech> |
| Safe Haskell | Safe-Inferred |
| Language | Haskell2010 |
Censor.Meter
Description
Measurement abstraction.
A Meter wraps an IO action and returns a Word64 reading.
wallClock is available everywhere. On Linux, withCounter
provides performance-counter meters via perf_event_open: the
retired-instruction and retired-branch counts are deterministic
properties of the executed code, immune to the frequency, cache,
and scheduling noise that limits the wall clock on fast targets,
while the other counters help attribute a difference to a channel
(see Counter). withSplitMeter reads the cycle counter and the
wall clock over the same execution, splitting wall time into the
cycles the code took and the clock period they ran at.
Synopsis
- newtype Meter = Meter {}
- wallClock :: Meter
- data Counter
- data MeterError
- = Unsupported
- | OpenFailed !Int
- withCounter :: Counter -> (Meter -> IO a) -> IO a
- newtype SplitMeter = SplitMeter {}
- withSplitMeter :: (SplitMeter -> IO a) -> IO a
Meter type
A measurement device. runs measure m k actact k times
back to back and returns one reading for the batch: nanoseconds
for wallClock, counted events for a withCounter meter. Each
meter owns its inner loop, reading the clock or arming the
counter once per batch.
Implementations
Wall-clock meter: elapsed nanoseconds over the batch.
On Darwin, backed by clock_gettime_nsec_np(CLOCK_UPTIME_RAW),
which resolves the hardware tick (~42 ns on Apple Silicon, so
sub-microsecond targets need batching; see cfgBatch).
Not GHC.Clock.getMonotonicTimeNSec, which macOS quantises to
microseconds, putting CDF cut points on quantisation boundaries
(see issues/handled/ISSUE2.md). Elsewhere, backed by
GHC.Clock.getMonotonicTimeNSec.
Hardware performance-counter meters (Linux)
A hardware (or, for TaskClock, software) event to count.
With wallClock the counters form a ladder in which each
adjacent gap isolates one cause of a class difference:
instructions (code path) -> cycles (microarchitectural latency)
-> ref-cycles (frequency) -> task-clock (kernel and interrupt
time) -> wall (scheduling gaps).
Constructors
| InstructionsRetired |
|
| BranchesRetired |
|
| Cycles |
|
| RefCycles |
|
| TaskClock |
|
| BranchMisses |
|
| CacheMisses |
|
data MeterError Source #
Why a withCounter meter could not be opened.
Constructors
| Unsupported | not a Linux build; no hardware-counter support |
| OpenFailed !Int |
|
Instances
| Exception MeterError Source # | |
Defined in Censor.Meter Methods toException :: MeterError -> SomeException # fromException :: SomeException -> Maybe MeterError # displayException :: MeterError -> String # backtraceDesired :: MeterError -> Bool # | |
| Show MeterError Source # | |
Defined in Censor.Meter Methods showsPrec :: Int -> MeterError -> ShowS # show :: MeterError -> String # showList :: [MeterError] -> ShowS # | |
| Eq MeterError Source # | |
Defined in Censor.Meter | |
withCounter :: Counter -> (Meter -> IO a) -> IO a Source #
Acquire a performance-counter Meter for the duration of the
callback, then release it. Throws MeterError if the counter
cannot be opened.
Counters count user space only, which unprivileged processes may
open at kernel.perf_event_paranoid 2 or lower; TaskClock
deliberately includes kernel time and needs 1 or lower, failing
with otherwise rather than silently degrading
to a user-only clock. A counter is bound to the thread that
opened it, so the callback runs on one OS thread.OpenFailed 13
Wall time split into cycles and period (Linux)
newtype SplitMeter Source #
A meter reading two quantities over one execution of the batch:
returns measureSplit m k act(cycles, nanoseconds). Wall
time is cycles times the clock period, and a secret can move it
through either factor: through cycles when the code's execution
depends on it, through the period when the core's frequency
does (data-dependent power under frequency scaling). Reading
both on the same execution tells the two apart.
Constructors
| SplitMeter | |
withSplitMeter :: (SplitMeter -> IO a) -> IO a Source #
Acquire a SplitMeter for the duration of the callback, then
release it. Throws MeterError if the cycle counter cannot be
opened.
The wall clock is read inside the counted region, so both
readings cover the batch and the clock reads; the overhead is
the same in both classes. As with withCounter, the callback
runs on one OS thread.