在c++中,编写代码只是第一步,衡量代码的“效率”同样重要。你如何知道你的 sort 算法比同事写的快?或者你的新功能导致了多少性能下降?你需要一个“秒表”来精确测量代码的执行时间。
在c++(及c语言)中,你有两种主要的“秒表”:
c 风格 “cpu 秒表” (<ctime>/clock()):
- 比喻: 这是一个老式的“机械秒表”,它只在你的 cpu **真正在为你的程序“工作”(执行指令)**时才会“嘀嗒”。
- 特点: 它测量的是 cpu 时间 (cpu time)。如果你的程序在“等待”(比如
sleep、等待用户输入或等待网络响应),这个秒表会暂停。 - 精度: 较低,受限于
clocks_per_sec宏。
现代 c++ 风格 “高精度电子秒表” (<chrono>):
- 比喻: 这是一个高精度的“电子秒表”,它测量的是墙上时钟 (wall clock) 走过的时间。
- 特点: 它测量的是真实世界流逝的时间。从你按下“开始”到按下“停止”,无论你的程序是在“工作”还是“等待”,它都在计时。
- 精度: 极高(通常可以达到纳秒
ns级别)。 - 这是现代c++中(c++11及以后)强烈推荐的方式。
在本教程中,你将学会:
- c 风格
clock():如何使用“cpu 秒表”。 - c++
std::chrono:如何使用“高精度电子秒表”(推荐!)。 - 新手的“头号噩梦”:cpu 时间 vs 挂钟时间 (wall time) 的致命区别。
- 实战演练:通过代码对比两种计时器的不同结果。
- “x光透 视”:用调试器“亲眼目睹”时间点(
time_point)对象。
前置知识说明 (100% 自洽):
- 变量 (variable):理解存储数据的“盒子”,如
double time_taken;。 - 函数 (function):理解可重复使用的“代码积木”。
#include:如何包含c++标准库(如<iostream>,<ctime>,<chrono>)。cout:c++ 中用于在屏幕上打印信息的“扬声器”。- 类型转换 (casting):如
double(value),将value转换为double类型。 - 编译 (compile):c++代码(“食谱”)必须被“编译”(“烘焙”),才能变成电脑可执行的程序(“蛋糕”)。
第一部分:c 风格clock()(“cpu 秒表”)
clock() 函数在 <ctime> 库中。它返回程序启动到当前时刻为止,cpu 花在你程序上的“嘀嗒数”(clock ticks)。
核心步骤:
- 包含
<ctime>。 - 在代码开始前,调用
clock_t start = clock();记录“开始嘀嗒数”。 - 在代码结束后,调用
clock_t end = clock();记录“结束嘀嗒数”。 - 计算差值
double(end - start),然后除以clocks_per_sec(一个系统常量,表示“每秒多少嘀嗒数”),得到秒数。
clock_example.cpp
#include <iostream>
#include <ctime> // 1. 包含 c 时间库
using namespace std;
// 一个耗时的函数 (模拟 cpu 密集型工作)
void heavy_work() {
// 执行一个(通常会被编译器优化的)空循环来消耗 cpu 时间
// 在实际测试时,应使用 -o0 (关闭优化) 来观察
for(long i = 0; i < 3000000000l; ++i) {}
}
int main() {
cout << "--- 测试 c 风格 <ctime> (clock_t) ---" << endl;
cout << "每秒“嘀嗒”数 (clocks_per_sec): " << clocks_per_sec << endl;
// 1. 获取开始时的“cpu嘀嗒数”
clock_t start = clock();
// 2. 执行你的代码
heavy_work();
// 3. 获取结束时的“cpu嘀嗒数”
clock_t end = clock();
// 4. 计算时间差 (秒)
double cpu_time_taken = double(end - start) / double(clocks_per_sec);
cout << "cpu 耗时: " << cpu_time_taken << " 秒" << endl;
return 0;
}
“手把手”终端模拟 (结果因机器和编译优化而异):
ps c:\mycode> g++ clock_example.cpp -o clock_example.exe -o0 # -o0 关闭优化 ps c:\mycode> .\clock_example.exe --- 测试 c 风格 <ctime> (clock_t) --- 每秒“嘀嗒”数 (clocks_per_sec): 1000 cpu 耗时: 1.156 秒
缺点: 精度不高,且无法测量“等待”时间。
第二部分:现代 c++std::chrono(“高精度电子秒表”)
std::chrono 库 (c++11) 是现代c++的黄金标准。它提供了高精度时钟和时间单位(纳秒、微秒、毫秒等)。
核心步骤:
- 包含
<chrono>。 - 使用
auto start = std::chrono::high_resolution_clock::now();获取当前挂钟时间点。 - 使用
auto end = std::chrono::high_resolution_clock::now();获取结束时间点。 - 计算差值:
end - start,得到一个duration(时间段) 对象。 - 使用
std::chrono::duration_cast将这个duration对象转换为你想要的单位(如毫秒)。
chrono_example.cpp (推荐用法)
#include <iostream>
#include <chrono> // 1. 包含 c++11 时间库
#include <thread> // 2. 包含线程库 (用来演示“等待”)
using namespace std;
// 使用 using 来简化类型名 (可选,但推荐)
using std::chrono::high_resolution_clock;
using std::chrono::duration_cast;
using std::chrono::duration;
using std::chrono::milliseconds;
using std::chrono::microseconds;
// 耗时函数 (包含工作和等待)
void work_and_sleep() {
// 模拟 cpu 工作
for(long i = 0; i < 3000000000l; ++i) {}
// 模拟 i/o 等待或休眠 (500毫秒)
cout << " (正在休眠 500 毫秒...)" << endl;
std::this_thread::sleep_for(milliseconds(500));
}
int main() {
cout << "--- 测试 现代 c++ <chrono> ---" << endl;
// 1. 获取开始时间点
auto start_time = high_resolution_clock::now();
// 2. 执行你的代码
work_and_sleep();
// 3. 获取结束时间点
auto end_time = high_resolution_clock::now();
// 4. 计算时间差
// 结果是一个 duration 对象
auto duration_total = duration_cast<milliseconds>(end_time - start_time);
// 5. 打印结果
cout << "总耗时 (挂钟时间): " << duration_total.count() << " 毫秒" << endl;
// 也可以转换为其他单位
auto duration_us = duration_cast<microseconds>(end_time - start_time);
cout << " (即 " << duration_us.count() << " 微秒)" << endl;
return 0;
}
“手把手”终端模拟 (结果因机器而异):
ps c:\mycode> g++ chrono_example.cpp -o chrono_example.exe -std=c++11 -o0 -pthread ps c:\mycode> .\chrono_example.exe --- 测试 现代 c++ <chrono> --- (正在休眠 500 毫秒...) 总耗时 (挂钟时间): 1658 毫秒 (即 1658390 微秒)
顿悟时刻: 结果 1658 毫秒,约等于 1158 毫秒 (cpu工作) + 500 毫秒 (休眠)。<chrono> 正确地测量了所有流逝的时间!
第三部分:新手的“头号噩梦”——clock()vs<chrono>
让我们把两个“秒表”放在同一个程序里,测量同一个函数,看看它们的区别。
comparison.cpp (关键对比)
#include <iostream>
#include <ctime>
#include <chrono>
#include <thread>
using namespace std;
// (函数 work_and_sleep 同上)
void work_and_sleep() {
for(long i = 0; i < 3000000000l; ++i) {}
cout << " (正在休眠 500 毫秒...)" << endl;
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
int main() {
// --- 1. <chrono> 计时 (挂钟时间) ---
auto start_chrono = std::chrono::high_resolution_clock::now();
// --- 2. <ctime> 计时 (cpu 时间) ---
clock_t start_clock = clock();
// --- 3. 执行同一个函数 ---
cout << "--- 正在执行 work_and_sleep... ---" << endl;
work_and_sleep();
cout << "--- 执行完毕 ---" << endl;
// --- 4. 获取 <ctime> 结果 ---
clock_t end_clock = clock();
double cpu_time = double(end_clock - start_clock) / double(clocks_per_sec);
// --- 5. 获取 <chrono> 结果 ---
auto end_chrono = std::chrono::high_resolution_clock::now();
auto wall_time_ms = std::chrono::duration_cast<std::chrono::milliseconds>(end_chrono - start_chrono);
// --- 6. 打印对比 ---
cout << "\n--- 结果对比 ---" << endl;
cout << "cpu 秒表 (clock()): " << cpu_time * 1000 << " 毫秒" << endl;
cout << "电子秒表 (chrono): " << wall_time_ms.count() << " 毫秒" << endl;
return 0;
}
“手把手”终端模拟 (结果因机器而异):
ps c:\mycode> g++ comparison.cpp -o comparison.exe -std=c++11 -o0 -pthread ps c:\mycode> .\comparison.exe --- 正在执行 work_and_sleep... --- (正在休眠 500 毫秒...) --- 执行完毕 --- --- 结果对比 --- cpu 秒表 (clock()): 1162 毫秒 # <-- 只有 cpu 工作的时间! 电子秒表 (chrono): 1663 毫秒 # <-- cpu 工作 (1162) + 休眠 (500)
“黄金法则”:
- 当你想知道“我的代码占用了多少 cpu 资源?”(用于算法分析),
clock()可以用(但<chrono>也有 cpu 时钟)。 - 当你想知道“用户等了多久?”(从按下按钮到看到结果),永远使用
std::chrono::high_resolution_clock!这几乎是你 99% 情况下想要的答案。
第四部分:“x光透 视”——观察时间点
auto start_time = high_resolution_clock::now(); 返回的对象是一个 time_point(时间点)。它通常只是一个(非常大的)数字,代表从某个“纪 元”(比如1970年1月1日,或计算机启动时)到现在的纳秒数。
“x光”实战(基于chrono_example.cpp)
- 设置断点:
- 在
main函数的第31行(auto start_time = ...)之后。 - 在
main函数的第36行(auto end_time = ...)之后。
- 在
- 启动调试 (f5)。
- 第一次“冻结” (第31行后):
- 观察“变量”(variables)窗口: 找到
start_time。 - 展开
start_time:你会看到它内部可能有一个成员,比如_m_time_since_epoch或_rep(内部表示)。 - 你会看到: 一个巨大的数字,例如
1678886400123456789(纳秒)。
- 观察“变量”(variables)窗口: 找到
- 继续执行 (f5)。
- 第二次“冻结” (第36行后):
- 观察“变量”窗口: 找到
end_time。 - 展开
end_time: - 你会看到: 另一个巨大的数字,例如
1678886401783456789。
- 观察“变量”窗口: 找到
- (关键!)“监视” (watch) 窗口:
- 动作: 在“监视”窗口中,添加这个表达式:
std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time).count() - 你会看到:
1660(或者你的实际测量值)! - 顿悟时刻: 调试器向你展示了
chrono库是如何通过两个“时间点”相减,得到一个“时间段”,并最终转换为你需要的单位的。
- 动作: 在“监视”窗口中,添加这个表达式:
动手试试!(终极挑战:你的“排序分析器”)
现在,你来当一次“算法分析师”。
任务:
- “激活”
iostream,vector,algorithm(为了std::sort),chrono和random(为了生成随机数)。 - 创建一个非常大的
vector<int>(例如,1,000,000个元素),并用随机数字填充它。 - 使用
std::chrono(高精度电子秒表):- 在调用
std::sort之前,获取“开始时间”。 - 调用
std::sort(myvector.begin(), myvector.end());。 - 在
std::sort之后,获取“结束时间”。
- 在调用
- 计算时间差,并以毫秒 (milliseconds) 为单位,打印出排序一百万个随机整数所花费的时间。
sort_timer.cpp (你的 todo):
#include <iostream>
#include <vector>
#include <algorithm> // 需要 std::sort
#include <chrono> // 需要计时
#include <random> // 需要随机数
using namespace std;
// (使用 using std::chrono::... 来简化)
using std::chrono::high_resolution_clock;
using std::chrono::duration_cast;
using std::chrono::milliseconds;
int main() {
// --- 1 & 2: 创建并填充 100 万个随机数 ---
const int n = 1000000;
vector<int> numbers;
numbers.reserve(n); // 预分配内存,提高效率
std::random_device rd; // 随机数种子源
std::mt19937 gen(rd()); // 随机数引擎
std::uniform_int_distribution<> distrib(1, n); // 分布范围
cout << "正在生成 " << n << " 个随机数..." << endl;
for (int i = 0; i < n; ++i) {
numbers.push_back(distrib(gen));
}
cout << "生成完毕。" << endl;
// --- todo 3: 计时 ---
cout << "\n--- 开始排序... ---" << endl;
// auto starttime = ...;
// std::sort(... , ...); // 执行排序
// auto endtime = ...;
// --- todo 4: 计算并打印 ---
// auto timetaken_ms = ...;
// cout << "排序 " << n << " 个元素耗时: "
// << timetaken_ms.count() << " 毫秒" << endl;
return 0;
}
这个挑战让你实践了如何使用 std::chrono 来测量一个真实、有意义的计算任务(std::sort)的性能。完成它,你就掌握了c++中最重要、最准确的计时工具!
以上就是在c++中测量代码执行时间的两种方法的详细内容,更多关于c++测量代码执行时间的资料请关注代码网其它相关文章!
发表评论