哈喽,各位好!今天咱们来聊聊C++ std::chrono
计时库,这玩意儿听起来高大上,其实就是个帮你精确测量时间的工具。就像你在厨房里用的计时器,只不过它更高级,更精确,而且能玩出更多花样。
咱们今天要讲的主要有三个方面:时间点(time_point
)、时长(duration
)和时钟精度(clock
)。这三者是std::chrono
的核心,理解了它们,你就能像个时间魔法师一样,在你的程序里自由地操纵时间。
1. 时长(duration
):时间流逝的长度
时长,顾名思义,就是一段时间的长度。比如,你说“我睡了8个小时”,这里的“8个小时”就是一个时长。在std::chrono
里,时长用std::chrono::duration
来表示。
std::chrono::duration
的定义方式是这样的:
std::chrono::duration<Rep, Period>
Rep
(Representation): 表示时长所用的数值类型,比如int
,long long
,double
等等。默认是int
。Period
(Ratio): 表示Rep
所代表的时间单位。它是一个std::ratio
类型,表示一个编译时有理数。
举个例子,std::chrono::seconds
实际上是 std::chrono::duration<long long, std::ratio<1, 1>>
的一个别名,也就是说,它用 long long
来存储秒数,并且每个 long long
代表 1 秒。 std::chrono::milliseconds
则是 std::chrono::duration<long long, std::ratio<1, 1000>>
的别名,表示毫秒。
常用的预定义时长类型:
类型 | 单位 | 描述 |
---|---|---|
std::chrono::nanoseconds |
纳秒 | 1 秒的十亿分之一 (1/1,000,000,000 秒) |
std::chrono::microseconds |
微秒 | 1 秒的百万分之一 (1/1,000,000 秒) |
std::chrono::milliseconds |
毫秒 | 1 秒的千分之一 (1/1,000 秒) |
std::chrono::seconds |
秒 | 基本的时间单位 |
std::chrono::minutes |
分钟 | 60 秒 |
std::chrono::hours |
小时 | 3600 秒 (60 分钟) |
创建时长对象:
#include <iostream>
#include <chrono>
int main() {
// 创建一个表示 5 秒的时长
std::chrono::seconds five_seconds(5);
// 创建一个表示 100 毫秒的时长
std::chrono::milliseconds hundred_milliseconds(100);
// 创建一个表示 2.5 秒的时长 (注意使用 double)
std::chrono::duration<double, std::ratio<1, 1>> two_and_half_seconds(2.5);
std::cout << "Five seconds: " << five_seconds.count() << " seconds" << std::endl;
std::cout << "Hundred milliseconds: " << hundred_milliseconds.count() << " milliseconds" << std::endl;
std::cout << "Two and a half seconds: " << two_and_half_seconds.count() << " seconds" << std::endl;
return 0;
}
时长对象的常用操作:
count()
: 返回时长所代表的数值。注意,返回值的类型取决于Rep
。- 加减乘除: 时长对象可以进行加减乘除运算,得到新的时长对象。
- 比较: 时长对象可以进行比较运算(
==
,!=
,<
,>
,<=
,>=
)。 - 类型转换: 可以使用
std::chrono::duration_cast
将一个时长转换为另一个时长。
#include <iostream>
#include <chrono>
int main() {
std::chrono::seconds ten_seconds(10);
std::chrono::milliseconds five_hundred_milliseconds(500);
// 加法
auto total_time = ten_seconds + five_hundred_milliseconds;
std::cout << "Total time: " << total_time.count() << " milliseconds" << std::endl; // 输出: 10500
// 减法
auto remaining_time = ten_seconds - std::chrono::seconds(3);
std::cout << "Remaining time: " << remaining_time.count() << " seconds" << std::endl; // 输出: 7
// 乘法
auto doubled_time = ten_seconds * 2;
std::cout << "Doubled time: " << doubled_time.count() << " seconds" << std::endl; // 输出: 20
// 类型转换
auto ten_seconds_in_ms = std::chrono::duration_cast<std::chrono::milliseconds>(ten_seconds);
std::cout << "Ten seconds in milliseconds: " << ten_seconds_in_ms.count() << " milliseconds" << std::endl; // 输出: 10000
// 比较
if (ten_seconds > std::chrono::seconds(5)) {
std::cout << "Ten seconds is greater than five seconds" << std::endl;
}
return 0;
}
duration_cast
的坑:
duration_cast
用于在不同的时长类型之间进行转换。但是,如果目标类型的精度低于源类型,可能会发生截断。
#include <iostream>
#include <chrono>
int main() {
std::chrono::duration<double, std::ratio<1, 1>> fractional_seconds(2.7);
// 转换为整数秒,小数部分会被截断
auto integer_seconds = std::chrono::duration_cast<std::chrono::seconds>(fractional_seconds);
std::cout << "Integer seconds: " << integer_seconds.count() << " seconds" << std::endl; // 输出: 2 (截断了 .7)
return 0;
}
所以,在使用duration_cast
的时候,一定要小心精度损失!
2. 时间点(time_point
):时间轴上的一个瞬间
时间点表示时间轴上的一个特定时刻。 比如,你说“今天早上8点”,这里的“今天早上8点”就是一个时间点。 在std::chrono
里,时间点用std::chrono::time_point
来表示。
std::chrono::time_point
的定义方式是这样的:
std::chrono::time_point<Clock, Duration>
Clock
: 表示时间点所使用的时钟。不同的时钟有不同的起点和精度。Duration
: 表示时间点相对于时钟起点的偏移量。
常用的时钟类型:
时钟类型 | 描述 |
---|---|
std::chrono::system_clock |
代表系统的挂钟时间(wall clock time)。它的起点是与系统相关的,通常是UNIX纪元(1970年1月1日 00:00:00 UTC)。这个时钟可能会因为系统时间调整(比如,NTP同步)而发生跳跃。 |
std::chrono::steady_clock |
保证是单调递增的,不会因为系统时间调整而发生跳跃。它通常用于测量时间间隔,因为它的值不受外部因素的影响。但是,steady_clock 的起点是未定义的,所以不能直接用它来表示一个绝对时间点。 |
std::chrono::high_resolution_clock |
提供系统中最高可能的时间精度。它可能是system_clock 或steady_clock 的别名,具体取决于系统的实现。使用high_resolution_clock 通常是为了获得最精确的时间测量,但是要注意,更高的精度并不总是意味着更高的准确性。有些系统可能只是简单地将system_clock 作为high_resolution_clock ,并没有提供真正更高精度的时钟。为了确定high_resolution_clock 是否是steady_clock ,你可以使用std::chrono::is_steady trait:std::chrono::is_steady<std::chrono::high_resolution_clock>::value ,如果结果为true ,则表示high_resolution_clock 是单调的。 |
获取当前时间点:
#include <iostream>
#include <chrono>
int main() {
// 获取当前系统时间点
auto now = std::chrono::system_clock::now();
// 获取当前稳定时钟时间点
auto steady_now = std::chrono::steady_clock::now();
std::cout << "System clock now: " << std::chrono::system_clock::to_time_t(now) << std::endl;
//steady_clock 的值没有明确的意义,通常用于计算时间差
//std::cout << "Steady clock now: " << steady_now.time_since_epoch().count() << std::endl; // 这样写会报编译错误, 因为steady_clock的时间起点是未定义的,所以不能直接转换为time_t
return 0;
}
时间点与时长:
时间点可以加上或减去一个时长,得到一个新的时间点。
#include <iostream>
#include <chrono>
int main() {
// 获取当前时间点
auto now = std::chrono::system_clock::now();
// 计算 5 秒后的时间点
auto five_seconds_later = now + std::chrono::seconds(5);
// 计算 10 毫秒前的时间点
auto ten_milliseconds_earlier = now - std::chrono::milliseconds(10);
std::cout << "Now: " << std::chrono::system_clock::to_time_t(now) << std::endl;
std::cout << "Five seconds later: " << std::chrono::system_clock::to_time_t(five_seconds_later) << std::endl;
std::cout << "Ten milliseconds earlier: " << std::chrono::system_clock::to_time_t(ten_milliseconds_earlier) << std::endl;
return 0;
}
计算时间间隔:
两个时间点相减,得到一个时长,表示这两个时间点之间的时间间隔。
#include <iostream>
#include <chrono>
#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 duration = end - start;
// 将时间间隔转换为毫秒
auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(duration);
std::cout << "Elapsed time: " << milliseconds.count() << " milliseconds" << std::endl;
return 0;
}
system_clock
和steady_clock
的选择:
- 如果你需要表示一个绝对时间点(比如,某个事件发生的具体时间),你应该使用
system_clock
。 - 如果你只需要测量时间间隔(比如,某个操作耗费的时间),你应该使用
steady_clock
。
将system_clock::time_point
转换为可读的时间字符串:
system_clock::time_point
本身并不是一个可以直接阅读的字符串。你需要将其转换为time_t
类型,然后再使用localtime
或strftime
等函数将其格式化为字符串。
#include <iostream>
#include <chrono>
#include <ctime>
#include <iomanip>
int main() {
auto now = std::chrono::system_clock::now();
std::time_t now_c = std::chrono::system_clock::to_time_t(now);
// 使用 localtime 转换为本地时间
std::tm* now_tm = std::localtime(&now_c);
// 使用 strftime 格式化时间
char buf[100];
std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", now_tm);
std::cout << "Current time: " << buf << std::endl;
// C++20 提供了更方便的格式化方式
// 注意:需要C++20支持
// std::cout << std::format("{:%Y-%m-%d %H:%M:%S}", now) << std::endl;
return 0;
}
3. 时钟精度(clock
):时间测量的精度
时钟精度指的是时钟能够测量的最小时间单位。不同的时钟有不同的精度。比如,有的时钟精度是秒,有的时钟精度是毫秒,有的时钟精度是纳秒。
在std::chrono
里,时钟精度由std::chrono::clock_traits
来定义。
template <class Clock>
struct clock_traits;
clock_traits
包含以下成员:
time_point
: 时钟的时间点类型。duration
: 时钟的时长类型。period
: 时钟的周期(精度)。is_steady
: 指示时钟是否稳定的布尔值。
获取时钟精度:
#include <iostream>
#include <chrono>
int main() {
// 获取 system_clock 的精度
using system_clock = std::chrono::system_clock;
using system_clock_period = system_clock::period;
// system_clock_period::num 是分子, system_clock_period::den 是分母
std::cout << "system_clock period: " << system_clock_period::num << "/" << system_clock_period::den << " seconds" << std::endl;
// 获取 high_resolution_clock 的精度
using high_resolution_clock = std::chrono::high_resolution_clock;
using high_resolution_clock_period = high_resolution_clock::period;
std::cout << "high_resolution_clock period: " << high_resolution_clock_period::num << "/" << high_resolution_clock_period::den << " seconds" << std::endl;
return 0;
}
理解period
:
period
是一个 std::ratio
类型,表示时钟的精度。 例如,如果 period::num
是 1, period::den
是 1000,那么时钟的精度就是 1/1000 秒,也就是 1 毫秒。
为什么要关心时钟精度?
了解时钟精度可以帮助你选择合适的时钟来测量时间。 如果你需要非常精确的时间测量,你应该选择精度更高的时钟。 但是,精度更高的时钟可能也会带来更高的开销。
高级用法:自定义时长和时钟
std::chrono
允许你自定义时长和时钟,以满足特定的需求。
自定义时长:
#include <iostream>
#include <chrono>
int main() {
// 定义一个表示 1/100 秒的时长
using hundredth_of_a_second = std::chrono::duration<long long, std::ratio<1, 100>>;
// 创建一个表示 5 个 1/100 秒的时长
hundredth_of_a_second five_hundredths_of_a_second(5);
std::cout << "Five hundredths of a second: " << five_hundredths_of_a_second.count() << " hundredths of a second" << std::endl;
// 将其转换为毫秒
auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(five_hundredths_of_a_second);
std::cout << "Five hundredths of a second in milliseconds: " << milliseconds.count() << " milliseconds" << std::endl;
return 0;
}
自定义时钟:
自定义时钟比较复杂,通常情况下你不需要这样做。 但是,如果你需要模拟一个时钟,或者使用一个特殊的硬件时钟,你可以自定义时钟。 自定义时钟需要满足 Clock
的要求,包括定义 time_point
、duration
、now()
等成员。
总结:
std::chrono
是一个强大的计时库,可以让你精确地测量时间。 掌握duration
、time_point
和clock
这三个核心概念,你就能在你的程序里灵活地操纵时间。 记住,system_clock
适合表示绝对时间,steady_clock
适合测量时间间隔。 选择合适的时钟和精度,才能得到准确的时间测量结果。
希望今天的讲解对你有所帮助! 以后写代码,就可以精确地测量程序的运行时间,找到性能瓶颈,优化你的代码啦!