비율 표현을 위한 ratio
C++11에서 도입된 std::ratio는 컴파일 시점에 유리수(분수)를 표현하기 위한 템플릿 클래스입니다. 분자(N)와 분모(D)를 비타입 템플릿 매개변수로 받아 비율을 정의합니다.
template<std::intmax_t N, std::intmax_t D = 1>
class ratio;
표준 라이브러리는 자주 사용되는 SI 접두어를 기반으로 한 비율 타입을 미리 정의해 두고 있습니다.
std::kilo: 1000/1std::centi: 1/100std::milli: 1/1000std::micro: 1/1000000std::nano: 1/1000000000
시간 간격을 나타내는 duration
std::chrono::duration은 특정 시간 단위로 표현된 시간의 간격(길이)을 나타냅니다. 내부적으로 Rep(tick 수를 저장하는 데이터 타입)와 Period(tick의 길이를 정의하는 ratio)를 사용합니다.
template<class Rep, class Period = std::ratio<1>>
class duration;
다양한 시간 단위를 다음과 같이 선언할 수 있습니다.
std::chrono::duration<int, std::ratio<3600>> two_hours(2); // 2시간
std::chrono::duration<double, std::milli> half_ms(0.5); // 0.5밀리초
표준 라이브러리는 편리함을 위해 일반적인 시간 간격 타입을 별칭으로 제공합니다:
std::chrono::hours,minutes,secondsstd::chrono::milliseconds,microseconds,nanoseconds
저장된 tick 수를 가져오려면 count() 멤버 함수를 호출합니다. 또한, 서로 다른 단위 간의 변환은 std::chrono::duration_cast를 통해 안전하게 수행할 수 있습니다.
std::chrono::seconds sec_interval(120);
// 초를 분으로 변환 (정밀도 손실이 없어야 하므로 duration_cast 사용)
auto min_interval = std::chrono::duration_cast<std::chrono::minutes>(sec_interval);
std::cout << min_interval.count() << " mins\n"; // 출력: 2 mins
특정 시점을 나타내는 time_point
time_point는 특정 시점(시간점)을 표현하며, 기준이 되는 클락(Clock)과 해당 클락의 에포크(Epoch)로부터 경과한 duration으로 구성됩니다.
template<class Clock, class Duration = typename Clock::duration>
class time_point;
현재 시스템 시간을 가져와 C 스타일의 시간 구조체로 변환하는 과정은 다음과 같습니다.
auto current_tp = std::chrono::system_clock::now();
std::time_t raw_time = std::chrono::system_clock::to_time_t(current_tp);
std::tm local_time_info;
localtime_r(&raw_time, &local_time_info); // 스레드 안전 버전 사용
char time_buffer[64];
std::strftime(time_buffer, sizeof(time_buffer), "%Y/%m/%d %H:%M:%S", &local_time_info);
std::cout << time_buffer << '\n';
time_point_cast를 사용하면 시간점의 정밀도(단위)를 조정할 수 있습니다.
시간을 측정하는 클락(Clock)의 종류
chrono 라이브러리는 용도에 따라 세 가지 주요 클락을 제공합니다.
1. system_clock
시스템의 실시간 시계(Wall-clock)를 나타냅니다. NTP 동기화 등으로 인해 시간이 뒤로 갈 수 있으므로(Monotonic하지 않음), 순수한 시간 간격 측정에는 부적합할 수 있습니다. 주로 현재 날짜와 시간을 표현하거나 time_t와 상호 변환할 때 사용합니다.
auto today = std::chrono::system_clock::now();
std::chrono::hours one_day(24);
auto tomorrow = today + one_day;
2. steady_clock
절대 조정되지 않으며 단조 증가(Monotonic)하는 클락입니다. tick 간의 간격이 일정하게 보장되므로, 알고리즘의 성능 측정이나 타임아웃 처리와 같은 시간 간격 측정에 가장 적합합니다.
3. high_resolution_clock
현재 시스템에서 사용 가능한 가장 높은 정밀도를 제공하는 클락입니다. 구현에 따라 steady_clock 또는 system_clock의 별칭일 수 있습니다.
코드 실행 시간 측정을 위한 RAII 타이머
기존의 매크로 기반 시간 측정 방식은 타입 안정성과 스코프 관리 측면에서 취약할 수 있습니다. 대신 현대 C++의 RAII(Resource Acquisition Is Initialization) 패턴을 활용하면 블록 단위의 실행 시간을 훨씬 깔끔하고 안전하게 측정할 수 있습니다. count()나 time_since_epoch()를 직접 호출하여 단위를 잘못 해석하는 실수를 방지하는 데도 도움이 됩니다.
#include <iostream>
#include <chrono>
#include <string>
class ScopedProfiler {
public:
explicit ScopedProfiler(std::string task_name)
: name(std::move(task_name)), start_time(std::chrono::steady_clock::now()) {}
~ScopedProfiler() {
auto end_time = std::chrono::steady_clock::now();
auto elapsed_us = std::chrono::duration_cast<std::chrono::microseconds>(end_time - start_time).count();
if (elapsed_us >= 1000) {
std::cout << "[Profile] " << name << " took " << elapsed_us / 1000.0 << " ms\n";
} else {
std::cout << "[Profile] " << name << " took " << elapsed_us << " us\n";
}
}
private:
std::string name;
std::chrono::steady_clock::time_point start_time;
};
int main() {
{
ScopedProfiler profiler("Heavy Computation");
volatile long long sum = 0;
for (int i = 0; i < 10000000; ++i) {
sum += i;
}
} // 소멸자가 호출되며 자동으로 소요 시간 출력
return 0;
}