Skip to content
Select theme

How to measure how long something took

Short answer: grab an Instant::now() before the work, call .elapsed() after it:

Terminal window
cargo run -q
sum: 12499997500000
took: 27.524ms

The sum is the same on every run; the time is not — yours will be whatever this loop costs on your machine. start is an Instant, and elapsed() is the method almost every measurement needs. From a fresh project — cargo new timing-demo, then replace src/main.rs with each block below.

SystemTime can do this too — take two readings, subtract — and the compiler will not stop you:

elapsed: 60 ms

The Err branch is the failure you can see: the wall clock is allowed to step backwards (NTP corrections, a manual change, a VM resuming from a snapshot), and when it steps between t1 and t2, the measurement fails outright. The worse failure is invisible — a clock that steps forward mid-measurement returns Ok with a number that is too large, and nothing in the type tells you. A stopwatch whose readings can be silently edited by the network is not a stopwatch.

Instant is the other clock in the standard library, and the one built for this. It is monotonic: it counts from an arbitrary fixed point (on this machine, boot), so nothing outside your process can change how much time passed between two readings.

The same subtraction, done with Instant, never fails and never lies:

forward: 40.005625ms
backward: 0ns
checked: None
ordering: true

Read that middle pair carefully. Subtracting an Instant from a later one gives 0ns — duration_since saturates to zero instead of producing a negative duration the type cannot represent. That is the right answer for a stopwatch, but if the order matters to your logic, checked_duration_since is the version that tells you: it returns None when its argument is later, which is the Err-shaped information SystemTime gives and Instant converts into a plain zero. Ordering comparisons (<, >=) work directly, and between two Instants from the same process they always have an answer.

One property to keep in mind: an Instant is only meaningful on the machine that created it, and only until that machine reboots. Never store one in a database, send one over the network, or print one into a log you will read tomorrow — that is what SystemTime is for.

Timing something fast: repeat it and use black_box

Section titled “Timing something fast: repeat it and use black_box”

A single call that takes microseconds is invisible to a stopwatch run once, and a loop timed naively measures the optimizer as much as the code: sum a range without using the sum, and the compiler is free to delete the whole loop. Two fixes, both in this block — repeat the work and average, and wrap the inputs and output in std::hint::black_box so the compiler has to treat them as unknown:

7.811ms
7.650ms
7.648ms
7.499ms
7.530ms
mean: 7.628ms

The black_box around i keeps every addition happening inside the loop; the one around total keeps the loop from being deleted entirely. Duration supports + and division by an integer, so the mean is ordinary arithmetic. The precision in {t:.3?} is the Duration debug format truncated to three digits — handy for readings you do not want printed as nanoseconds.

elapsed() returns a Duration, and that type is where the measurement gets used:

seconds: 0.085011
times two: 170.02275ms
over budget: true
divided by 3: 28.337ms

as_secs_f64 and as_millis are the two most common exits: the float form for logging and computing, the integer forms for display (as_millis truncates — 75.1 ms reports as 75). Comparisons work against literals built with Duration::from_millis, which is how a “took longer than 50 ms” budget check is written. Multiplication and division by integers are defined, so scaling and averaging stay one expression; and an Instant plus a Duration is another Instant, which is how deadline math (“give up if this is still running at start + 2s”) is built on the same types.

None of this makes SystemTime bad — it makes it the wrong tool for measuring. The moment the value has to outlive the process, Instant is disqualified and the wall clock is the only option: how long did this job take as the logs see it, which run started before the deploy, what happened while the machine was asleep. Those questions are answered with SystemTime on both ends, and the fallibility you accept is the same one — a clock correction in the middle of a measurement is a fact about the timeline, not a bug in the stopwatch. Keep them apart: Instant for durations the process measures itself, SystemTime for timestamps that leave it.

  • The previous article covered parsing and formatting — turning calendar strings into values and back with chrono.
  • Turning a raw timestamp into an actual date is the next article in this chapter.
  • Working with dates and time — the rest of the chapter.