C++ Chrono 상세 가이드 | "시간 라이브러리" 가이드
이 글의 핵심
std::chrono는 duration·time_point·clock으로 시간 간격과 시각을 타입 안전하게 다루는 C++11 라이브러리입니다. 이 글에서는 단위 변환, steady_clock과 system_clock 선택, 측정·타임아웃 코드 작성법을 예제와 함께 다룹니다.
들어가며
**std::chrono**는 C++11에서 도입된 타입 안전한 시간 처리 라이브러리입니다. duration(시간 간격), time_point(시간 지점), clock(시계)의 세 가지 핵심 개념으로 구성됩니다.
1. chrono 기본 구성
핵심 개념
#include <chrono>
#include <iostream>
int main() {
// 1. duration: 시간 간격 (시간의 길이)
// std::chrono::seconds: 초 단위 duration 타입
std::chrono::seconds sec(10); // 10초
// std::chrono::milliseconds: 밀리초 단위 duration 타입
std::chrono::milliseconds ms(1000); // 1000밀리초 = 1초
// 다른 단위: hours, minutes, microseconds, nanoseconds
// 2. time_point: 시간 지점 (특정 시각)
// std::chrono::system_clock::now(): 현재 시스템 시간
// time_point는 epoch(1970-01-01 00:00:00)부터의 duration
auto now = std::chrono::system_clock::now();
// 3. clock: 시계 (시간을 측정하는 방법)
// - system_clock: 시스템 시간 (벽시계 시간, 조정 가능)
// 달력 시간과 변환 가능, 시스템 시간 변경 시 영향 받음
// - steady_clock: 단조 증가 (절대 뒤로 가지 않음)
// 시간 측정에 적합, 시스템 시간 변경 영향 없음
// - high_resolution_clock: 고정밀 (가장 정밀한 시계)
// 보통 steady_clock의 별칭
return 0;
}
Clock 종류
| Clock | 특징 | 사용 시기 |
|---|---|---|
| system_clock | 시스템 시간, 조정 가능 | 현재 시각, 타임스탬프 |
| steady_clock | 단조 증가, 조정 불가 | 성능 측정, 타이머 |
| high_resolution_clock | 최고 정밀도 | 정밀 측정 |
2. 시간 측정
기본 측정
#include <chrono>
#include <iostream>
#include <thread>
int main() {
// using namespace: std::chrono 생략 가능
using namespace std::chrono;
// 시작 시간: 현재 시각 기록
// high_resolution_clock::now(): 가장 정밀한 시계로 현재 시각 측정
// time_point 타입 반환 (특정 시각)
auto start = high_resolution_clock::now();
// 작업 수행: 측정할 코드
for (int i = 0; i < 1000000; ++i) {
// volatile: 컴파일러 최적화 방지 (루프가 제거되지 않도록)
volatile int x = i * i;
}
// 종료 시간: 작업 완료 후 시각 기록
auto end = high_resolution_clock::now();
// 경과 시간: end - start
// end - start: duration 타입 (시간 간격)
// duration_cast<milliseconds>: 밀리초 단위로 변환
// 원래 단위(나노초 등)에서 밀리초로 형변환
auto duration = duration_cast<milliseconds>(end - start);
// count(): duration의 숫자 값 추출
std::cout << "실행 시간: " << duration.count() << "ms" << std::endl;
return 0;
}
출력:
실행 시간: 15ms
Timer 클래스
#include <chrono>
#include <iostream>
// Timer 클래스: RAII 패턴으로 자동 시간 측정
class Timer {
// time_point: 특정 시각을 저장하는 타입
std::chrono::time_point<std::chrono::high_resolution_clock> start;
std::string name;
public:
// 생성자: 타이머 시작
// 초기화 리스트로 start를 현재 시각으로 설정
Timer(const std::string& name = "Timer")
: start(std::chrono::high_resolution_clock::now()), name(name) {}
// 소멸자: 타이머 종료 (객체 소멸 시 자동 호출)
// RAII: 객체 생명주기와 함께 자동으로 시간 측정
~Timer() {
auto end = std::chrono::high_resolution_clock::now();
// duration_cast<microseconds>: 마이크로초 단위로 변환
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
// count(): duration의 숫자 값 추출
std::cout << name << " 경과: " << duration.count() << "μs" << std::endl;
}
// 중간 측정: 타이머를 종료하지 않고 중간 경과 시간 출력
// lap: 랩 타임 (구간 시간)
void lap() {
auto now = std::chrono::high_resolution_clock::now();
// start부터 현재까지의 경과 시간 계산
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(now - start);
std::cout << name << " 랩: " << duration.count() << "ms" << std::endl;
}
};
void processData() {
// Timer 객체 생성: 이 시점부터 시간 측정 시작
Timer t("processData");
// 작업 1
for (int i = 0; i < 1000000; ++i) {}
// 중간 체크: 작업1 완료 시점 출력
t.lap();
// 작업 2
for (int i = 0; i < 2000000; ++i) {}
// 중간 체크: 작업2 완료 시점 출력
t.lap();
// 함수 종료: Timer 소멸자 자동 호출 → 전체 경과 시간 출력
}
int main() {
processData();
return 0;
}
출력:
processData 랩: 5ms
processData 랩: 15ms
processData 경과: 15234μs
3. duration (시간 간격)
기본 사용
#include <chrono>
#include <iostream>
int main() {
using namespace std::chrono;
// 시간 간격 생성: duration 타입
// std::chrono::seconds: 초 단위 duration
seconds sec(10); // 10초
// std::chrono::milliseconds: 밀리초 단위 duration
milliseconds ms(10000); // 10000밀리초 = 10초
// std::chrono::minutes: 분 단위 duration
minutes min(1); // 1분 = 60초
// std::chrono::hours: 시간 단위 duration
hours hr(1); // 1시간 = 60분 = 3600초
// 값 얻기: count() 메서드로 숫자 값 추출
std::cout << "초: " << sec.count() << std::endl; // 10
std::cout << "밀리초: " << ms.count() << std::endl; // 10000
return 0;
}
시간 변환
#include <chrono>
#include <iostream>
int main() {
using namespace std::chrono;
seconds s(10); // 10초
// 상위 → 하위 (암시적 변환 가능)
// 큰 단위 → 작은 단위: 정보 손실 없음
// seconds → milliseconds: 10초 = 10000밀리초
milliseconds ms = s; // 10000ms (자동 변환)
std::cout << "밀리초: " << ms.count() << std::endl; // 10000
// 하위 → 상위 (명시적 duration_cast 필요)
// 작은 단위 → 큰 단위: 정보 손실 가능
milliseconds ms2(1500); // 1500밀리초 = 1.5초
// duration_cast<seconds>: 명시적 변환 (소수점 절삭)
seconds s2 = duration_cast<seconds>(ms2); // 1s (0.5초 버림)
std::cout << "초: " << s2.count() << std::endl; // 1
return 0;
}
시간 연산
#include <chrono>
#include <iostream>
int main() {
using namespace std::chrono;
seconds s1(10); // 10초
seconds s2(5); // 5초
// 덧셈: duration + duration
auto sum = s1 + s2; // 15s
// 뺄셈: duration - duration
auto diff = s1 - s2; // 5s
// 곱셈: duration * 스칼라
auto mul = s1 * 2; // 20s (10초 * 2)
// 나눗셈: duration / 스칼라
auto div = s1 / 2; // 5s (10초 / 2)
std::cout << "합: " << sum.count() << "s" << std::endl;
std::cout << "차: " << diff.count() << "s" << std::endl;
std::cout << "곱: " << mul.count() << "s" << std::endl;
std::cout << "나눔: " << div.count() << "s" << std::endl;
// 비교: duration끼리 비교 가능
// <, >, <=, >=, ==, != 모두 지원
if (s1 > s2) {
std::cout << "s1이 더 김" << std::endl;
}
return 0;
}
4. time_point (시간 지점)
현재 시간
#include <chrono>
#include <iostream>
int main() {
using namespace std::chrono;
// 현재 시간: time_point 타입
// system_clock::now(): 현재 시스템 시간 (벽시계 시간)
auto now = system_clock::now();
// epoch 이후 시간: Unix epoch (1970-01-01 00:00:00 UTC)부터의 시간
// time_since_epoch(): time_point를 duration으로 변환
// 현재 시각 - epoch = 경과 시간
auto epoch = now.time_since_epoch();
// duration_cast<milliseconds>: 밀리초 단위로 변환
auto ms = duration_cast<milliseconds>(epoch);
// count(): 밀리초 값 추출 (1970년 1월 1일부터의 밀리초)
std::cout << "epoch 이후: " << ms.count() << "ms" << std::endl;
// 예: 1743000000000ms (약 55년)
return 0;
}
시간 연산
#include <chrono>
#include <iostream>
int main() {
using namespace std::chrono;
auto now = system_clock::now();
// 1시간 후
auto future = now + hours(1);
// 30분 전
auto past = now - minutes(30);
// 시간 차이
auto diff = future - now;
auto hours_diff = duration_cast<hours>(diff);
std::cout << "시간 차이: " << hours_diff.count() << "시간" << std::endl;
return 0;
}
5. 실전 예제
예제 1: 벤치마크
#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("생성", [&]() {
std::generate(v.begin(), v.end(), std::rand);
});
benchmark("정렬", [&]() {
std::sort(v.begin(), v.end());
});
benchmark("검색", [&]() {
std::binary_search(v.begin(), v.end(), 500000);
});
return 0;
}
출력:
생성: 45231μs
정렬: 123456μs
검색: 12μs
예제 2: 타임아웃
#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);
// 1초 대기
auto status = future.wait_for(seconds(1));
if (status == std::future_status::timeout) {
std::cout << "타임아웃!" << std::endl;
} else {
std::cout << "결과: " << future.get() << std::endl;
}
return 0;
}
출력:
타임아웃!
예제 3: 주기적 실행
#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;
// 작업 수행
std::cout << "작업 " << i + 1 << std::endl;
// 다음 실행까지 대기
std::this_thread::sleep_until(nextRun);
}
}
int main() {
periodicTask();
return 0;
}
6. chrono 리터럴 (C++14)
리터럴 사용
#include <chrono>
#include <thread>
#include <iostream>
int main() {
using namespace std::chrono_literals;
// 리터럴로 duration 생성
auto d1 = 10s; // seconds
auto d2 = 500ms; // milliseconds
auto d3 = 100us; // microseconds
auto d4 = 1min; // minutes
auto d5 = 2h; // hours
// 조합
auto total = 1h + 30min + 45s;
std::cout << "총 시간: "
<< std::chrono::duration_cast<std::chrono::seconds>(total).count()
<< "초" << std::endl; // 5445초
// sleep_for와 함께
std::this_thread::sleep_for(100ms);
return 0;
}
7. 자주 발생하는 문제
문제 1: 단위 변환
#include <chrono>
#include <iostream>
int main() {
using namespace std::chrono;
seconds sec(10);
// ❌ 암시적 변환 안됨 (하위 → 상위)
// milliseconds ms = sec; // 에러
// ✅ duration_cast
auto ms = duration_cast<milliseconds>(sec);
std::cout << "밀리초: " << ms.count() << std::endl; // 10000
return 0;
}
문제 2: 시계 선택
#include <chrono>
#include <iostream>
#include <thread>
void wrongClock() {
using namespace std::chrono;
// ❌ system_clock (시스템 시간 변경 영향)
auto start = system_clock::now();
// 사용자가 시스템 시간을 변경하면?
std::this_thread::sleep_for(seconds(1));
auto end = system_clock::now();
auto duration = duration_cast<milliseconds>(end - start);
// 음수일 수도 있음!
std::cout << "경과: " << duration.count() << "ms" << std::endl;
}
void correctClock() {
using namespace std::chrono;
// ✅ steady_clock (단조 증가 보장)
auto start = steady_clock::now();
std::this_thread::sleep_for(seconds(1));
auto end = steady_clock::now();
auto duration = duration_cast<milliseconds>(end - start);
// 항상 양수
std::cout << "경과: " << duration.count() << "ms" << std::endl;
}
int main() {
correctClock();
return 0;
}
문제 3: 정밀도 손실
#include <chrono>
#include <iostream>
int main() {
using namespace std::chrono;
milliseconds ms(1500);
// duration_cast: 절삭
auto s1 = duration_cast<seconds>(ms);
std::cout << "절삭: " << s1.count() << "s" << std::endl; // 1s (500ms 손실)
// C++17: round (반올림)
auto s2 = round<seconds>(ms);
std::cout << "반올림: " << s2.count() << "s" << std::endl; // 2s
// C++17: ceil (올림)
auto s3 = ceil<seconds>(ms);
std::cout << "올림: " << s3.count() << "s" << std::endl; // 2s
// C++17: floor (내림)
auto s4 = floor<seconds>(ms);
std::cout << "내림: " << s4.count() << "s" << std::endl; // 1s
return 0;
}
문제 4: 타입 추론
#include <chrono>
#include <iostream>
int main() {
using namespace std::chrono;
// ❌ 타입 불명확
auto d1 = 10; // int
// ✅ 명시적 타입
seconds d2(10);
// ✅ 리터럴 (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. 실전 예제: 성능 프로파일러
#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; // 마이크로초
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=== 프로파일링 결과 ===" << 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;
// 알고리즘 1 측정
profiler.start("algorithm1");
algorithm1();
profiler.stop("algorithm1");
// 알고리즘 2 측정
profiler.start("algorithm2");
algorithm2();
profiler.stop("algorithm2");
// 여러 번 실행
for (int i = 0; i < 5; ++i) {
profiler.start("algorithm1");
algorithm1();
profiler.stop("algorithm1");
}
profiler.report();
return 0;
}
출력:
=== 프로파일링 결과 ===
algorithm1: 25.5ms
algorithm2: 8.3ms
정리
핵심 요약
- chrono: C++11 시간 라이브러리
- duration: 시간 간격 (seconds, milliseconds 등)
- time_point: 시간 지점
- clock: system_clock, steady_clock, high_resolution_clock
- duration_cast: 단위 변환
- 리터럴:
10s,500ms(C++14)
Clock 선택 가이드
| 목적 | Clock | 이유 |
|---|---|---|
| 성능 측정 | steady_clock | 단조 증가 |
| 타이머 | steady_clock | 시간 조정 영향 없음 |
| 현재 시각 | system_clock | 시스템 시간 |
| 타임스탬프 | system_clock | Unix epoch 변환 |
| 정밀 측정 | high_resolution_clock | 최고 정밀도 |
실전 팁
사용 원칙:
- 성능 측정은
steady_clock - 현재 시각은
system_clock - 하위→상위 변환은
duration_cast - 리터럴 사용 (
10s,500ms)
성능:
steady_clock이 가장 빠름duration_cast는 컴파일 타임- 리터럴은 가독성 향상
- Timer 클래스로 RAII 활용
주의사항:
system_clock은 시간 조정 영향duration_cast는 절삭 (정밀도 손실)- 하위→상위 변환은 명시적
- 음수 duration 가능
다음 단계
- C++ Duration
- C++ Time Point
- C++ Timer Utilities
관련 글
- C++ duration |
- C++ Chrono 완벽 가이드 |
- C++ Chrono Literals |
- C++ ratio |
- C++ steady_clock |