Apple M1(Monterey 12.0.1)下clock_gettime()计时结果异常问题
问题根因
这个异常是两个问题共同导致的,完全匹配你观察到的所有现象:
- 高优化等级下编译器触发了死代码消除(DCE)
你没有强制使用矩阵乘法输出矩阵C的所有元素,clang在-O3加链接时优化(LTO)的场景下,会通过过程间分析追踪C矩阵的后续使用逻辑:如果print_matrix只打印C矩阵的少量元素、甚至实现存在问题没有真正读取C的内存,编译器就会判定大部分矩阵乘法计算是无用代码,直接优化删除,最终matmul实际只计算了极小部分结果,运行时间自然极短。
你用time命令统计到的4秒左右耗时,几乎全部来自计时开始前的rand_matrix调用:arc4random()是密码学安全随机数生成器,单线程吞吐量仅为每秒数百万次,填充两个4096*4096的double矩阵需要调用3300万次arc4random(),刚好对应4秒左右的CPU耗时,和time输出完全吻合。 clock_gettime调用存在错误且未做返回值检查
无优化等级下你大概率没有正确包含<time.h>头文件,导致clock_gettime被隐式声明为无参数原型的int返回函数,在ARM架构(Apple Silicon等设备)上会出现调用约定不匹配的问题,clock_gettime直接返回错误,没有写入你初始化为{0,0}的tstart和tend结构体,计算出的时间差自然为0。
此外你代码中手写的MONOTONIC_CLOCK、MONOTONIC_CLOCK_RAW是错误的宏名,POSIX标准定义的正确时钟ID为CLOCK_MONOTONIC、CLOCK_MONOTONIC_RAW,使用未定义的宏会被预处理器替换为0,部分系统上会直接触发EINVAL错误导致计时失败。
你在Intel Ubuntu设备上运行正常也和这两点对应:x86_64架构的调用约定对无原型函数容忍度更高,且你在Ubuntu上可能未开启LTO,或print_matrix实现遍历了整个C矩阵,编译器无法消除计算逻辑。
解决方法
按以下步骤排查修复即可:
- 阻止编译器对矩阵乘法做死代码消除
在matmul调用完成后,添加一段不可被优化的逻辑,强制使用C矩阵的所有元素:
测试阶段可以先用-O0编译,确认矩阵乘法本身的运行耗时符合预期,再逐步调高优化等级。// 计时结束后添加 volatile double sink = 0.0; for (int i = 0; i < M; i++) { for (int j = 0; j < M; j++) { sink += C[i][j]; } } printf("matmul result checksum: %f\n", sink); - 修复
clock_gettime调用逻辑- 必须在代码开头正确包含
<time.h>头文件,不要自定义struct timespec结构或时钟相关宏 - 使用正确的时钟ID宏,推荐用
CLOCK_MONOTONIC做性能计时(不受系统时间手动修改影响) - 必须检查
clock_gettime的返回值,出错时直接打印错误信息方便定位:struct timespec tstart = {0, 0}, tend = {0, 0}; if (clock_gettime(CLOCK_MONOTONIC, &tstart) != 0) { perror("get start time failed"); exit(EXIT_FAILURE); } matmul(M, C, A, B); if (clock_gettime(CLOCK_MONOTONIC, &tend) != 0) { perror("get end time failed"); exit(EXIT_FAILURE); }
- 必须在代码开头正确包含
- 性能测试阶段替换慢随机数生成器
性能测试不需要密码学安全的随机数,把arc4random()换成普通的快速随机数实现(比如xorshift、甚至标准库rand()),避免随机数填充占用过多测试时间,干扰你对矩阵乘法本身的性能统计。
内容的提问来源于stack exchange,提问作者jmpnz
相关产品推荐
相关产品推荐

