C++ std::chrono::steady_clock | Monotonic time for benchmarks
이 글의 핵심
steady_clock gives monotonic time_points for measuring intervals and deadlines without NTP jumps; when to prefer system_clock for wall time and how to read is_steady.
What is steady_clock?
A monotonically non-decreasing clock (C++11).
#include <chrono>
auto start = std::chrono::steady_clock::now();
// work
auto end = std::chrono::steady_clock::now();
auto elapsed = end - start;
Properties
// Monotonic increase guaranteed
// - Unaffected by system time changes
// - Always moves forward
// - Good for performance measurement
std::chrono::steady_clock::is_steady; // true
Practical examples
Example 1: Benchmark helper
template<typename Func>
auto benchmark(Func f) {
auto start = std::chrono::steady_clock::now();
f();
auto end = std::chrono::steady_clock::now();
return std::chrono::duration_cast<std::chrono::microseconds>(
end - start
);
}
int main() {
auto duration = benchmark([&] {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
});
std::cout << "Elapsed: " << duration.count() << " μs" << std::endl;
}
Example 2: Timer class
class Timer {
std::chrono::steady_clock::time_point start;
public:
Timer() : start(std::chrono::steady_clock::now()) {}
void reset() {
start = std::chrono::steady_clock::now();
}
auto elapsed() const {
auto end = std::chrono::steady_clock::now();
return std::chrono::duration_cast<std::chrono::milliseconds>(
end - start
);
}
};
int main() {
Timer timer;
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "Elapsed: " << timer.elapsed().count() << " ms" << std::endl;
}
Example 3: Timeout polling
bool waitWithTimeout(std::chrono::milliseconds timeout) {
auto start = std::chrono::steady_clock::now();
while (true) {
if (isReady()) {
return true;
}
auto now = std::chrono::steady_clock::now();
if (now - start >= timeout) {
return false; // timeout
}
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
Example 4: Comparing algorithms
void comparePerformance() {
auto duration1 = benchmark(algorithm1);
auto duration2 = benchmark(algorithm2);
if (duration1 < duration2) {
std::cout << "algorithm1 is faster" << std::endl;
}
auto diff = duration2 - duration1;
std::cout << "Delta: " << diff.count() << " μs" << std::endl;
}
system_clock vs steady_clock
// system_clock: wall time (can jump)
auto sys = std::chrono::system_clock::now();
// steady_clock: monotonic
auto steady = std::chrono::steady_clock::now();
// Prefer steady_clock for elapsed intervals
Common pitfalls
Pitfall 1: Wrong clock for intervals
// ❌ system_clock (user/NTP can move time backward)
auto start = std::chrono::system_clock::now();
// system time adjusted backward
auto end = std::chrono::system_clock::now();
// duration may be negative
// ✅ steady_clock
auto start = std::chrono::steady_clock::now();
auto end = std::chrono::steady_clock::now();
// non-negative for forward progress
Pitfall 2: Resolution
using period = std::chrono::steady_clock::period;
std::cout << "Resolution: " << period::num << "/" << period::den << " s" << std::endl;
Pitfall 3: Timer call overhead
auto start = std::chrono::steady_clock::now();
auto end = std::chrono::steady_clock::now();
auto overhead = end - start;
std::cout << "Overhead: " << overhead.count() << " ns" << std::endl;
Pitfall 4: Very long measurements
// For multi-day spans, watch representation limits (rare on desktop)
auto start = std::chrono::steady_clock::now();
// ...
auto end = std::chrono::steady_clock::now();
Usage patterns
// 1. Benchmark
auto duration = benchmark(func);
// 2. Timeout
bool success = waitWithTimeout(1000ms);
// 3. Compare algorithms
compareAlgorithms();
// 4. Profiling
profileFunction();
steady_clock vs system_clock vs high_resolution_clock
C++11 chrono offers three clocks you see most often. Picking “high_resolution” by name alone can hurt portability.
| Clock | Monotonic (is_steady) | Meaningful absolute time | Typical use |
|---|---|---|---|
steady_clock | Yes | Epoch is implementation-defined; not great for “what time is it?” | Elapsed time, deadlines, timeouts, benchmarks |
system_clock | No (NTP/manual) | Wall clock, works with to_time_t | Log timestamps, file/DB times, calendar expiry |
high_resolution_clock | Implementation-defined | Often alias of steady or system | Short micro-benchmarks (check is_steady) |
Note on high_resolution_clock: The standard allows it to be a typedef of steady_clock or system_clock. For wall time, use system_clock; for elapsed/timeout, use steady_clock directly. Treat high_resolution_clock as a local micro-benchmark helper after you verify behavior.
#include <chrono>
#include <iostream>
int main() {
using namespace std::chrono;
std::cout << std::boolalpha;
std::cout << "steady_clock::is_steady: " << steady_clock::is_steady << '\n';
std::cout << "system_clock::is_steady: " << system_clock::is_steady << '\n';
std::cout << "high_resolution_clock::is_steady: " << high_resolution_clock::is_steady << '\n';
}
Measurement patterns
-
Single interval:
auto t0 = steady_clock::now();… work …auto dt = steady_clock::now() - t0;— keep nativedurationuntil you display, thenduration_castto reduce rounding drift. -
Accumulating segments: Sum
durationvalues or differences oftime_points on the same clock. -
Very short work: Two
now()calls can already show hundreds of nanoseconds—calibrate or repeat and aggregate (mean/median). -
Mixed with sleep:
sleep_foronly guarantees a minimum sleep; measure real elapsed withsteady_clock.
auto t0 = std::chrono::steady_clock::now();
do_work();
auto t1 = std::chrono::steady_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(t1 - t0);
Benchmarking notes
- Warm up caches and allocators before timing.
- Repeat runs; use median or mean when noise is high.
- Prevent optimization of dead work with benchmark helpers (
DoNotOptimize) in real harnesses (e.g. Google Benchmark). - Clock choice: measure intervals with
steady_clock; if you log human-readable timestamps, usesystem_clockonly for that line.
The benchmark template above is illustrative; production code should pair micro-benchmarks with profiling tools.
Advanced: timeout with a deadline
For polling loops, a deadline time_point is often clearer than “start + budget” scattered everywhere.
#include <chrono>
#include <thread>
bool wait_until(std::chrono::steady_clock::time_point deadline,
auto pred, std::chrono::milliseconds poll) {
while (std::chrono::steady_clock::now() < deadline) {
if (pred()) return true;
std::this_thread::sleep_for(poll);
}
return false;
}
- Prefer condition variables / futures when you can block efficiently instead of polling.
- If
deadline - steady_clock::now()is negative, the deadline already passed—clamp or check before casting.
Platform notes
- Linux:
steady_clockis usually monotonic (CLOCK_MONOTONIC-like). Checkperiodfor resolution. - Windows: MSVC/MinGW implementations vary but monotonicity is still the contract.
- macOS/iOS: Typically monotonic;
high_resolution_clockoften aliases the same underlying clock. - Embedded/RTOS: Low tick rates can quantize short intervals—validate
periodand measurement error. - Very long uptime: Theoretical counter overflow is rare on desktop/server
durationtypes.
One-line summary: Wall time → system_clock; durations and timeouts → steady_clock; do not pick high_resolution_clock by name alone—check is_steady and your implementation.
FAQ
Q1: What is steady_clock?
A: A clock whose time_points never decrease (for a given process run).
Q2: Difference from system_clock?
A: steady: monotonic. system: tracks system/wall time and can jump.
Q3: Performance measurement?
A: Prefer steady_clock for intervals.
Q4: Precision?
A: Platform-dependent; often nanosecond-class resolution.
Q5: Overhead of now()?
A: Usually small (nanoseconds) but not free for micro-benchmarks.
Q6: Learning resources?
A: C++ Primer, Effective Modern C++, cppreference — steady_clock.
Related posts (internal)
- C++ Chrono overview
- C++ Chrono guide
- C++ time_point
Practical tips
Debugging
- Fix compiler warnings first.
- Reproduce with a small test.
Performance
- Profile before optimizing.
- Define measurable targets.
Code review
- Follow team conventions.
Production checklist
Before coding
- Right approach?
- Maintainable?
- Meets performance needs?
While coding
- Warnings cleared?
- Edge cases covered?
- Error handling OK?
At review
- Intent clear?
- Tests sufficient?
- Documented?
Keywords
C++, steady_clock, chrono, benchmark, C++11
Related posts
- C++ Chrono guide |
- C++ Chrono |
- C++ duration |
- C++ ratio |
- C++ Stopwatch and benchmark