Performancefrequencyboostdvfspowermeasurement

The Clock Is a Variable

The number printed on the box is a nominal figure, not an operating one. Real clock frequency moves continuously with load, thermal headroom, power budget, how many cores are active, and even which instructions are executing — wide vector code frequently runs at a lower clock than scalar code on the same chip.

Follow the mechanism

Software view, hardware view

The gap between what you wrote and what the machine does is where this whole domain lives.

The question
Why do identical runs of the same code take different amounts of time on the same machine?
What you wrote
The CPU is a 3.5 GHz part, so it executes 3.5 billion cycles per second and a run taking seven billion cycles will take two seconds.
What the hardware does
The core selected an operating point from a range, based on how many cores are busy, how much thermal and power headroom exists at that instant, and what kind of instructions are in flight. That frequency changed several times during the run.
Every cycle-based measurement is converted to time by a frequency that is not constant, so cycle counts and wall-clock times can disagree — and a benchmark that finishes before the machine settles reports a speed that cannot be sustained.
SourceCompilerInstructionsFront EndExecutionRegistersCachesMemoryI/OBehavior

One nominal number, a range of real ones

A modern core continuously selects an operating point — a frequency and voltage pair — from a range the part supports. The selection is made by hardware and firmware in response to conditions that change on a millisecond timescale: how many cores are active, current power draw against the package limit, temperature against the thermal limit, and the governor policy the operating system has requested.

Two consequences follow immediately. First, the same code can take measurably different wall-clock times on the same machine depending on what else was happening, how warm the chip already was, and how long ago it was last idle. Second, cycles and time are not interchangeable units. A run that consumed the same number of cycles as another can take longer if it spent them at a lower frequency, which is a routine source of confusion when comparing cycle counts to stopwatch results.

The single most important practical instance is that a core running alone can typically clock higher than the same core when all its siblings are busy, because power and thermal budget are shared across the package. This means a single-threaded benchmark and the same code running on every core are measuring the chip in two genuinely different states, and comparing them without noting that is comparing two machines.

What moves the operating frequency, and in which direction
ConditionEffect on clockWhyWhat it does to a measurement
One core busy, rest idleHigherPackage power and thermal budget is available to that coreSingle-thread numbers look better than any multi-thread run will
All cores busyLowerShared power and thermal budget divided across the packagePer-core throughput below the single-core figure
Sustained load, chip warmLower over timeThermal limit reached; see The First Ten Seconds LieFirst seconds unrepresentative of steady state
Just came off idleHigher brieflyAccumulated thermal headroom permits a boost windowA benchmark run after a pause is systematically optimistic
Wide vector instructionsFrequently lowerWide execution draws substantially more power per cycleVector speedup measured in cycles overstates the wall-clock win
Power-saving governorLower, slower to rampPolicy favours energy over responsivenessShort benchmarks may never reach a high operating point at all

Wide instructions can clock the core down

MICROARCH-SPECIFICWhether and how much wide-vector execution reduces frequency, and how long any reduction persists, varies substantially by vendor and by generation; some designs show a large effect, others almost none.

The least intuitive entry in that table deserves its own treatment. Executing wide vector instructions activates a great deal more silicon per cycle than scalar code does, and that draws more power. On several designs the response is to reduce the operating frequency while such code is executing, so that the package stays within its power envelope.

The practical effect is that a vectorized loop measured in cycles can show an impressive speedup that partly evaporates when measured in seconds, because those cycles were shorter-lived. Worse, on some implementations the frequency reduction persists for a while after the vector code finishes, so an unrelated scalar section running immediately afterwards is penalised by code it did not execute.

None of this makes vectorization a bad idea — it usually remains a substantial net win, and SIMD: One Instruction, Many Elements explains why. What it does mean is that the wall-clock measurement is the honest one, that cycle-count comparisons across differently-vectorized code should be treated with suspicion, and that the magnitude of the effect is strongly design-dependent, having changed considerably across generations even within a single vendor.

The same workload measured two ways. Illustrative of the divergence, not measured from a specific part.
                          scalar loop        vector loop
instructions retired         4.0e9              0.5e9
cycles                       2.20e9             0.80e9
cycle-based speedup            1.00x              2.75x   <- what the counters say
average frequency            3.4 GHz            2.9 GHz
wall-clock time              0.647 s            0.276 s
wall-clock speedup             1.00x              2.34x   <- what the user experiences

The vector version is genuinely faster. It is not as much
faster as the cycle counts alone suggest.

Measuring on a machine whose speed is not constant

The defence is a small set of habits. Report wall-clock time as the primary result, because it is what a user experiences and it already includes the frequency effect. Report cycles as the diagnostic, because cycles are what the counters explain. When the two disagree, the difference is frequency, and that is information rather than an error.

