我的代码中有一个循环迭代1亿次(仿真模型的1亿次复制需要)。对于1亿次迭代中的每次迭代,我通过索引名为myarray
的整数变量从数组(age
)中检索值。由于数组的长度,仅对myarray[age]
的{{1}}编制索引是有效的。但是,age=0,...,99
的实际域名为age
。
所以,我有以下功能
0,...,inf
允许按int tidx(const int& a) {
return std::min(a,99);
}
编制索引。
我怎样才能更有效地做到这一点?
[PERF OUTPUT BELOW]
构建源文件的示例,该文件说明了我正在使用的编译器标志:
myarray[tidx(age)]
从Building file: ../SAR.cpp
Invoking: GCC C++ Compiler
g++ -O3 -Wall -c -fmessage-length=0 -Wno-sign-compare -fopenmp -MMD -MP -MF"SAR.d" -MT"SAR.d" -o"SAR.o" "../SAR.cpp"
Finished building: ../SAR.cpp
后跟perf record
:
perf report
来自Samples: 280 of event 'cycles', Event count (approx.): 179855989
24.78% pc2 libc-2.17.so [.] __GI_____strtod_l_internal
11.35% pc2 pc2 [.] samplePSA(int, double, int, NRRan&)
6.67% pc2 libc-2.17.so [.] str_to_mpn.isra.0
6.15% pc2 pc2 [.] simulate4_NEJMdisutilities(Policy&, bool)
5.68% pc2 pc2 [.] (anonymous namespace)::stateTransition(double const&, int const&, int&, double const&, bool const&, bool&, bo
5.25% pc2 pc2 [.] HistogramAges::add(double const&)
3.73% pc2 libstdc++.so.6.0.17 [.] std::istream::getline(char*, long, char)
3.02% pc2 libstdc++.so.6.0.17 [.] std::basic_istream<char, std::char_traits<char> >& std::operator>><char, std::char_traits<char> >(std::basic_
2.49% pc2 [kernel.kallsyms] [k] 0xffffffff81043e6a
2.29% pc2 libc-2.17.so [.] __strlen_sse2
2.00% pc2 libc-2.17.so [.] __mpn_lshift
1.72% pc2 libstdc++.so.6.0.17 [.] __cxxabiv1::__vmi_class_type_info::__do_dyncast(long, __cxxabiv1::__class_type_info::__sub_kind, __cxxabiv1::
1.71% pc2 libc-2.17.so [.] __memcpy_ssse3_back
1.67% pc2 libstdc++.so.6.0.17 [.] std::locale::~locale()
1.65% pc2 libc-2.17.so [.] __mpn_construct_double
1.38% pc2 libc-2.17.so [.] memchr
1.29% pc2 pc2 [.] (anonymous namespace)::readTransitionMatrix(double*, std::string)
1.27% pc2 libstdc++.so.6.0.17 [.] std::string::_M_mutate(unsigned long, unsigned long, unsigned long)
1.15% pc2 libc-2.17.so [.] round_and_return
1.02% pc2 libc-2.17.so [.] __mpn_mul
1.01% pc2 libstdc++.so.6.0.17 [.] std::istream::sentry::sentry(std::istream&, bool)
1.00% pc2 libc-2.17.so [.] __memcpy_sse2
0.85% pc2 libstdc++.so.6.0.17 [.] std::locale::locale(std::locale const&)
0.85% pc2 libstdc++.so.6.0.17 [.] std::string::_M_replace_safe(unsigned long, unsigned long, char const*, unsigned long)
0.83% pc2 libstdc++.so.6.0.17 [.] std::locale::locale()
0.73% pc2 libc-2.17.so [.] __mpn_mul_1
:
perf stat
我将不胜感激。我需要学习如何解释/阅读这些信息,所以任何可能有助于我开始的提示都将不胜感激。
答案 0 :(得分:9)
为了优化代码,首先要弄清楚哪个地方是瓶颈。要找到瓶颈,必须对代码进行分析。否则,很多时候只会浪费很多时间进行微观/虚假优化,而这些优化根本无关紧要。
我没有在您的最小工作代码示例(您没有提供)上使用过探查器,但根据我的经验,我可以告诉您 - 您的tidx()
函数不是瓶颈,您应该在这种情况下,不要关注std::min()
的表现。瓶颈很可能是内存访问和停滞的CPU周期。
对于初学者,如果可能的话,尝试展开你的循环(如果编译器没有为你做这个)。执行25000000次迭代而不是100000000次迭代可能更有效,但在单个循环周期中执行更多操作。但在你这样做之前,你必须确保展开循环有助于而不是伤害。这通常是通过分析来完成的,所以我们回到这一点,为了优化代码,必须先弄清楚哪个地方是瓶颈。找到瓶颈...哦,等等,我几乎陷入了不定期的循环。中止。
答案 1 :(得分:3)
很多人犯的第一个错误就是眼睛注意代码,回想某些东西,并想知道他们是否可以让它更快。
第二个错误是在其上运行gprof
,希望找到“瓶颈”。
唯一有用的东西gprof
可以找到的是你的代码是否受CPU限制,并且它是你编译的代码中的CPU绑定。
它不擅长发现可以消除的函数调用问题。
它不擅长发现你不知道自己在做什么的I / O问题。
答案 2 :(得分:2)
已经提供了有关分析的好建议。
min<T>(T x, T y)
应相当于return (x < y)?x:y;
作为汇编程序,将成为:
mov x, %eax
cmp y, %eax
cmovg y, %eax
或类似的东西。如果你在gcc中启用-O2,这三条指令肯定应该内联。
答案 3 :(得分:2)
要研究的几个想法
但实际上,它是一个极其简单的功能。通过查看其实现,没有多少可以获得。我的大部分建议涉及如何调用函数,调用函数的位置,调用函数的位置,以及 on 上的数据。这些可能是你需要看到的。
答案 4 :(得分:1)
请记住在启用优化的情况下分析可执行文件。对于性能测试,运行非优化的可执行文件是没用的,因为它将具有完全不同的性能特征。
然后考虑一下你可以做些什么来避免这么多的查找。减少工作量(算法改进)将花费更少的时间。
也可以编写没有“过早的pesimization”的代码,因为Herb Sutter喜欢称之为。
定义:过早的悲观化就是当你编写的代码比它需要的慢时,通常是通过要求不必要的额外工作,当等效的复杂代码更快并且应该自然地流出你的手指。
例如内联可能有帮助,也可能没有帮助,但您可能希望编写代码以免排除内联。稍后您可以强制或阻止内联并比较对您的执行环境更好的内容。您也可能不想使用像int这样的小类型的引用。如果没有优化,可能会使用指针实现引用参数,而指针现在通常比int更大更慢。即使在32位硬件上,引用也不会比int快。
int tidx(int a) {
return std::min(a,99);
}
然后你可以尝试其他一些优化技巧;独立任务并行运行?您的数据结构是否具有良好的参考特征局部性?如果要并行运行,您是否受到虚假共享的影响?你可以使用SIMD或其他数据并行化吗?您还可以使用编译器设置或在代码的特定部分中启用特定优化。这是测试性能变得非常重要的地方,因为不同的硬件可能具有截然不同的行为。此外,由于大多数此类优化都会使代码混淆,因此您不希望以低价或低价支付该价格。