perf 性能分析案例¶
1 分支预测¶
- 分支对代码性能的影响和优化 - 知乎
- How branches influence the performance of your code and what can you do about it? - Johnny's Software Lab
- 数组排序后比排序前执行更快
- 测试代码
#include <algorithm>
#include <ctime>
#include <iostream>
int main() {
// Generate data
const unsigned arraySize = 32768;
int data[arraySize];
for (unsigned c = 0; c < arraySize; ++c)
data[c] = std::rand() % 256;
// !!! With this, the next loop runs faster.
// std::sort(data, data + arraySize);
// Test
clock_t start = clock();
long long sum = 0;
for (unsigned i = 0; i < 100000; ++i) {
for (unsigned c = 0; c < arraySize; ++c) { // Primary loop.
if (data[c] >= 128)
sum += data[c];
}
}
double elapsedTime = static_cast<double>(clock() - start) / CLOCKS_PER_SEC;
std::cout << elapsedTime << '\n';
std::cout << "sum = " << sum << '\n';
}
1.1 如何降低分支预测错误¶
降低分支预测错误(branch-misses
)可以显著提高程序的性能,因为分支预测错误会导致流水线中的指令流被打断,从而浪费了处理器的时钟周期。下面是一些可能的方法来降低分支预测错误:
- 代码结构优化: 重新组织代码以最小化分支的数量和嵌套,可以减少分支预测错误的机会。尽量使用循环展开、去除不必要的条件分支等来简化代码结构。
- 避免过多嵌套的条件分支: 深度嵌套的条件分支会增加分支预测错误的概率。尽量避免过多的嵌套,或者考虑使用其他方式来组织逻辑,比如查表法等。
- 分支方向预测: 分支方向预测是处理器硬件中的一项技术,它可以通过分析历史行为来预测分支的方向,从而减少分支预测错误。在编写代码时,可以尽量让分支的方向对预测器更容易预测,例如避免频繁改变分支的方向。
- 循环优化: 循环中的分支预测错误可能会对性能产生较大影响。循环展开、循环拆分、循环不变量代码外提等优化方法可以减少循环中的分支预测错误。
- 避免条件分支内的复杂计算: 在条件分支内执行复杂的计算可能会导致分支预测错误,因为处理器难以在预测分支方向时同时预测复杂计算的结果。将复杂计算移到条件分支外部可以降低分支预测错误。
- 使用可能性高的分支条件: 如果有多个条件分支,选择那些可能性更高的分支条件放在前面,这样处理器预测器更有可能预测正确。
- 使用 likely/unlikely 宏(针对某些编程语言): 一些编程语言(如 C/C++ 中的 GCC 编译器)提供了 likely 和 unlikely 宏,用于提示编译器和优化器分支的可能性,以帮助处理器更好地进行分支预测。
- 静态分析工具: 使用静态分析工具可以帮助你识别代码中潜在的分支预测错误,从而指导代码优化。
2 缓存¶
- 测试代码
#include <cstdint>
#include <cstdio>
#include <string.h>
uint32_t g_num = 0;
void sum_array(uint32_t *a, uint32_t *b, uint32_t num) {
for (uint32_t i = 0; i < num; ++i) {
g_num = g_num + a[i] + b[i];
}
}
int main() {
constexpr uint32_t num = 1024;
uint32_t a[1024], b[1024];
memset(a, 1, num);
memset(b, 1, num);
sum_array(a, b, num);
printf("sum= [%u]",g_num);
return 0;
}