For any comparison, run long enough to reach a steady operating point rather than measuring the boost window, and state whether the run was single-threaded or fully loaded, since those are different machine states. Where the environment permits it, pinning the frequency governor to a fixed performance state removes the variable entirely and makes cycle counts directly comparable — at the cost of no longer measuring the machine as users will actually experience it.

On cloud instances this is largely out of reach: frequency policy belongs to the host, the physical core may be shared, and the neighbouring tenants influence thermal and power state in ways you cannot observe. That is a genuine limitation rather than a solvable problem, and the correct response is to measure more repetitions, report distributions rather than single numbers, and treat modest differences with appropriate scepticism.

  • Wall-clock is the result; cycles are the diagnosis. When they disagree, the gap is frequency.
  • Run to steady state, and say whether the run was single-threaded or fully loaded.
  • Pin the governor where you can, accepting that you are no longer measuring the user-visible machine.
  • On shared or virtualized hosts, frequency policy is not yours — measure distributions, not single runs.
  • Never convert cycles to time using the nominal frequency; that number is a marketing figure, not an operating one.

Key points

  • Operating frequency is selected continuously from a range based on active core count, power, temperature and governor policy.
  • Cycles and wall-clock time are not interchangeable, because the seconds-per-cycle factor moves during the run.
  • A core running alone typically clocks higher than the same core with all siblings busy, so single-thread and full-load runs measure different machine states.
  • Wide vector instructions can reduce the operating frequency, so cycle-based vector speedups overstate the wall-clock win.
  • On virtualized and shared hosts, frequency policy is not under your control at all — measure distributions rather than single runs.

Follow the mechanism

The path through the machine, hop by hop — and the conclusions it invites that are wrong.

  1. 1
    Workload → power draw: the instruction mix and active core count determine instantaneous package power.
  2. 2
    Power and temperature → controller: firmware compares draw and temperature against package limits several times per millisecond.
  3. 3
    Controller → operating point: a frequency and voltage pair is selected from the supported range.
  4. 4
    Operating point → seconds per cycle: the conversion factor between the cycle counter and the stopwatch changes.
  5. 5
    Measurement → divergence: a cycle-based result and a wall-clock result now disagree, by exactly the frequency change.
What people conclude from this — wrongly
  • Multiplying cycles by the nominal frequency to obtain time.
  • Comparing a single-threaded result with a fully loaded one as though they were the same machine.
  • Reading a cycle-based vector speedup as the speedup a user will experience.
  • Treating run-to-run variance on a cloud instance as measurement error rather than genuine machine variation.

Consequences, controls and cost

What it causes
  • • Repeated runs of identical code differ measurably, especially between the first run after idle and subsequent ones.
  • • Cycle-count speedups and wall-clock speedups diverge, most visibly for vectorized code.
  • • Single-threaded benchmark results do not predict per-core throughput under full load.
  • • Cloud measurements carry irreducible variance from host-level frequency policy and neighbouring tenants.
What you can do
  • • Report wall-clock time as the headline result and cycles as the explanation, never converting between them via nominal frequency.
  • • Run long enough to leave the boost window and reach a steady operating point before recording.
  • • State the thread count and machine state alongside every result, since these are different machines.
  • • Pin the governor to a fixed performance state when comparing implementations, and say that you did.
  • • On shared hosts, accept the variance: many repetitions, reported as a distribution.
How to see it
  • • Average and instantaneous frequency over the run, alongside cycles and wall-clock time.
  • • The same benchmark single-threaded and at full core count, reported separately.
  • • A long run with frequency sampled throughout, to see where the steady state actually is.
  • • Repeat-run distribution rather than a single number, particularly on shared infrastructure.
What it costs
  • • Pinning frequency makes results reproducible but stops them representing the machine users actually get.
  • • Running to steady state takes far longer than a quick benchmark and consumes more energy.
  • • Reporting distributions is more honest and considerably harder to communicate than a single figure.

Scope

§224 — what these claims are specific to.

What these claims are specific to
  • MICROARCH-SPECIFICBoost algorithms, the frequency range, and the magnitude of any wide-vector frequency reduction differ by vendor and generation; some designs show a pronounced vector effect and others almost none.
  • PLATFORM-SPECIFICGovernor policy, available power and thermal limits, and whether frequency can be pinned at all are properties of the operating system and the physical machine, and are typically unavailable on shared cloud instances.

Misconceptions

Claim
“A 3.5 GHz CPU executes 3.5 billion cycles every second.”
Reality
That is a nominal figure. The real operating frequency moves continuously with active core count, power headroom, temperature and instruction mix, often across a wide range.
Claim
“If cycle count went down, the program got faster.”
Reality
Only if the frequency held. Vectorized code frequently runs at a lower clock, so a large cycle reduction can translate to a smaller wall-clock gain.
Claim
“Run-to-run variance means the benchmark is badly written.”
Reality
Some of it is genuine machine variation from boost state, thermal history and neighbouring load — particularly on shared hosts, where frequency policy is not yours to control.

Apply it