Concepts / Durations and Time Units in C++

Durations and Time Units in C++

Naming the unit in the cast is what fixes the unit of the result.

  • Programming

Why Timing Needs Two Readings

Suppose you want to know how long a piece of C++ code takes to run. You do not need to build your own stopwatch. The C++ standard library already provides facilities for dealing with time, and the std::chrono library together with high_resolution_clock is the standard tool for this job. The core idea is simple: read the clock once before the work starts, read it again after the work finishes, and report the gap between the two readings.

What do you think happens?

Suppose do_work contains a loop that runs for a while, and the timing code is the only timing you do. Which lines do you think are included in the number that gets printed: only the call to do_work, or also the cout line? Write your answer down before reading on.

  • Only the call to do_work

  • Both the call to do_work and the cout line

Reveal answer
Answer:

Only the call to do_work is included.

Only the code between the two clock readings is included in the difference. The cout line runs after the second reading has already been taken, so it sits outside the measured interval.

Following the Timing Pattern Step by Step

Before reading any explanation, look at the complete pattern and follow it line by line, keeping track of what happens first, what happens second, and what gets printed at the end. In the pattern, do_work is a placeholder for whatever code you want to measure. It is not part of the timing facility. Everything else in the pattern is bookkeeping that wraps that one call with two readings of the time and reports the gap between them.

cpp
Walk through it
  1. 1
    Line 1 reads the clock and stores the time point in t0 before the work starts.
  2. 2
    Line 2 runs do_work(), the code being measured.
  3. 3
    Line 3 reads the clock again and stores the time point in t1 after the work finishes.
  4. 4
    Line 4 subtracts t0 from t1 to get the elapsed time as a duration, converts it to milliseconds with duration_cast, and calls count() to get a plain number.
  5. 5
    Line 5 prints that number followed by the text msec.
Notes on the code
  • high_resolution_clock::now() reads the current time and returns a time point
  • auto stores each time point in a variable; the compiler decides its type from the call
  • do_work() is the only operation being timed
  • The subtraction t1 - t0 yields a duration, which is then converted and extracted
Programhigh_resolution_clockdo_workcout1. now() stored in t02. call do_work()3. work finishes4. now() stored in t15. print elapsed msec
In what order do the start clock read, the measured code, the end clock read, and the subtraction actually execute, and which step produces the value that gets printed?

Trace the pattern in the order the program runs it. The first clock reading produces t0 and waits in its variable. The work runs. The second clock reading produces t1. Only the work sitting between these two readings contributes to the gap. The subtraction and the conversion that follow are arithmetic on the stored values; they are not a second run of your program's logic. The value that actually gets printed is produced at the final step, when count() extracts the converted number.

What duration_cast and count() Actually Do

The subtraction t1 minus t0 gives the difference between the two readings. That difference is a duration, not a plain number, so the pattern passes it through duration_cast before printing. Here, duration_cast is used to convert the time duration, and the target unit is named explicitly as milliseconds. Naming the unit in the cast is what fixes the unit of the result.

The call to count() at the end turns the converted duration into a plain number that cout can print. The number is the value in milliseconds, so the unit is already settled by the cast. The label msec written after the number simply documents that unit; it does not perform the conversion. Because the output is a plain number, the cast and the count() call are both needed. Printing the duration object without count() does not give you the bare number, and a label that names a different unit than the cast will mislead a reader.

t1 - t0a durationduration_cast<milliseconds>converts the unitmilliseconds durationstill a duration objectcount()extracts the numberplain integerthe value in millisecondscoutprints the number, thenmseccastresultcount()numberprint
How does a difference measured between two readings flow through duration_cast<milliseconds> and count() to become the printed integer?

Where the Two Readings Belong

Only the code between the two readings is included in the difference, so placement determines what the number means. Imagine a print statement placed just before the first clock reading, or just after the second. Each placement changes what the reported time describes. The two diagrams below show the same program with the print line in two different positions.

t0 readt0 readprint linedo_work()do_work()t1 readt1 readprint linenextnextnextnextnextnext
If a print statement sits just before the first now() or just after the second, does it end up inside the measured interval, and how would that change the reported time?

In the first arrangement, the print line sits between the first reading and the work, so its execution time is counted in the interval. In the second, the print line runs after t1 has already been stored, so the interval covers only do_work. The span between the two clock readings is a stretch of time on a single timeline, and the reported number corresponds exactly to whatever code falls inside that stretch.

Mistakes That Make Timings Misleading

The most common ways a timing result misleads are misplaced output, a missing count(), and a mismatched unit label.

  • Common mistake

    Putting output or setup calls between the clock readings

    What it looks like

    Placing cout between the first now() and do_work(), so the printing time is measured along with the work.

    Why it fails

    Only the code between the two readings is included in the difference, so anything extra sitting there inflates the number.

    Do this instead

    Keep the two readings tight around the code you want to measure, and print only after the second reading has been stored.

  • Common mistake

    Leaving out count()

    What it looks like

    Printing the duration object directly after the cast instead of extracting its number.

    Why it fails

    The converted result is a duration, not a plain number, so the printed output is not the bare value in milliseconds.

    Do this instead

    Call count() on the converted duration before sending it to cout.

  • Common mistake

    Mismatched unit label

    What it looks like

    Casting to milliseconds but printing a label such as usec after the number.

    Why it fails

    The number is already in milliseconds because the cast set the unit, so a different label describes the wrong quantity.

    Do this instead

    Make the unit in the cast and the unit in the printed label agree.

  • Common mistake

    Reading a zero as no work done

    What it looks like

    Seeing 0 msec printed for a short function and concluding it took no time.

    Why it fails

    The conversion to whole milliseconds drops any fraction, so a very short duration can print as 0.

    Do this instead

    Use a finer unit if the code is short enough that milliseconds hide the result.

Practice Reading a Timing Block

MEDIUM

Consider this variant: a student places cout << "starting" right after the line that stores t0, then calls do_work(), then stores t1, then computes and prints the elapsed milliseconds. Which operations are inside the measured interval, and which are outside? Explain how the reported number would differ from the original pattern.

Hints
  • Ask which operations sit between the two stored readings.
  • Trace the order: t0 read, cout, do_work, t1 read, then the subtraction and printing.
  • Think about whether the cout line is between the readings in this variant.

Key Takeaways

  • Two calls to high_resolution_clock::now(), stored with auto in t0 and t1, bound the interval that gets measured; only the code between them contributes to the difference.
  • The subtraction t1 minus t0 produces a duration, which duration_cast<milliseconds> converts to milliseconds and count() extracts as a plain number for printing.
  • Placement decides meaning: output or setup calls placed between the two readings inflate the reported time, while output placed after the second reading stays outside it.
  • Printed results can mislead through a missing count(), a unit label that does not match the cast, or truncation of fractions when casting to milliseconds.

More concepts starting with D