C++ std::chrono 완벽 가이드 | duration·time_point·클럭·시간 측정 실전 활용
이 글의 핵심
time() 대신 std::chrono로 정확한 시간 측정. duration, time_point, system_clock, steady_clock, duration_cast, 벤치마크·타임아웃·로그 타임스탬프까지 실전 패턴.
들어가며: “함수 실행 시간이 매번 달라요”
time()으로 측정했는데 결과가 신뢰할 수 없다
벤치마크 코드를 작성했습니다. time()이나 clock()으로 함수 실행 시간을 측정했는데, 실행할 때마다 결과가 들쭉날쭉합니다. 시스템 시계가 NTP로 조정되거나, clock()이 CPU 시간만 측정해서 멀티스레드 환경에서 부정확합니다.
문제의 코드:
// ❌ 나쁜 예: time()은 1초 단위라 짧은 시간 측정 불가
#include <ctime>
void benchmark() {
time_t start = time(nullptr);
doSomething();
time_t end = time(nullptr);
printf("Elapsed: %ld sec\n", end - start); // 1초 미만이면 0
}
// ❌ 나쁜 예: clock()은 CPU 시간이라 sleep 중에는 증가 안 함
#include <ctime>
void benchmark() {
clock_t start = clock();
doSomething(); // sleep 포함 시 부정확
clock_t end = clock();
printf("CPU: %f sec\n", (double)(end - start) / CLOCKS_PER_SEC);
}
chrono로 해결:
// ✅ 좋은 예: steady_clock은 단조 증가, 나노초 단위
#include <chrono>
#include <iostream>
void benchmark() {
auto start = std::chrono::steady_clock::now();
doSomething();
auto end = std::chrono::steady_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
std::cout << "Elapsed: " << elapsed.count() << " ms\n";
}
이 글을 읽으면:
std::chrono의 duration, time_point, clocks를 이해할 수 있습니다.duration_cast로 단위 변환을 할 수 있습니다.- 벤치마크, 타임아웃, 로그 타임스탬프 등 실전 패턴을 적용할 수 있습니다.
- 자주 발생하는 에러와 프로덕션 패턴을 알 수 있습니다.
개념을 잡는 비유
시간·파일·로그·JSON은 도구 상자의 자주 쓰는 렌치입니다. 표준·검증된 라이브러리로 한 가지 규칙을 정해 두면, 팀 전체가 같은 단위·같은 포맷으로 맞출 수 있습니다.
목차
- 실무에서 겪는 문제 시나리오
- duration: 시간 구간
- time_point: 시점
- clocks: system_clock, steady_clock, high_resolution_clock
- duration_cast와 단위 변환
- C++20 Calendar·날짜 연산
- 시간 측정 실전 예제
- 자주 발생하는 에러와 해결법
- 베스트 프랙티스
- 프로덕션 패턴
- 체크리스트
- 정리
1. 실무에서 겪는 문제 시나리오
시나리오 1: 벤치마크 결과가 실행마다 다르다
문제: 알고리즘 A와 B의 성능을 비교하는데, time(nullptr)로 측정하면 둘 다 0초로 나옵니다. 1초 단위라 밀리초 단위 차이를 잡을 수 없습니다.
// ❌ 문제: 1초 미만 실행 시 항상 0
time_t t1 = time(nullptr);
algorithmA();
time_t t2 = time(nullptr);
printf("A: %ld sec\n", t2 - t1); // 0.05초 걸려도 0 출력
해결: std::chrono::steady_clock과 duration_cast로 밀리초·마이크로초 단위 측정.
// ✅ 해결
#include <chrono>
auto start = std::chrono::steady_clock::now();
algorithmA();
auto end = std::chrono::steady_clock::now();
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
std::cout << "A: " << ms.count() << " ms\n";
시나리오 2: 타임아웃 계산이 NTP 조정 시 꼬인다
문제: “30초 후에 타임아웃”을 system_clock::now() + 30초로 계산했는데, 실행 중 NTP가 시스템 시계를 1분 뒤로 되돌리면 타임아웃이 영원히 오지 않습니다.
// ❌ 문제: system_clock은 시스템 시계 변경에 영향받음
auto deadline = std::chrono::system_clock::now() + std::chrono::seconds(30);
while (std::chrono::system_clock::now() < deadline) {
doWork(); // NTP 조정 시 deadline이 과거가 되어 무한 루프 가능
}
해결: 타임아웃에는 steady_clock을 사용합니다. steady_clock은 단조 증가하므로 NTP 영향 없음.
// ✅ 해결
auto start = std::chrono::steady_clock::now();
auto timeout = std::chrono::seconds(30);
while (std::chrono::steady_clock::now() - start < timeout) {
doWork();
}
시나리오 3: 로그에 사람이 읽을 수 있는 시간을 찍고 싶다
문제: steady_clock::now()는 “부팅 후 경과 시간”이라 사람이 읽기 어렵습니다. “2026-03-11 14:30:00” 형태로 출력하려면 system_clock과 포맷 변환이 필요합니다.
// ❌ steady_clock은 "부팅 후 나노초" 같은 값
auto tp = std::chrono::steady_clock::now();
// 사람이 읽을 수 있는 형식으로 변환 불가
// ✅ system_clock은 실제 날짜/시간과 대응
auto tp = std::chrono::system_clock::now();
// C++20 chrono 또는 localtime으로 포맷 가능
시나리오 4: duration 단위가 달라서 컴파일 에러
문제: std::chrono::seconds와 std::chrono::milliseconds를 직접 더하거나 비교하려 하면 타입이 달라 컴파일 에러가 납니다.
// ❌ 문제: 서로 다른 duration 타입
auto s = std::chrono::seconds(1);
auto ms = std::chrono::milliseconds(500);
auto sum = s + ms; // ✅ C++11부터는 공통 타입으로 자동 변환되어 OK
// 하지만 특정 단위로 캐스팅할 때는 duration_cast 필요
시나리오 5: Asio deadline_timer에 chrono duration 전달
문제: Boost.Asio나 std::experimental::net의 async_wait에는 chrono::duration을 직접 넘겨야 합니다. int 초를 넘기면 타입 에러가 납니다.
// ❌ 문제
timer.expires_after(30); // 30이 뭔지? 초? 밀리초?
// ✅ 해결
timer.expires_after(std::chrono::seconds(30));
시나리오 6: “다음 월요일” 같은 날짜 계산이 어렵다
문제: localtime과 tm 구조체로 “다음 주 월요일”, “이번 달 마지막 날” 같은 날짜를 계산하려면 수동으로 일수·윤년·월 경계를 처리해야 합니다. 실수하기 쉽고 가독성이 떨어집니다.
// ❌ C++17 이하: 수동 날짜 계산
std::tm tm = *std::localtime(&tt);
tm.tm_mday += 7; // 7일 후? 월 넘어가면?
mktime(&tm); // 정규화 필요
해결: C++20 std::chrono::year_month_day, weekday 등 calendar 타입을 사용하면 타입 안전하고 직관적입니다.
// ✅ C++20: calendar 타입으로 명확한 날짜 연산
#include <chrono>
using namespace std::chrono;
auto today = floor<days>(system_clock::now());
year_month_day ymd = year_month_day{sys_days{today}};
// 다음 월요일: weekday_indexed 활용
2. duration: 시간 구간
기본 개념
std::chrono::duration은 시간의 길이를 표현합니다. 템플릿 인자로 “표현 타입(Rep)“과 “단위 비율(Ratio)“을 받습니다.
// 복사해 붙여넣은 뒤: g++ -std=c++17 -o duration_basic duration_basic.cpp && ./duration_basic
#include <chrono>
#include <iostream>
int main() {
// 기본 duration 타입들
std::chrono::nanoseconds ns(1000000000); // 10^9 나노초 = 1초
std::chrono::microseconds us(1000000); // 10^6 마이크로초 = 1초
std::chrono::milliseconds ms(1000); // 1000 밀리초 = 1초
std::chrono::seconds s(1); // 1초
std::chrono::minutes m(1); // 1분
std::chrono::hours h(1); // 1시간
std::cout << "1초 = " << ns.count() << " ns\n";
std::cout << "1초 = " << ms.count() << " ms\n";
std::cout << "1분 = " << std::chrono::duration_cast<std::chrono::seconds>(m).count() << "초\n";
// 연산
auto sum = std::chrono::seconds(30) + std::chrono::milliseconds(500);
std::cout << "30.5초 = " << std::chrono::duration_cast<std::chrono::milliseconds>(sum).count() << " ms\n";
return 0;
}
실행 결과:
1초 = 1000000000 ns
1초 = 1000 ms
1분 = 60초
30.5초 = 30500 ms
duration 내부 구조:
Rep:int64_t,double등 실제 값을 저장하는 타입Period:std::ratio<1, 1000>= 1/1000초 = 밀리초
duration 연산
#include <chrono>
int main() {
using namespace std::chrono;
auto d1 = seconds(10);
auto d2 = milliseconds(500);
// 덧셈, 뺄셈 (공통 타입으로 자동 변환)
auto sum = d1 + d2; // 10.5초
auto diff = d1 - d2; // 9.5초
// 곱셈, 나눗셈 (스칼라와)
auto doubled = d1 * 2; // 20초
auto half = d1 / 2; // 5초
// 비교
bool b = (d1 > d2); // true
// count(): 내부 값 추출
auto ms = duration_cast<milliseconds>(sum);
std::cout << ms.count() << " ms\n"; // 10500
return 0;
}
커스텀 duration
#include <chrono>
#include <iostream>
// 1/100 초 = 10ms 단위
using Deciseconds = std::chrono::duration<int, std::ratio<1, 10>>;
// 1/30 초 (프레임 기반 게임용)
using FrameDuration = std::chrono::duration<double, std::ratio<1, 30>>;
int main() {
Deciseconds ds(5); // 0.5초
FrameDuration fd(1); // 1프레임 = 1/30초
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(ds);
std::cout << "5 deciseconds = " << ms.count() << " ms\n";
return 0;
}
3. time_point: 시점
기본 개념
std::chrono::time_point는 특정 시점을 표현합니다. “어떤 clock의 epoch(기준 시점)으로부터 얼마나 지났는지”를 duration으로 저장합니다.
// 복사해 붙여넣은 뒤: g++ -std=c++17 -o timepoint_basic timepoint_basic.cpp && ./timepoint_basic
#include <chrono>
#include <iostream>
int main() {
// 현재 시점
auto now_sys = std::chrono::system_clock::now();
auto now_steady = std::chrono::steady_clock::now();
// time_point + duration = time_point
auto future = now_sys + std::chrono::hours(24);
auto past = now_sys - std::chrono::seconds(3600);
// time_point - time_point = duration
auto elapsed = now_steady - (now_steady - std::chrono::milliseconds(100));
std::cout << "Elapsed: " << elapsed.count() << " (steady_clock 단위)\n";
// system_clock::time_point -> time_t (C API 호환)
auto tt = std::chrono::system_clock::to_time_t(now_sys);
std::cout << "time_t: " << tt << "\n";
return 0;
}
time_point 연산
#include <chrono>
int main() {
using namespace std::chrono;
auto tp = steady_clock::now();
// 시점 + 구간 = 시점
auto later = tp + seconds(10);
auto earlier = tp - milliseconds(500);
// 시점 - 시점 = 구간
auto diff = later - tp; // 10초
auto ms = duration_cast<milliseconds>(diff);
std::cout << ms.count() << " ms\n";
return 0;
}
4. clocks: system_clock, steady_clock, high_resolution_clock
세 가지 표준 클럭
flowchart TB
subgraph system["system_clock"]
S1[실제 날짜/시간]
S2[NTP 영향 받음]
S3[to_time_t 가능]
end
subgraph steady["steady_clock"]
T1[단조 증가]
T2[NTP 영향 없음]
T3[벤치마크·타임아웃]
end
subgraph high["high_resolution_clock"]
H1[가장 높은 해상도]
H2[구현에 따라 steady일 수도]
end
| 클럭 | 용도 | NTP 영향 | 단조 증가 |
|---|---|---|---|
system_clock | 실제 날짜/시간, 로그 타임스탬프 | 있음 | 아니오 |
steady_clock | 벤치마크, 타임아웃, 경과 시간 | 없음 | 예 |
high_resolution_clock | 최고 해상도 필요 시 | 구현 의존 | 구현 의존 |
system_clock
#include <chrono>
#include <ctime>
#include <iostream>
int main() {
auto now = std::chrono::system_clock::now();
// time_t로 변환 후 localtime
auto tt = std::chrono::system_clock::to_time_t(now);
std::tm* tm = std::localtime(&tt);
char buf[64];
std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", tm);
std::cout << "현재 시간: " << buf << "\n";
// from_time_t: time_t -> time_point
auto tp = std::chrono::system_clock::from_time_t(tt);
return 0;
}
steady_clock
#include <chrono>
#include <iostream>
#include <thread>
int main() {
auto start = std::chrono::steady_clock::now();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
auto end = std::chrono::steady_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
std::cout << "Slept: " << elapsed.count() << " ms\n";
// steady_clock은 to_time_t 없음 (실제 날짜와 무관)
// auto tt = std::chrono::steady_clock::to_time_t(start); // ❌ 컴파일 에러
return 0;
}
high_resolution_clock
#include <chrono>
#include <iostream>
int main() {
// gcc/clang: high_resolution_clock은 steady_clock의 별칭
// MSVC: 별도 구현일 수 있음
auto start = std::chrono::high_resolution_clock::now();
volatile int x = 0;
for (int i = 0; i < 1000; ++i) x += i;
auto end = std::chrono::high_resolution_clock::now();
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(end - start);
std::cout << "Loop: " << ns.count() << " ns\n";
return 0;
}
주의: high_resolution_clock이 steady인지 구현에 따라 다릅니다. 벤치마크에서 “단조 증가”가 중요하면 steady_clock을 쓰는 것이 안전합니다.
5. duration_cast와 단위 변환
duration_cast
서로 다른 단위의 duration을 변환할 때 duration_cast를 사용합니다. 잘림(truncation)이 발생할 수 있습니다.
#include <chrono>
#include <iostream>
int main() {
using namespace std::chrono;
// 1500ms -> 1초 (잘림)
milliseconds ms(1500);
seconds s = duration_cast<seconds>(ms);
std::cout << "1500 ms = " << s.count() << " sec (truncated)\n";
// 1.5초 -> 1500ms
duration<double> sec(1.5);
auto ms2 = duration_cast<milliseconds>(sec);
std::cout << "1.5 sec = " << ms2.count() << " ms\n";
// floor, ceil, round (C++17)
auto sec_floor = floor<seconds>(ms); // 1초
auto sec_ceil = ceil<seconds>(ms); // 2초
auto sec_round = round<seconds>(ms); // 2초 (1.5는 반올림)
std::cout << "floor: " << sec_floor.count() << ", ceil: " << sec_ceil.count()
<< ", round: " << sec_round.count() << "\n";
return 0;
}
floor, ceil, round (C++17)
#include <chrono>
#include <iostream>
int main() {
using namespace std::chrono;
milliseconds ms(1250);
// floor: 내림
auto s_floor = floor<seconds>(ms); // 1초
// ceil: 올림
auto s_ceil = ceil<seconds>(ms); // 2초
// round: 반올림
auto s_round = round<seconds>(ms); // 1초 (1250ms는 1.25초, 반올림 시 1초)
std::cout << "1250 ms -> floor: " << s_floor.count()
<< "s, ceil: " << s_ceil.count()
<< "s, round: " << s_round.count() << "s\n";
return 0;
}
6. C++20 Calendar·날짜 연산
C++20부터 <chrono>에 calendar와 timezone 확장이 추가되었습니다. year, month, day, weekday, year_month_day 등 타입으로 날짜를 타입 안전하게 다룰 수 있습니다.
year_month_day
#include <chrono>
#include <iostream>
#if __cplusplus >= 202002L
int main() {
using namespace std::chrono;
// 2026년 3월 11일
year_month_day ymd{year(2026), month(3), day(11)};
// 유효성 검사
if (ymd.ok()) {
std::cout << "Valid date: " << ymd << "\n";
}
// system_clock::time_point와 변환
auto tp = sys_days{ymd};
auto ymd2 = year_month_day{floor<days>(tp)};
// 오늘 날짜
auto today = year_month_day{floor<days>(system_clock::now())};
std::cout << "Today: " << today << "\n";
return 0;
}
#endif
weekday, month_day_last (요일·월 마지막 날)
#if __cplusplus >= 202002L
#include <chrono>
#include <iostream>
int main() {
using namespace std::chrono;
year_month_day ymd{2026y, March, 11d};
std::cout << "2026-03-11: " << weekday{ymd} << "\n"; // Wednesday
// 2월 마지막 날 (윤년 자동)
year_month_day_last ymdl{year(2026), month_day_last(month(2))};
std::cout << "Last of 2026-02: " << year_month_day{ymdl} << "\n";
return 0;
}
#endif
C++20 chrono vs C++17 이하
| 기능 | C++17 이하 | C++20 |
|---|---|---|
| 날짜 표현 | time_t + tm | year_month_day |
| 요일 | tm.tm_wday 수동 | weekday 타입 |
| 월 마지막 날 | 수동 계산 | month_day_last |
| 포맷 | strftime | std::format |
| 타임존 | 수동 | std::chrono::time_zone |
7. 시간 측정 실전 예제
예제 1: 함수 실행 시간 측정
#include <chrono>
#include <functional>
#include <iostream>
#include <thread>
template <typename Func, typename... Args>
auto measure_ms(Func&& f, Args&&... args) {
auto start = std::chrono::steady_clock::now();
std::invoke(std::forward<Func>(f), std::forward<Args>(args)...);
auto end = std::chrono::steady_clock::now();
return std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
}
int main() {
auto ms = measure_ms([] { std::this_thread::sleep_for(std::chrono::milliseconds(50)); });
std::cout << "Elapsed: " << ms << " ms\n";
return 0;
}
예제 2: RAII 스코프 타이머
#include <chrono>
#include <iostream>
#include <string>
#include <thread>
class ScopedTimer {
public:
explicit ScopedTimer(const std::string& name)
: name_(name), start_(std::chrono::steady_clock::now()) {}
~ScopedTimer() {
auto end = std::chrono::steady_clock::now();
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - start_).count();
std::cout << "[" << name_ << "] " << ms << " ms\n";
}
private:
std::string name_;
std::chrono::steady_clock::time_point start_;
};
void slowFunction() {
ScopedTimer timer("slowFunction");
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
int main() {
slowFunction();
return 0;
}
예제 3: 타임아웃 루프
#include <chrono>
#include <iostream>
#include <thread>
bool doWorkWithTimeout(std::chrono::milliseconds timeout) {
auto deadline = std::chrono::steady_clock::now() + timeout;
while (std::chrono::steady_clock::now() < deadline) {
if (tryDoWork()) {
return true;
}
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
return false;
}
bool tryDoWork() {
static int count = 0;
return ++count >= 5; // 5번째 시도에서 성공
}
int main() {
if (doWorkWithTimeout(std::chrono::seconds(1))) {
std::cout << "Success\n";
} else {
std::cout << "Timeout\n";
}
return 0;
}
예제 4: 로그 타임스탬프 (system_clock)
#include <chrono>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <sstream>
std::string getTimestamp() {
auto now = std::chrono::system_clock::now();
auto tt = std::chrono::system_clock::to_time_t(now);
std::tm tm_buf;
#ifdef _WIN32
localtime_s(&tm_buf, &tt);
#else
localtime_r(&tt, &tm_buf);
#endif
std::ostringstream oss;
oss << std::put_time(&tm_buf, "%Y-%m-%d %H:%M:%S");
return oss.str();
}
int main() {
std::cout << "[" << getTimestamp() << "] Application started\n";
return 0;
}
예제 5: duration 산술 종합
#include <chrono>
#include <iostream>
int main() {
using namespace std::chrono;
// 다양한 단위 혼합
auto total = hours(1) + minutes(30) + seconds(45) + milliseconds(123);
std::cout << "Total: " << duration_cast<milliseconds>(total).count() << " ms\n";
// 비율 계산
auto fps = 60.0;
auto frame_time = duration<double, std::ratio<1>>(1.0 / fps);
auto frame_ms = duration_cast<milliseconds>(frame_time);
std::cout << "60 FPS = " << frame_ms.count() << " ms per frame\n";
return 0;
}
예제 6: time() vs chrono 해상도·정확도 비교
| 측정 방식 | 최소 해상도 | NTP 영향 | sleep 포함 시 | 멀티스레드 |
|---|---|---|---|---|
time() | 1초 | 있음 | 부정확 | 부정확 |
clock() | ~1ms | 없음 | CPU 시간만 (sleep 미포함) | 프로세스 전체 합산 |
steady_clock | 나노초 | 없음 | 정확 | 정확 |
system_clock | 나노초 | 있음 | 정확 | 정확 |
#include <chrono>
#include <ctime>
#include <iostream>
#include <thread>
void compareResolution() {
// time(): 50ms sleep을 0초로 측정
auto t1 = std::time(nullptr);
std::this_thread::sleep_for(std::chrono::milliseconds(50));
auto t2 = std::time(nullptr);
std::cout << "time(): " << (t2 - t1) << " sec (실제 50ms)\n";
// chrono: 정확히 50ms 측정
auto start = std::chrono::steady_clock::now();
std::this_thread::sleep_for(std::chrono::milliseconds(50));
auto end = std::chrono::steady_clock::now();
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
std::cout << "chrono: " << ms << " ms\n";
}
실행 결과 예시:
time(): 0 sec (실제 50ms)
chrono: 50 ms
8. 자주 발생하는 에러와 해결법
에러 1: system_clock을 타임아웃에 사용
증상: NTP 조정 후 타임아웃이 오지 않거나, 너무 빨리 발생합니다.
// ❌ 잘못된 사용
auto deadline = std::chrono::system_clock::now() + std::chrono::seconds(30);
while (condition && std::chrono::system_clock::now() < deadline) { ... }
해결: 타임아웃·경과 시간에는 steady_clock을 사용합니다.
// ✅ 올바른 사용
auto start = std::chrono::steady_clock::now();
auto timeout = std::chrono::seconds(30);
while (condition && (std::chrono::steady_clock::now() - start < timeout)) { ... }
에러 2: duration_cast 잘림 무시
증상: 1500ms를 seconds로 캐스팅하면 1초가 되어 0.5초가 사라집니다.
// ❌ 주의: 잘림 발생
auto ms = std::chrono::milliseconds(1500);
auto s = std::chrono::duration_cast<std::chrono::seconds>(ms);
// s.count() == 1 (0.5초 손실)
해결: duration<double, std::ratio<1>>로 부동소수 초를 쓰거나, floor/ceil/round로 의도를 명확히 합니다.
// ✅ 부동소수로 정밀도 유지
auto ms = std::chrono::milliseconds(1500);
std::chrono::duration<double> sec(ms);
// sec.count() == 1.5
// ✅ 반올림이 필요하면 round
auto s = std::chrono::round<std::chrono::seconds>(ms);
에러 3: steady_clock을 로그 타임스탬프에 사용
증상: 로그에 “부팅 후 1234567890 나노초” 같은 값이 찍혀 사람이 읽을 수 없습니다.
// ❌ steady_clock은 실제 날짜/시간 아님
auto tp = std::chrono::steady_clock::now();
// to_time_t 없음 -> localtime 불가
해결: 로그 타임스탬프에는 system_clock을 사용합니다.
// ✅ system_clock + to_time_t + strftime
auto now = std::chrono::system_clock::now();
auto tt = std::chrono::system_clock::to_time_t(now);
std::tm* tm = std::localtime(&tt);
char buf[64];
std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", tm);
에러 4: count()만 사용하고 단위 혼동
증상: duration.count()는 “내부 표현값”이라 단위가 duration 타입에 따라 다릅니다.
// ❌ count()의 의미가 duration 타입에 따라 다름
auto d = std::chrono::milliseconds(1500);
std::cout << d.count(); // 1500 (ms)
auto d2 = std::chrono::seconds(1);
std::cout << d2.count(); // 1 (sec)
해결: 출력 시 단위를 명시하거나, duration_cast로 원하는 단위로 맞춘 뒤 count()를 사용합니다.
// ✅ 단위 명시
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(d);
std::cout << ms.count() << " ms\n";
에러 5: 다른 clock의 time_point 혼용
증상: system_clock::time_point와 steady_clock::time_point를 빼거나 비교하면 컴파일 에러입니다.
// ❌ 컴파일 에러: 서로 다른 clock
auto tp_sys = std::chrono::system_clock::now();
auto tp_steady = std::chrono::steady_clock::now();
auto diff = tp_sys - tp_steady; // 에러!
해결: 같은 clock 내에서만 time_point 연산을 합니다. C++20에서는 clock_cast로 변환할 수 있습니다.
// ✅ 같은 clock 내에서만 연산
auto t1 = std::chrono::steady_clock::now();
// ...
auto t2 = std::chrono::steady_clock::now();
auto elapsed = t2 - t1;
9. 베스트 프랙티스
1. 용도별 clock 선택
| 용도 | 권장 clock |
|---|---|
| 벤치마크, 성능 측정 | steady_clock |
| 타임아웃, 경과 시간 | steady_clock |
| 로그 타임스탬프, 실제 날짜/시간 | system_clock |
| 최고 해상도 필요 (구현 확인 후) | high_resolution_clock |
2. using으로 가독성 향상
using namespace std::chrono;
using namespace std::chrono_literals; // C++14
auto timeout = 30s; // 30초
auto delay = 100ms; // 100밀리초
auto deadline = steady_clock::now() + timeout;
3. chrono_literals (C++14)
#include <chrono>
using namespace std::chrono_literals;
auto d1 = 1s; // seconds(1)
auto d2 = 100ms; // milliseconds(100)
auto d3 = 1.5min; // duration<double, ratio<60>>(1.5)
4. duration을 함수 인자/반환에 사용
// ✅ 명확한 의도
void setTimeout(std::chrono::milliseconds ms);
std::chrono::milliseconds getElapsed();
// ❌ int 초는 모호함
void setTimeout(int seconds); // 초인지 ms인지 불명확
5. 벤치마크 시 워밍업
// 첫 실행은 캐시 미스 등으로 느릴 수 있음
for (int i = 0; i < 3; ++i) {
doWork(); // 워밍업
}
auto start = std::chrono::steady_clock::now();
for (int i = 0; i < 100; ++i) {
doWork();
}
auto end = std::chrono::steady_clock::now();
auto avg = (end - start) / 100;
10. 프로덕션 패턴
패턴 1: 재사용 가능한 타이머 클래스
#include <chrono>
#include <functional>
#include <iostream>
class Timer {
public:
using Callback = std::function<void(std::chrono::milliseconds)>;
void start() {
start_ = std::chrono::steady_clock::now();
}
std::chrono::milliseconds elapsed() const {
auto now = std::chrono::steady_clock::now();
return std::chrono::duration_cast<std::chrono::milliseconds>(now - start_);
}
void stopAndReport(Callback cb) {
cb(elapsed());
}
private:
std::chrono::steady_clock::time_point start_;
};
int main() {
Timer t;
t.start();
std::this_thread::sleep_for(std::chrono::milliseconds(50));
t.stopAndReport( {
std::cout << "Elapsed: " << ms.count() << " ms\n";
});
return 0;
}
패턴 2: 캐시 만료 시간
#include <chrono>
#include <unordered_map>
#include <optional>
template <typename K, typename V>
class TimedCache {
public:
using Clock = std::chrono::steady_clock;
using TimePoint = Clock::time_point;
using Duration = std::chrono::seconds;
explicit TimedCache(Duration ttl) : ttl_(ttl) {}
void insert(const K& key, const V& value) {
cache_[key] = {value, Clock::now()};
}
std::optional<V> get(const K& key) {
auto it = cache_.find(key);
if (it == cache_.end()) return std::nullopt;
if (Clock::now() - it->second.second > ttl_) {
cache_.erase(it);
return std::nullopt;
}
return it->second.first;
}
private:
Duration ttl_;
std::unordered_map<K, std::pair<V, TimePoint>> cache_;
};
int main() {
TimedCache<std::string, int> cache(std::chrono::seconds(60));
cache.insert("key", 42);
if (auto v = cache.get("key")) {
std::cout << "Value: " << *v << "\n";
}
return 0;
}
패턴 3: Asio 타이머와 연동
#include <chrono>
#include <boost/asio.hpp>
void runWithTimeout(boost::asio::io_context& io, std::chrono::seconds timeout) {
boost::asio::steady_timer timer(io, timeout);
timer.async_wait( {
if (!ec) {
std::cout << "Timeout!\n";
}
});
io.run();
}
패턴 4: FPS 제한 (프레임 타이밍)
#include <chrono>
#include <functional>
#include <thread>
void runAtFixedRate(std::chrono::milliseconds frameTime,
std::function<void()> update,
std::function<bool()> isRunning) {
auto nextFrame = std::chrono::steady_clock::now();
while (isRunning()) {
update();
nextFrame += frameTime;
std::this_thread::sleep_until(nextFrame);
}
}
// 60 FPS 사용 예
// runAtFixedRate(16ms, update, []{ return g_running; });
패턴 5: 로그에 마이크로초까지 포함 (C++20)
// C++20: std::format + chrono
#include <chrono>
#include <format>
#include <iostream>
#if __cplusplus >= 202002L
void logWithMicroseconds() {
auto now = std::chrono::system_clock::now();
auto us = std::chrono::duration_cast<std::chrono::microseconds>(
now.time_since_epoch()) % 1000000;
auto tt = std::chrono::system_clock::to_time_t(now);
std::tm* tm = std::localtime(&tt);
std::cout << std::format("{:%Y-%m-%d %H:%M:%S}.{:06d}\n",
*tm, us.count());
}
#endif
11. 체크리스트
구현 전 확인 사항:
- 벤치마크/타임아웃에는
steady_clock사용 - 로그 타임스탬프에는
system_clock사용 -
duration_cast시 잘림(truncation) 고려 -
chrono_literals로30s,100ms등 가독성 향상 - 함수 인자에
std::chrono::duration타입 명시 - 다른 clock의 time_point 혼용 금지
-
count()사용 시 단위 명시
12. 정리
| 개념 | 요약 |
|---|---|
| duration | 시간 구간. seconds, milliseconds 등. 연산 가능. |
| time_point | 시점. clock의 epoch 기준. now(), +, - 지원. |
| system_clock | 실제 날짜/시간. NTP 영향. to_time_t 가능. |
| steady_clock | 단조 증가. 벤치마크·타임아웃에 사용. |
| duration_cast | 단위 변환. floor, ceil, round (C++17). |
핵심 규칙:
- 측정·타임아웃 →
steady_clock - 날짜/시간 표시 →
system_clock - 단위 변환 →
duration_cast(잘림 주의)
자주 묻는 질문 (FAQ)
Q. 이 내용을 실무에서 언제 쓰나요?
A. time() 대신 std::chrono로 정확한 시간 측정. duration, time_point, system_clock, steady_clock, duration_cast, 벤치마크·타임아웃·로그 타임스탬프까지 … 실무에서는 위 본문의 예제와 선택 가이드를 참고해 적용하면 됩니다.
Q. 선행으로 읽으면 좋은 글은?
A. 각 글 하단의 이전 글 또는 관련 글 링크를 따라가면 순서대로 배울 수 있습니다. C++ 시리즈 목차에서 전체 흐름을 확인할 수 있습니다.
Q. 더 깊이 공부하려면?
A. cppreference와 해당 라이브러리 공식 문서를 참고하세요. 글 말미의 참고 자료 링크도 활용하면 좋습니다.
참고 자료
- cppreference: std::chrono
- cppreference: duration
- cppreference: time_point
- C++20: Calendar and timezone
같이 보면 좋은 글 (내부 링크)
이 주제와 연결되는 다른 글입니다.
- C++ 프로파일링 | “어디가 느린지 모르겠어요” perf·gprof로 병목 찾기
- C++ 로깅 라이브러리 (spdlog) | 빠른 로깅과 다중 싱크 [#27-3]
- C++ Boost.Asio 입문 | io_context·async_read
실전 체크리스트
실무에서 이 개념을 적용할 때 확인해야 할 사항입니다.
코드 작성 전
- 이 기법이 현재 문제를 해결하는 최선의 방법인가?
- 팀원들이 이 코드를 이해하고 유지보수할 수 있는가?
- 성능 요구사항을 만족하는가?
코드 작성 중
- 컴파일러 경고를 모두 해결했는가?
- 엣지 케이스를 고려했는가?
- 에러 처리가 적절한가?
코드 리뷰 시
- 코드의 의도가 명확한가?
- 테스트 케이스가 충분한가?
- 문서화가 되어 있는가?
이 체크리스트를 활용하여 실수를 줄이고 코드 품질을 높이세요.
이 글에서 다루는 키워드 (관련 검색어)
C++, std::chrono, duration, time_point, steady_clock, system_clock, duration_cast, C++11, 시간측정, 벤치마크 등으로 검색하시면 이 글이 도움이 됩니다.
관련 글
- C++ 시리즈 전체 보기
- C++ Adapter Pattern 완벽 가이드 | 인터페이스 변환과 호환성
- C++ ADL |
- C++ Aggregate Initialization |