clock() 是 C 标准库 <time.h> 中提供的一个函数,它主要用于测量程序(或程序的某个部分)的CPU 运行时间。
函数原型
#include <time.h> clock_t clock(void);
- 返回值类型:
clock_t,这是一个在<time.h>中定义的算术类型(通常是long或long long)。 - 参数: 无。
返回值
clock() 函数返回自程序启动以来,处理器(CPU)所消耗的时钟计时单元(clock ticks)的数量。
这个数值本身可能不是直观的秒数,你需要通过一个换算系数来将其转换为秒,这个换算系数是 CLOCKS_PER_SEC,它同样在 <time.h> 中定义。
核心换算公式:
double seconds = (double)(clock()) / CLOCKS_PER_SEC;
CLOCKS_PER_SEC
CLOCKS_PER_SEC 是一个宏,它表示每秒钟有多少个时钟计时单元,在不同的系统上,它的值可能不同,但通常是 1000000。
- 在 Windows 上:
CLOCKS_PER_SEC通常是 1000000 (1,000,000)。 - 在 Linux/macOS 上:
CLOCKS_PER_SEC通常是 1000000 (1,000,000) 或 1000000000 (1,000,000,000),具体取决于编译器和系统实现,但绝大多数现代系统上都是 1000000。
重要提示: 不要直接假设 CLOCKS_PER_SEC 的值,而是直接使用宏名进行计算,以保证代码的可移植性。
clock() 的行为特点
-
测量 CPU 时间,而非 wall-clock 时间:
- CPU 时间:指程序实际占用 CPU 并执行指令的时间,如果你的程序在等待用户输入、等待磁盘 I/O 或网络请求,这段时间不会被计入
clock()的返回值。 - Wall-clock 时间:指我们日常生活中使用的挂钟时间,从开始到结束的总时间。
clock()不测量这个。
- CPU 时间:指程序实际占用 CPU 并执行指令的时间,如果你的程序在等待用户输入、等待磁盘 I/O 或网络请求,这段时间不会被计入
-
精度有限:
clock()的精度取决于系统的时钟频率。CLOCKS_PER_SEC是 1000000,那么它的精度就是微秒级别,但实际上,系统可能无法提供如此高的精度,通常会取整到系统时钟的“滴答”周期。
-
返回值溢出:
clock_t是一个有符号整数,如果程序运行时间非常长,超过了clock_t能表示的最大值,它可能会溢出并回绕到负数,虽然对于大多数短时间测量任务来说这不是问题,但在长时间运行的程序中需要注意。
实际应用示例
示例 1:测量一段代码的执行时间
这是最常见的用法。
#include <stdio.h>
#include <time.h>
int main() {
// 1. 记录开始时间
clock_t start, end;
double cpu_time_used;
start = clock();
// --- 这是你要测量时间的代码段 ---
// 模拟一段耗时工作
long long i;
for (i = 0; i < 1000000000; ++i) {
// 空循环,用于消耗CPU时间
}
// ---------------------------------
// 2. 记录结束时间
end = clock();
// 3. 计算并打印耗时
cpu_time_used = ((double)(end - start)) / CLOCKS_PER_SEC;
printf("代码执行耗时: %f 秒\n", cpu_time_used);
return 0;
}
代码解释:
start = clock();:在代码段开始前调用clock(),记录下此时的时钟单元数。end = clock();:在代码段结束后调用clock(),记录下此时的时钟单元数。end - start:得到这段代码执行所消耗的总时钟单元数。((double)(end - start)) / CLOCKS_PER_SEC:将时钟单元数转换为秒数。
示例 2:测量整个程序的运行时间
你可以在 main 函数的开头和结尾分别调用 clock()。
#include <stdio.h>
#include <time.h>
int main() {
clock_t start, end;
double total_time;
start = clock();
// ... 程序的其他所有代码 ...
end = clock();
total_time = ((double)(end - start)) / CLOCKS_PER_SEC;
printf("程序总运行耗时: %f 秒\n", total_time);
return 0;
}
clock() vs. time()
| 特性 | clock() |
time() |
|---|---|---|
| 头文件 | <time.h> |
<time.h> |
| 测量对象 | CPU 运行时间 (Process CPU time) | 挂钟时间 (Calendar/real time) |
| 返回值 | clock_t (自启动以来的时钟单元数) |
time_t (自 Epoch (1970-01-01) 以来的秒数) |
| 适用场景 | 测量算法效率、代码执行性能。 | 获取当前时间、计算时间间隔、记录事件发生的时间。 |
| 精度 | 通常较高(微秒级),但取决于 CLOCKS_PER_SEC。 |
通常较低(秒级)。 |
- 当你想知道“我的代码运行得有多快?”(关注 CPU 效率)时,使用
clock()。 - 当你想知道“现在几点了?”或“这个操作从开始到结束花了多少秒?”(关注真实世界的时间流逝)时,使用
time()。
局限性与替代方案
对于更精确或更复杂的性能分析,clock() 可能不是最佳选择。
局限性:
- 无法测量线程时间:
clock()测量的是整个进程的 CPU 时间,无法单独测量某个线程的时间。 - 精度有限:对于现代高性能计算,微秒级的精度可能不够。
- 平台差异:虽然可移植,但不同平台的实现细节可能存在细微差别。
替代方案:
- C11 标准:C11 引入了
<threads.h>和<chrono.h>(虽然<chrono.h>后来被移出标准,但概念被采纳),提供了更现代、更精确的时间测量工具,如timespec_get。 - POSIX 标准 (Linux/Unix/macOS):
gettimeofday():提供微秒级的挂钟时间。clock_gettime():功能强大且灵活,可以获取不同类型的时钟(如CLOCK_REALTIME挂钟时间,CLOCK_PROCESS_CPUTIME_ID进程CPU时间,CLOCK_THREAD_CPUTIME_ID线程CPU时间),精度可达纳秒级,这是目前推荐的高性能测量方法。
- Windows API:
QueryPerformanceCounter()和QueryPerformanceFrequency():提供高精度(通常微秒或更高)的计时,是 Windows 上进行性能分析的首选。
clock() 是 C 语言中一个简单、有效且基础的测量 CPU 执行时间的工具,对于大多数学习和一般性的性能测试来说,它完全够用,理解它能测量 CPU 时间而非真实时间,并掌握 CLOCKS_PER_SEC 的换算方法,是使用好这个函数的关键,在进行更专业或更高要求的性能分析时,则需要考虑使用更现代和强大的平台特定 API。
