C++性能分析

📅 2026/7/23 17:41:45
C++性能分析
目录一性能对比二性能分析工具1VS性能分析工具2汇编分析工具三计算机性能1存储访问2处理器性能加速3系统调用开销4时钟函数四编译器优化1总体特点2指令重排3防优化技巧五常见代码的性能1二维数组的访问2大批量内存拷贝3多分支语句的顺序4循环嵌套条件语句5虚函数6volatile六经验总结1异常性能2性能优化手段总结3增加Cache命中率4并行处理一性能对比我们经常对比2份代码的性能首先要注意控制这几点12份代码的功能完全相同2使用相同的测试环境windows还是linux编译器等3使用相同的性能测试代码4使用相同的编译优化级别VS开release模式对于简单的情况编译器很可能已经做了大量的优化使得对比结果并不明显。但是这却并不代表我们写代码可以完全依赖编译器。代码的两种写法在不同程度的编译优化下哪种写法更快可能没有定论。例如下面的“循环嵌套条件语句”clion上运行的是1770 1501visual studio上运行的是849 1228感觉应该是vs做的编译优化比较多简单的if语句可能被优化掉了。我在windows机器上写C代码用cmake编译运行用clock函数计时用来判断程序运行时间。性能测试代码auto s1 clock(); test(); auto e1 clock(); cout endl e1 - s1;二性能分析工具1VS性能分析工具点击 分析、性能探测器、更改目标点击 可执行文件、开始、CPU采用、下一步、可执行文件、下一步填写路径完成。2汇编分析工具在线汇编左边可以用不同语言写代码右边可以选择不同的环境和编译器版本还支持输入编译选项。三计算机性能1存储访问连续的不跳跃的存储访问是最快的这对程序性能影响很大。2处理器性能加速处理器性能加速指令乱序执行、流水线、并发条件分支代码可能打乱流水线造成性能下降。3系统调用开销read write open close mmap 耗时比较长4时钟函数Linux中的时钟函数Windows中的时钟函数四编译器优化1总体特点2指令重排好处是可以提高指令并行度。int x,y,a; int main() { xa; y2; return 0; }然而从gcc8开始这个代码在O2下是不进行指令重排的不知道为啥。3防优化技巧五常见代码的性能1二维数组的访问二维数组的访问最好不要跳内存。#include stdio.h #include time.h #define N 1000000 #define M 1000 typedef struct { int a[N]; }Node; #define OUTCLOCK \ printf(%d ,clock()-theClock); \ theClockclock(); int main() { clock_t theClockclock(); Node *p(Node *)malloc(sizeof(Node)*M); OUTCLOCK for(int i0;iM;i)for(int j0;jN;j)p[i].a[j]i*j1; OUTCLOCK for(int j0;jN;j)for(int i0;iM;i)p[i].a[j]i*j1; OUTCLOCK return 0; }运行结果0 2339 2234单位是毫秒2大批量内存拷贝大批量内存拷贝用memcpy代替赋值语句int main() { clock_t theClockclock(); Node *p(Node *)malloc(sizeof(Node)*M); int *p2(int *)malloc(sizeof(int)*N*M); OUTCLOCK for(int i0;iM;i)for(int j0;jN;j)p2[i*Nj]p[i].a[j]; OUTCLOCK memcpy(p2,p, sizeof(int)*N*M); OUTCLOCK return 0; }运行结果0 2811 2763多分支语句的顺序形如如下的代码if(con1)do1; else if(con2)do2; else if(con3)do3; else do4;假设do语句里面没有continue、break、goto、return语句那么这段代码的执行时间分为con判断时间、do语句时间两部分。其中无论这些分支如何调整顺序都不影响do语句时间所以只需要考虑con判断时间。假设各个分支的命中概率分别为p1 p2 p3 p4判断时间单个con表达式的执行时间分别为t1 t2 t3 t4则con判断时间Tp1t1 p2(t1t2) p3(t1t2t3) p4(t1t2t3t4)显然当p1/t1 p2/t2 p3/t3 p4/t4时T取到最小值。也就是说命中率高的分支往前放单个con表达式执行时间较长的往后放这种比如con表达式包含了执行一个函数4循环嵌套条件语句如果循环里面有if语句无论是对程序员还是对cpu来说都是一个复杂的行为。#include stdio.h #include time.h #define N 1000000 #define M 1000 int x[M],y[M]; #define OUTCLOCK \ printf(%d ,clock()-theClock); \ theClockclock(); int main() { for(int i0;iM;i)x[i]i*i,y[i]i*i*ii*31; int d,s0; scanf(%d,d); clock_t theClockclock(); for(int i0;iN;i)for(int i0;iM;i)if(x[i]d)sy[i]; OUTCLOCK for(int i0;iN;i)for(int i0;iM;i)s((x[i]d)?y[i]:0); OUTCLOCK return 0; }运行结果2500001770 1501可此可见让条件只控制数据不控制指令跳转对于CPU来说是很友好的。5虚函数虚函数比较慢的原因有两个一是需要通过函数指针来调用二是通常会防止内联。所以CRTP模式会比继承模式要快。6volatile普通代码#includeiostream using namespace std; int main() { char ch[80]; for(int i0;i80;i)ch[i]0; //核心代码 for(int i0;i80;i)coutch[i]; return 0; }其中赋值的那一行是核心代码。汇编汇编结果是5次操作每次操作16个字节。加了volatile的代码#includeiostream using namespace std; int main() { volatile char ch[80]; for(int i0;i80;i)ch[i]0; //核心代码 for(int i0;i80;i)coutch[i]; return 0; }汇编加了volatile的代码汇编结果就是80次的循环了。所以 volatile是对性能有负面影响的。六经验总结1异常性能功能相同代码差不多但性能差异明显有可能是算法写错了。实例杀手数独错误代码中漏了规则即少了DFS的剪枝条件或许即使少了条件也有唯一答案但搜索效率一定会降低。2性能优化手段总结来自吴咏炜老师3增加Cache命中率1增加Cache块大小2增加Cache容量3增加Cache相联度4way预测5编译优化4并行处理1指令级并行流水线、超标量、超长指令字、乱序执行2向量数据并行向量架构、多媒体SIMD指令集拓展、脉动阵列、gpu3线程级并行多处理机多核多线程