2行关键代码的c ++优化

时间:2016-06-17 12:13:29

标签: c++ optimization

使用valgrind和perf / FlameGraphs,我发现我的部分应用程序消耗了几乎100%的CPU:

for(size_t i = 0; i < objects.size(); i++) {

  //this part consumes 11% CPU -----> 
  collions_count = database->get_collisions(collisions_block, objects[i].getKey());
  feature1 = objects[i].feature1;
  //<--------

  for(int j = 0; j < collions_count * 2; j += 2) {

    hash = 
      ((collisions_block[j] & config::MASK_1) << config::SHIFT) | 
      ((collisions_block[j+1] - feature1) & config::MASK_2);

    if (++offsets[hash] >= config::THRESHOLD_1) {

      //... this part consumes < 1% of CPU

    }
  }
}

哈希和后续if语句的计算占用了所有应用程序的近90%的CPU。

  • collisions_block初始化一次,类型为int[100000]
  • config::是包含全局配置变量的名称空间
  • offsets初始化一次,类型为uint8_t[1<<24]
  • 我正在运行Centos7 Linux 3.10.0-327.13.1.el7.x86_64
  • 所有CPU用于usr mpstat输出中没有iowait
  • 我正在使用g ++(GCC)4.8.5 20150623(Red Hat 4.8.5-4)和标记-std=gnu++11 -Ofast -Wall
  • 进行编译

有没有办法加速内循环?

1 个答案:

答案 0 :(得分:1)

我发现性能瓶颈是对数组++offsets[hash]的无序访问。耗费了大部分CPU时间(75 +%)。通过将数组的大小从1<<24减少到1<<21并尝试使用适当的MASKS配置,我实现了2.5倍的速度提升。

我将简要描述我如何识别问题

for(size_t i = 0; i < objects.size(); i++) {

  //this part consumes 11% CPU -----> 
  collions_count = database->get_collisions(collisions_block, objects[i].getKey());
  feature1 = objects[i].feature1;
  //<--------

  for(int j = 0; j < collions_count * 2; j += 2) {

    hash = calculate_hash(collisions_block[j], 
      collisions_block[j+1],
      feature1,
      config::MASK_1,
      config::MASK_2
      config::SHIFT);

    if (check_condition(hash, config::THRESHOLD_1)) {

       //... this part consumes < 1% of CPU

    }
  }
}
  1. 将关键的2行拆分为单独的函数以便更好地进行性能分析(小心放置__attribute__((noinline))以防止gcc内联新函数。如果内联,它们将不会出现在调用堆栈中)
  2. 使用-g -rdynamic gcc flags
  3. 编译代码
  4. 运行抽样pefrormance工具perf record -p <pid> -F 200 -g --call-graph dwarf -- sleep 60
  5. 转换为FlameGraph以提高可读性perf script | ./stackcollapse-perf.pl > out.perf-folded && ./flamegraph.pl out.perf-folded > graph.svg
  6. 从火焰图识别最昂贵的功能并对其进行优化