跳转至

perf 性能分析案例

1 分支预测

#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)可以显著提高程序的性能,因为分支预测错误会导致流水线中的指令流被打断,从而浪费了处理器的时钟周期。下面是一些可能的方法来降低分支预测错误:

  1. 代码结构优化: 重新组织代码以最小化分支的数量和嵌套,可以减少分支预测错误的机会。尽量使用循环展开、去除不必要的条件分支等来简化代码结构。
  2. 避免过多嵌套的条件分支: 深度嵌套的条件分支会增加分支预测错误的概率。尽量避免过多的嵌套,或者考虑使用其他方式来组织逻辑,比如查表法等。
  3. 分支方向预测: 分支方向预测是处理器硬件中的一项技术,它可以通过分析历史行为来预测分支的方向,从而减少分支预测错误。在编写代码时,可以尽量让分支的方向对预测器更容易预测,例如避免频繁改变分支的方向。
  4. 循环优化: 循环中的分支预测错误可能会对性能产生较大影响。循环展开、循环拆分、循环不变量代码外提等优化方法可以减少循环中的分支预测错误。
  5. 避免条件分支内的复杂计算: 在条件分支内执行复杂的计算可能会导致分支预测错误,因为处理器难以在预测分支方向时同时预测复杂计算的结果。将复杂计算移到条件分支外部可以降低分支预测错误。
  6. 使用可能性高的分支条件: 如果有多个条件分支,选择那些可能性更高的分支条件放在前面,这样处理器预测器更有可能预测正确。
  7. 使用 likely/unlikely 宏(针对某些编程语言): 一些编程语言(如 C/C++ 中的 GCC 编译器)提供了 likely 和 unlikely 宏,用于提示编译器和优化器分支的可能性,以帮助处理器更好地进行分支预测。
  8. 静态分析工具: 使用静态分析工具可以帮助你识别代码中潜在的分支预测错误,从而指导代码优化。

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;
}