【发布时间】:2016-06-24 21:38:45
【问题描述】:
通过 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
}
}
}
hash 的计算和后面的 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 都用于
usrmpstat 输出中没有iowait - 我正在使用 g++ (GCC) 4.8.5 20150623 (Red Hat 4.8.5-4) 和标志
-std=gnu++11 -Ofast -Wall进行编译
有什么办法可以加快内循环?
【问题讨论】:
-
另外,
offsets是一个用hash访问的巨大数组,我认为它根本没有顺序值。这意味着缓存可能不适用于该数组,这会使循环变慢。虽然不确定解决方法(也许使用稀疏数据结构,例如“默认地图”?) -
@jdehesa 由于哈希的特性,我认为不可能有解决方法......
-
我看到有两件事情可以尝试,但它们的作用不大。 1) 计算“const int n_collisions = collions_count * 2;”在循环之前,将循环更改为“for(int j = 0; j config::THRESHOLD_1 不是
const,则在循环之前制作const的副本。编译器可能可以在这里进行更多优化。其他两个configs 同上。我怀疑这其中的任何一项都算不了什么,但值得一试。 -
预计算
collions_count * 2在 -O1 之后不会有任何改进。除非您更改循环内的值,否则编译器应自行预先计算。尝试使用-flto编译和链接,有时它可以启用 大量 优化。这取决于程序,但通常我发现-ftlo比-O3更重要。 -
尝试不同的优化选项。有时
-Os可能比-O[123]快,有时-O2比-O3和-Ofast快——这完全取决于要优化的代码——有时对不同的文件使用不同的优化级别是有意义的。也可以试试 LTO。
标签: c++ optimization