g++ -O3编译选项如何优化循环?为何耗时未随runtime增长?
关于GCC -O3优化导致循环耗时不随迭代次数增长的问题分析
你使用以下代码测试程序运行耗时:
#include <stdint.h> #include <time.h> #include <math.h> #include <random> #define N 1000 #define DIM 1000 #define NS_PER_SECOND 1000000000 void sub_timespec(struct timespec t1, struct timespec t2, struct timespec *td) { td->tv_nsec = t2.tv_nsec - t1.tv_nsec; td->tv_sec = t2.tv_sec - t1.tv_sec; if (td->tv_sec > 0 && td->tv_nsec < 0) { td->tv_nsec += NS_PER_SECOND; td->tv_sec--; } else if (td->tv_sec < 0 && td->tv_nsec > 0) { td->tv_nsec -= NS_PER_SECOND; td->tv_sec++; } } float cal_data_sqrtf(float *data, int data_length) { float sum = 0; for (int i = 0; i < data_length; i++) { sum += data[i] * data[i]; } return sqrtf(sum); } int main() { float db_feat[N][DIM]; float db_feat_norm[N]; for (int j = 0; j < N; j++) { for (int i = 0; i < DIM; i++) { db_feat[j][i] = rand()/float(RANDMAX); } db_feat_norm[j] = cal_data_sqrtf(db_feat[j], DIM); } float features[DIM]; for (int i = 0; i < DIM; i++) { features[i] = rand()/float(RANDMAX); } float features_norm = cal_data_sqrtf(features, DIM); int runtime = 10; struct timespec start, end, delta; clock_gettime(CLOCK_REALTIME, &start); float view_sim[N]; for (int k = 0; k < runtime; k++) { for (int j = 0; j < N; j++) { float sum = 0; for (int i = 0; i < N; i++) { sum += db_feat[j][i] * features[i]; } if (sum < 0) sum = 0; view_sim[j] = sum / (db_feat_norm[j] * features_norm); } } clock_gettime(CLOCK_REALTIME, &end); sub_timespec(start, end, &delta); printf("running time: %d.%.9ld\n", (int)delta.tv_sec, delta.tv_nsec); }
使用g++ -std=c++11 -o main main.c编译时,程序运行时间与runtime变量成正比;但添加-O3优化(g++ -std=c++11 -O3 -o main main.c)后,即便把runtime设为1000000,运行时间依然极短(0.000000128秒),完全不随runtime增长。下面是-O3做的关键优化:
- 死代码消除:这是核心原因。外层
for (int k = 0; k < runtime; k++)的每一轮迭代,计算逻辑完全重复——所有输入数据在循环开始前就已固定,每次算出的view_sim结果也完全相同,而且程序最终根本没用到view_sim的任何值(只是不断被覆盖)。-O3级别的编译器会直接判定这个外层循环是无意义的重复计算,直接删掉整个循环,自然不会随runtime增加耗时。 - 常量传播与冗余计算消除:编译器会识别出
db_feat_norm[j] * features_norm是固定常量(两个值都是循环前计算好的),不会在每轮内层循环里重复计算这个乘积,减少了冗余操作。 - 循环向量化:对于内层计算点积的循环,-O3会自动启用SIMD指令(如AVX、SSE),一次性批量计算多个元素的乘积和,大幅加快单次计算的速度,但这不是耗时不随
runtime变化的核心原因。 - 无用变量优化:因为
view_sim数组的结果既没被输出,也没被后续逻辑使用,编译器甚至可能直接跳过整个view_sim的计算逻辑,只保留计时和打印代码,导致不管runtime多大,实际执行的有效代码都极少。
内容的提问来源于stack exchange,提问作者Chuck Liu
相关产品推荐
相关产品推荐

