How spdlog Stopwatch Works for Precision Timing in C++ Logging
spdlog::stopwatch is a header-only utility class that leverages std::chrono::steady_clock to measure elapsed intervals and seamlessly formats timing data into log messages through native fmt integration.
The gabime/spdlog library provides this lightweight stopwatch feature in include/spdlog/stopwatch.h, offering zero-overhead timing capabilities that eliminate boilerplate when profiling code sections. Unlike external timing libraries, spdlog's stopwatch integrates directly with the logging pipeline, allowing you to inject precise duration measurements into debug or performance traces with minimal syntax.
Core Implementation in spdlog Stopwatch
The stopwatch implementation relies on std::chrono::steady_clock to ensure monotonic timing unaffected by system clock adjustments.
Construction and Initialization
When instantiating a spdlog::stopwatch object, the constructor captures the current time point from std::chrono::steady_clock and stores it in the private member start_tp_. This initialization occurs in include/spdlog/stopwatch.h at lines 31-38, establishing the baseline for all subsequent elapsed time calculations.
#include <spdlog/stopwatch.h>
// Construction starts the timer immediately
spdlog::stopwatch sw; // start_tp_ set to steady_clock::now()
Measuring Elapsed Time
The class provides two primary methods for retrieving elapsed duration:
- elapsed() – Returns a
std::chrono::duration<double>representing the interval in seconds since construction or the last reset (lines 39-41 instopwatch.h). - elapsed_ms() – Casts the duration to
std::chrono::millisecondsfor integer-based millisecond granularity (lines 43-45).
Both methods calculate the delta between the stored start_tp_ and the current steady clock time, ensuring accurate interval measurement even during long-running operations.
Resetting the Timer
To measure multiple consecutive intervals without destroying the object, the reset() method updates start_tp_ to the current steady clock time (lines 47-48). This effectively restarts the stopwatch without heap allocation or significant computational cost.
sw.reset(); // Restart timing from current steady_clock::now()
fmt Library Integration and Formatting
spdlog extends the fmt formatting system through a template specialization that enables direct insertion of stopwatch objects into log strings.
Formatter Specialization
The specialization fmt::formatter<spdlog::stopwatch> inherits from fmt::formatter<double> and implements a custom format() method. This method invokes sw.elapsed().count() to extract the raw double value representing seconds, then delegates formatting to the base double formatter (lines 60-66 in include/spdlog/stopwatch.h).
Because the formatter treats the elapsed time as a double, you control output precision using standard fmt format specifications:
spdlog::info("Time: {}", sw); // Default precision: 0.0123456
spdlog::info("Time: {:.3}", sw); // 3 decimal places: 0.012
spdlog::info("Time: {:.6}", sw); // Microsecond precision: 0.012345
Alternative Millisecond Formatting
For native millisecond display without floating-point formatting, include spdlog/fmt/chrono.h and cast the elapsed duration:
#include <spdlog/fmt/chrono.h>
using std::chrono::duration_cast;
using std::chrono::milliseconds;
spdlog::info("Elapsed: {}", duration_cast<milliseconds>(sw.elapsed()));
// Output: "Elapsed: 12ms"
Practical spdlog Stopwatch Examples
The following examples demonstrate common patterns for timing code sections and formatting the results.
Basic Timing with Default Precision
#include <spdlog/spdlog.h>
#include <spdlog/stopwatch.h>
int main()
{
spdlog::stopwatch sw;
// Simulate work
std::this_thread::sleep_for(std::chrono::milliseconds(50));
spdlog::info("Operation completed in {} seconds", sw);
// Example output: "Operation completed in 0.0501234 seconds"
}
High-Precision Microsecond Logging
spdlog::stopwatch sw;
// ... performance critical section ...
spdlog::debug("Critical section: {:.6} seconds", sw);
// Forces 6 decimal places for microsecond resolution
Resetting Between Measurements
spdlog::stopwatch sw;
// First phase
process_data();
spdlog::info("Phase 1: {} seconds", sw);
sw.reset();
// Second phase
validate_results();
spdlog::info("Phase 2: {} seconds", sw);
Millisecond-Accurate Timing
#include <spdlog/fmt/chrono.h>
spdlog::stopwatch sw;
// ... work ...
spdlog::info("Duration: {}",
std::chrono::duration_cast<std::chrono::milliseconds>(sw.elapsed()));
// Output displays as "Duration: 50ms"
Key Source Files
Understanding the stopwatch implementation requires examining these specific files in the gabime/spdlog repository:
include/spdlog/stopwatch.h– Contains thestopwatchclass definition,elapsed()andreset()method implementations, and thefmt::formatterspecialization that enables automatic string conversion.tests/test_stopwatch.cpp– Unit tests verifying timing accuracy and formatter behavior under various precision settings.include/spdlog/fmt/fmt.h– Provides the underlying formatting infrastructure that the stopwatch formatter extends.include/spdlog/fmt/chrono.h– Optional header enablingstd::chronoduration formatting when millisecond or other time unit display is required.
Summary
- spdlog::stopwatch uses
std::chrono::steady_clockto provide monotonic timing measurements immune to system clock changes. - The constructor initializes
start_tp_immediately, whilereset()updates this baseline without object reconstruction. - elapsed() returns double-based seconds, and elapsed_ms() provides integer milliseconds for different precision requirements.
- Native
fmtintegration viaformatter<spdlog::stopwatch>allows direct insertion into log strings with customizable decimal precision using format specifiers like{:.3}. - Zero-overhead design makes the stopwatch suitable for production logging and performance profiling without impacting runtime metrics.
Frequently Asked Questions
What clock source does spdlog stopwatch use?
The implementation uses std::chrono::steady_clock exclusively, as defined in include/spdlog/stopwatch.h. This clock guarantees monotonic progression regardless of system time adjustments, ensuring that timing measurements remain accurate even if the operating system clock changes during execution.
How do I display stopwatch results in milliseconds instead of seconds?
Include spdlog/fmt/chrono.h and use std::chrono::duration_cast<std::chrono::milliseconds>(sw.elapsed()) within your log statement. While elapsed_ms() returns the correct duration type, the chrono formatter provides the cleanest "ms" suffix output without manual string construction.
Can I reset a spdlog stopwatch without creating a new object?
Yes. Calling sw.reset() updates the internal start_tp_ member to the current steady clock time, effectively restarting the measurement interval. This method avoids allocation overhead and maintains the same stack-allocated object for multiple consecutive timing operations.
Does using spdlog stopwatch impact application performance?
No. The stopwatch is header-only and performs only trivial arithmetic operations (subtracting two time points) when elapsed() is called. The memory footprint consists solely of one std::chrono::steady_clock::time_point member, making it suitable for high-frequency logging in performance-critical paths.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →