C++ Chrono Detailed Complete Guide | 'Time Library' Guide

C++ Chrono Detailed Complete Guide | 'Time Library' Guide

이 글의 핵심

std::chrono is a C++11 library that handles time intervals and points in a type-safe manner using duration, time_point, and clock. This guide covers unit conversion, steady_clock vs system_clock selection, and measurement/timeout code with examples.

Introduction

std::chrono is a type-safe time handling library introduced in C++11. It consists of three core concepts: duration (time interval), time_point (time point), and clock (clock).


Reality in Production

When learning development, everything is clean and theoretical. But production is different. You wrestle with legacy code, chase tight deadlines, and face unexpected bugs. The content covered in this guide was initially learned as theory, but I realized “ah, that’s why it’s designed this way” while applying it to actual projects.

What stands out in my memory is the trial and error from my first project. I did it as I learned from books but spent days not knowing why it didn’t work. Eventually, I found the problem through a senior developer’s code review and learned a lot in the process. This guide covers not only theory but also pitfalls you may encounter in practice and their solutions.

1. chrono Basic Structure

Core Concepts

Here is detailed implementation code using C++. Import the necessary modules and understand the role of each part while examining the code.

#include <chrono>
#include <iostream>

int main() {
    // 1. duration: time interval (length of time)
    // std::chrono::seconds: duration type in seconds
    std::chrono::seconds sec(10);  // 10 seconds
    // std::chrono::milliseconds: duration type in milliseconds
    std::chrono::milliseconds ms(1000);  // 1000 milliseconds = 1 second
    // Other units: hours, minutes, microseconds, nanoseconds
    
    // 2. time_point: time point (specific time)
    // std::chrono::system_clock::now(): current system time
    // time_point is duration from epoch (1970-01-01 00:00:00)
    auto now = std::chrono::system_clock::now();
    
    // 3. clock: clock (method to measure time)
    // - system_clock: system time (wall clock time, adjustable)
    //   Convertible to calendar time, affected by system time changes
    // - steady_clock: monotonic (never goes backward)
    //   Suitable for time measurement, unaffected by system time changes
    // - high_resolution_clock: high precision (most precise clock)
    //   Usually an alias for steady_clock
    
    return 0;
}

Clock Types

ClockCharacteristicsUse Case
system_clockSystem time, adjustableCurrent time, timestamp
steady_clockMonotonic, not adjustablePerformance measurement, timer
high_resolution_clockHighest precisionPrecise measurement

2. Time Measurement

Basic Measurement

Here is detailed implementation code using C++. Import the necessary modules and process data with loops. Understand the role of each part while examining the code.

#include <chrono>
#include <iostream>
#include <thread>

int main() {
    // using namespace: std::chrono can be omitted
    using namespace std::chrono;
    
    // Start time: record current time
    // high_resolution_clock::now(): measure current time with most precise clock
    // Returns time_point type (specific time)
    auto start = high_resolution_clock::now();
    
    // Perform work: code to measure
    for (int i = 0; i < 1000000; ++i) {
        // volatile: prevent compiler optimization (so loop isn't removed)
        volatile int x = i * i;
    }
    
    // End time: record time after work completion
    auto end = high_resolution_clock::now();
    
    // Elapsed time: end - start
    // end - start: duration type (time interval)
    // duration_cast<milliseconds>: convert to milliseconds
    //   Type conversion from original unit (nanoseconds, etc.) to milliseconds
    auto duration = duration_cast<milliseconds>(end - start);
    
    // count(): extract numeric value from duration
    std::cout << "Execution time: " << duration.count() << "ms" << std::endl;
    
    return 0;
}

Output:

Execution time: 15ms

Timer Class

Here is detailed implementation code using C++. Import the necessary modules, define a class to encapsulate data and functionality, and process data with loops. Understand the role of each part while examining the code.

#include <chrono>
#include <iostream>

// Timer class: automatic time measurement with RAII pattern
class Timer {
    // time_point: type that stores specific time
    std::chrono::time_point<std::chrono::high_resolution_clock> start;
    std::string name;
    
public:
    // Constructor: start timer
    // Initialize start to current time with initialization list
    Timer(const std::string& name = "Timer") 
        : start(std::chrono::high_resolution_clock::now()), name(name) {}
    
    // Destructor: stop timer (automatically called when object is destroyed)
    // RAII: automatically measure time with object lifetime
    ~Timer() {
        auto end = std::chrono::high_resolution_clock::now();
        // duration_cast<microseconds>: convert to microseconds
        auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
        // count(): extract numeric value from duration
        std::cout << name << " elapsed: " << duration.count() << "μs" << std::endl;
    }
    
    // Intermediate measurement: output intermediate elapsed time without stopping timer
    // lap: lap time (section time)
    void lap() {
        auto now = std::chrono::high_resolution_clock::now();
        // Calculate elapsed time from start to now
        auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(now - start);
        std::cout << name << " lap: " << duration.count() << "ms" << std::endl;
    }
};

void processData() {
    // Create Timer object: start time measurement from this point
    Timer t("processData");
    
    // Task 1
    for (int i = 0; i < 1000000; ++i) {}
    // Intermediate check: output task1 completion time
    t.lap();
    
    // Task 2
    for (int i = 0; i < 2000000; ++i) {}
    // Intermediate check: output task2 completion time
    t.lap();
    
    // Function end: Timer destructor automatically called → output total elapsed time
}

int main() {
    processData();
    return 0;
}

Output:

processData lap: 5ms
processData lap: 15ms
processData elapsed: 15234μs

3. duration (Time Interval)

Basic Usage

Here is detailed implementation code using C++. Import the necessary modules and understand the role of each part while examining the code.

#include <chrono>
#include <iostream>

int main() {
    using namespace std::chrono;
    
    // Create time interval: duration type
    // std::chrono::seconds: duration in seconds
    seconds sec(10);  // 10 seconds
    // std::chrono::milliseconds: duration in milliseconds
    milliseconds ms(10000);  // 10000 milliseconds = 10 seconds
    // std::chrono::minutes: duration in minutes
    minutes min(1);  // 1 minute = 60 seconds
    // std::chrono::hours: duration in hours
    hours hr(1);  // 1 hour = 60 minutes = 3600 seconds
    
    // Get value: extract numeric value with count() method
    std::cout << "Seconds: " << sec.count() << std::endl;  // 10
    std::cout << "Milliseconds: " << ms.count() << std::endl;  // 10000
    
    return 0;
}

Time Conversion

Here is detailed implementation code using C++. Import the necessary modules and understand the role of each part while examining the code.

#include <chrono>
#include <iostream>

int main() {
    using namespace std::chrono;
    
    seconds s(10);  // 10 seconds
    
    // Higher → Lower (implicit conversion possible)
    // Large unit → Small unit: no information loss
    // seconds → milliseconds: 10 seconds = 10000 milliseconds
    milliseconds ms = s;  // 10000ms (automatic conversion)
    
    std::cout << "Milliseconds: " << ms.count() << std::endl;  // 10000
    
    // Lower → Higher (explicit duration_cast required)
    // Small unit → Large unit: possible information loss
    milliseconds ms2(1500);  // 1500 milliseconds = 1.5 seconds
    // duration_cast<seconds>: explicit conversion (truncate decimal)
    seconds s2 = duration_cast<seconds>(ms2);  // 1s (discard 0.5 seconds)
    
    std::cout << "Seconds: " << s2.count() << std::endl;  // 1
    
    return 0;
}

Time Operations

Here is detailed implementation code using C++. Import the necessary modules and perform branching with conditionals. Understand the role of each part while examining the code.

#include <chrono>
#include <iostream>

int main() {
    using namespace std::chrono;
    
    seconds s1(10);  // 10 seconds
    seconds s2(5);   // 5 seconds
    
    // Addition: duration + duration
    auto sum = s1 + s2;   // 15s
    // Subtraction: duration - duration
    auto diff = s1 - s2;  // 5s
    // Multiplication: duration * scalar
    auto mul = s1 * 2;    // 20s (10 seconds * 2)
    // Division: duration / scalar
    auto div = s1 / 2;    // 5s (10 seconds / 2)
    
    std::cout << "Sum: " << sum.count() << "s" << std::endl;
    std::cout << "Difference: " << diff.count() << "s" << std::endl;
    std::cout << "Multiplication: " << mul.count() << "s" << std::endl;
    std::cout << "Division: " << div.count() << "s" << std::endl;
    
    // Comparison: durations can be compared
    // <, >, <=, >=, ==, != all supported
    if (s1 > s2) {
        std::cout << "s1 is longer" << std::endl;
    }
    
    return 0;
}

4. time_point (Time Point)

Current Time

Here is detailed implementation code using C++. Import the necessary modules and understand the role of each part while examining the code.

#include <chrono>
#include <iostream>

int main() {
    using namespace std::chrono;
    
    // Current time: time_point type
    // system_clock::now(): current system time (wall clock time)
    auto now = system_clock::now();
    
    // Time since epoch: time from Unix epoch (1970-01-01 00:00:00 UTC)
    // time_since_epoch(): convert time_point to duration
    //   current time - epoch = elapsed time
    auto epoch = now.time_since_epoch();
    // duration_cast<milliseconds>: convert to milliseconds
    auto ms = duration_cast<milliseconds>(epoch);
    
    // count(): extract millisecond value (milliseconds since January 1, 1970)
    std::cout << "Since epoch: " << ms.count() << "ms" << std::endl;
    // Example: 1743000000000ms (about 55 years)
    
    return 0;
}

Time Operations

Here is detailed implementation code using C++. Import the necessary modules and understand the role of each part while examining the code.

#include <chrono>
#include <iostream>

int main() {
    using namespace std::chrono;
    
    auto now = system_clock::now();
    
    // 1 hour later
    auto future = now + hours(1);
    
    // 30 minutes ago
    auto past = now - minutes(30);
    
    // Time difference
    auto diff = future - now;
    auto hours_diff = duration_cast<hours>(diff);
    
    std::cout << "Time difference: " << hours_diff.count() << " hours" << std::endl;
    
    return 0;
}

5. Practical Examples

Example 1: Benchmark

Here is detailed implementation code using C++. Import the necessary modules and understand the role of each part while examining the code.

#include <chrono>
#include <vector>
#include <algorithm>
#include <iostream>

template<typename Func>
void benchmark(const std::string& name, Func func) {
    using namespace std::chrono;
    
    auto start = high_resolution_clock::now();
    func();
    auto end = high_resolution_clock::now();
    
    auto duration = duration_cast<microseconds>(end - start);
    std::cout << name << ": " << duration.count() << "μs" << std::endl;
}

int main() {
    std::vector<int> v(1000000);
    
    benchmark("Generation", [&]() {
        std::generate(v.begin(), v.end(), std::rand);
    });
    
    benchmark("Sorting", [&]() {
        std::sort(v.begin(), v.end());
    });
    
    benchmark("Search", [&]() {
        std::binary_search(v.begin(), v.end(), 500000);
    });
    
    return 0;
}

Output:

Generation: 45231μs
Sorting: 123456μs
Search: 12μs

Example 2: Timeout

Here is the longTask implementation:

#include <chrono>
#include <thread>
#include <future>
#include <iostream>

int longTask() {
    std::this_thread::sleep_for(std::chrono::seconds(2));
    return 42;
}

int main() {
    using namespace std::chrono;
    
    auto future = std::async(std::launch::async, longTask);
    
    // Wait 1 second
    auto status = future.wait_for(seconds(1));
    
    if (status == std::future_status::timeout) {
        std::cout << "Timeout!" << std::endl;
    } else {
        std::cout << "Result: " << future.get() << std::endl;
    }
    
    return 0;
}

Output:

Timeout!

Example 3: Periodic Execution

Here is detailed implementation code using C++. Import the necessary modules and process data with loops. Understand the role of each part while examining the code.

#include <chrono>
#include <thread>
#include <iostream>

void periodicTask() {
    using namespace std::chrono;
    
    auto nextRun = steady_clock::now();
    const auto interval = seconds(1);
    
    for (int i = 0; i < 5; ++i) {
        nextRun += interval;
        
        // Perform task
        std::cout << "Task " << i + 1 << std::endl;
        
        // Wait until next execution
        std::this_thread::sleep_until(nextRun);
    }
}

int main() {
    periodicTask();
    return 0;
}

6. chrono Literals (C++14)

Using Literals

Here is detailed implementation code using C++. Import the necessary modules and process data with loops. Understand the role of each part while examining the code.

#include <chrono>
#include <thread>
#include <iostream>

int main() {
    using namespace std::chrono_literals;
    
    // Create duration with literals
    auto d1 = 10s;      // seconds
    auto d2 = 500ms;    // milliseconds
    auto d3 = 100us;    // microseconds
    auto d4 = 1min;     // minutes
    auto d5 = 2h;       // hours
    
    // Combination
    auto total = 1h + 30min + 45s;
    
    std::cout << "Total time: " 
              << std::chrono::duration_cast<std::chrono::seconds>(total).count() 
              << " seconds" << std::endl;  // 5445 seconds
    
    // With sleep_for
    std::this_thread::sleep_for(100ms);
    
    return 0;
}

7. Common Issues

Issue 1: Unit Conversion

Here is detailed implementation code using C++. Import the necessary modules and understand the role of each part while examining the code.

#include <chrono>
#include <iostream>

int main() {
    using namespace std::chrono;
    
    seconds sec(10);
    
    // ❌ Implicit conversion not allowed (lower → higher)
    // milliseconds ms = sec;  // Error
    
    // ✅ duration_cast
    auto ms = duration_cast<milliseconds>(sec);
    std::cout << "Milliseconds: " << ms.count() << std::endl;  // 10000
    
    return 0;
}

Issue 2: Clock Selection

Here is detailed implementation code using C++. Import the necessary modules and process data with loops. Understand the role of each part while examining the code.

#include <chrono>
#include <iostream>
#include <thread>

void wrongClock() {
    using namespace std::chrono;
    
    // ❌ system_clock (affected by system time changes)
    auto start = system_clock::now();
    
    // What if user changes system time?
    std::this_thread::sleep_for(seconds(1));
    
    auto end = system_clock::now();
    auto duration = duration_cast<milliseconds>(end - start);
    
    // Could be negative!
    std::cout << "Elapsed: " << duration.count() << "ms" << std::endl;
}

void correctClock() {
    using namespace std::chrono;
    
    // ✅ steady_clock (guaranteed monotonic)
    auto start = steady_clock::now();
    
    std::this_thread::sleep_for(seconds(1));
    
    auto end = steady_clock::now();
    auto duration = duration_cast<milliseconds>(end - start);
    
    // Always positive
    std::cout << "Elapsed: " << duration.count() << "ms" << std::endl;
}

int main() {
    correctClock();
    return 0;
}

Issue 3: Precision Loss

Here is detailed implementation code using C++. Import the necessary modules and understand the role of each part while examining the code.

#include <chrono>
#include <iostream>

int main() {
    using namespace std::chrono;
    
    milliseconds ms(1500);
    
    // duration_cast: truncation
    auto s1 = duration_cast<seconds>(ms);
    std::cout << "Truncation: " << s1.count() << "s" << std::endl;  // 1s (500ms loss)
    
    // C++17: round
    auto s2 = round<seconds>(ms);
    std::cout << "Round: " << s2.count() << "s" << std::endl;  // 2s
    
    // C++17: ceil
    auto s3 = ceil<seconds>(ms);
    std::cout << "Ceil: " << s3.count() << "s" << std::endl;  // 2s
    
    // C++17: floor
    auto s4 = floor<seconds>(ms);
    std::cout << "Floor: " << s4.count() << "s" << std::endl;  // 1s
    
    return 0;
}

Issue 4: Type Inference

Here is detailed implementation code using C++. Import the necessary modules and understand the role of each part while examining the code.

#include <chrono>
#include <iostream>

int main() {
    using namespace std::chrono;
    
    // ❌ Type unclear
    auto d1 = 10;  // int
    
    // ✅ Explicit type
    seconds d2(10);
    
    // ✅ Literal (C++14)
    using namespace std::chrono_literals;
    auto d3 = 10s;
    
    std::cout << "d2: " << d2.count() << "s" << std::endl;
    std::cout << "d3: " << d3.count() << "s" << std::endl;
    
    return 0;
}

8. Practical Example: Performance Profiler

Here is detailed implementation code using C++. Import the necessary modules, define a class to encapsulate data and functionality, process data with loops, and perform branching with conditionals. Understand the role of each part while examining the code.

#include <chrono>
#include <string>
#include <map>
#include <iostream>

class Profiler {
    using Clock = std::chrono::high_resolution_clock;
    using TimePoint = std::chrono::time_point<Clock>;
    
    std::map<std::string, TimePoint> starts;
    std::map<std::string, long long> totals;  // microseconds
    
public:
    void start(const std::string& name) {
        starts[name] = Clock::now();
    }
    
    void stop(const std::string& name) {
        auto end = Clock::now();
        auto it = starts.find(name);
        
        if (it != starts.end()) {
            auto duration = std::chrono::duration_cast<std::chrono::microseconds>(
                end - it->second
            );
            totals[name] += duration.count();
            starts.erase(it);
        }
    }
    
    void report() {
        std::cout << "\n=== Profiling Results ===" << std::endl;
        
        for (const auto& [name, total] : totals) {
            if (total < 1000) {
                std::cout << name << ": " << total << "μs" << std::endl;
            } else if (total < 1000000) {
                std::cout << name << ": " << (total / 1000.0) << "ms" << std::endl;
            } else {
                std::cout << name << ": " << (total / 1000000.0) << "s" << std::endl;
            }
        }
    }
};

void algorithm1() {
    for (int i = 0; i < 1000000; ++i) {}
}

void algorithm2() {
    for (int i = 0; i < 2000000; ++i) {}
}

int main() {
    Profiler profiler;
    
    // Measure algorithm 1
    profiler.start("algorithm1");
    algorithm1();
    profiler.stop("algorithm1");
    
    // Measure algorithm 2
    profiler.start("algorithm2");
    algorithm2();
    profiler.stop("algorithm2");
    
    // Run multiple times
    for (int i = 0; i < 5; ++i) {
        profiler.start("algorithm1");
        algorithm1();
        profiler.stop("algorithm1");
    }
    
    profiler.report();
    
    return 0;
}

Output:

=== Profiling Results ===
algorithm1: 25.5ms
algorithm2: 8.3ms

Summary

Key Points

  1. chrono: C++11 time library
  2. duration: Time interval (seconds, milliseconds, etc.)
  3. time_point: Time point
  4. clock: system_clock, steady_clock, high_resolution_clock
  5. duration_cast: Unit conversion
  6. Literals: 10s, 500ms (C++14)

Clock Selection Guide

PurposeClockReason
Performance measurementsteady_clockMonotonic
Timersteady_clockUnaffected by time adjustment
Current timesystem_clockSystem time
Timestampsystem_clockUnix epoch conversion
Precise measurementhigh_resolution_clockHighest precision

Practical Tips

Usage Principles:

  • Performance measurement: steady_clock
  • Current time: system_clock
  • Lower→Higher conversion: duration_cast
  • Use literals (10s, 500ms)

Performance:

  • steady_clock is fastest
  • duration_cast is compile-time
  • Literals improve readability
  • Use Timer class with RAII

Cautions:

  • system_clock affected by time adjustment
  • duration_cast truncates (precision loss)
  • Lower→Higher conversion is explicit
  • Negative duration possible

Next Steps