c ++中std :: min(int)的效率

时间:2013-05-24 17:53:22

标签: c++ performance std

我的代码中有一个循环迭代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

我将不胜感激。我需要学习如何解释/阅读这些信息,所以任何可能有助于我开始的提示都将不胜感激。

5 个答案:

答案 0 :(得分:9)

为了优化代码,首先要弄清楚哪个地方是瓶颈。要找到瓶颈,必须对代码进行分析。否则,很多时候只会浪费很多时间进行微观/虚假优化,而这些优化根本无关紧要。

我没有在您的最小工作代码示例(您没有提供)上使用过探查器,但根据我的经验,我可以告诉您 - 您的tidx()函数不是瓶颈,您应该在这种情况下,不要关注std::min()的表现。瓶颈很可能是内存访问和停滞的CPU周期。

对于初学者,如果可能的话,尝试展开你的循环(如果编译器没有为你做这个)。执行25000000次迭代而不是100000000次迭代可能更有效,但在单个循环周期中执行更多操作。但在你这样做之前,你必须确保展开循环有助于而不是伤害。这通常是通过分析来完成的,所以我们回到这一点,为了优化代码,必须先弄清楚哪个地方是瓶颈。找到瓶颈...哦,等等,我几乎陷入了不定期的循环。中止。

答案 1 :(得分:3)

很多人犯的第一个错误就是眼睛注意代码,回想某些东西,并想知道他们是否可以让它更快。

第二个错误是在其上运行gprof,希望找到“瓶颈”。

唯一有用的东西gprof可以找到的是你的代码是否受CPU限制,并且它是你编译的代码中的CPU绑定。 它不擅长发现可以消除的函数调用问题。 它不擅长发现你不知道自己在做什么的I / O问题。

很多人使用this methodhere's why it works

答案 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)

要研究的几个想法

  • 检查生成的asssembly是否有该功能。它可能会或可能不会编译为次优
  • 调用该函数次数
  • 重写函数以使用SSE指令一次计算4(或8个AVX)值的最小值
  • 重新调整函数的调用方式。理想情况下,调用不应该远离代码从代码缓存中逐出。
  • 不要通过const引用传递int。有什么意义呢?只需传递一份int。
  • 检查是否有任何别名或错误共享可能会降低您在呼叫站点的速度。

但实际上,它是一个极其简单的功能。通过查看其实现,没有多少可以获得。我的大部分建议涉及如何调用函数,调用函数的位置,调用函数的位置,以及 on 上的数据。这些可能是你需要看到的。

答案 4 :(得分:1)

请记住在启用优化的情况下分析可执行文件。对于性能测试,运行非优化的可执行文件是没用的,因为它将具有完全不同的性能特征。

然后考虑一下你可以做些什么来避免这么多的查找。减少工作量(算法改进)将花费更少的时间。

也可以编写没有“过早的pesimization”的代码,因为Herb Sutter喜欢称之为。

  

定义:过早的悲观化就是当你编写的代码比它需要的慢时,通常是通过要求不必要的额外工作,当等效的复杂代码更快并且应该自然地流出你的手指。

例如内联可能有帮助,也可能没有帮助,但您可能希望编写代码以免排除内联。稍后您可以强制或阻止内联并比较对您的执行环境更好的内容。您也可能不想使用像int这样的小类型的引用。如果没有优化,可能会使用指针实现引用参数,而指针现在通常比int更大更慢。即使在32位硬件上,引用也不会比int快。

int tidx(int a) {
    return std::min(a,99);
}

然后你可以尝试其他一些优化技巧;独立任务并行运行?您的数据结构是否具有良好的参考特征局部性?如果要并行运行,您是否受到虚假共享的影响?你可以使用SIMD或其他数据并行化吗?您还可以使用编译器设置或在代码的特定部分中启用特定优化。这是测试性能变得非常重要的地方,因为不同的硬件可能具有截然不同的行为。此外,由于大多数此类优化都会使代码混淆,因此您不希望以低价或低价支付该价格。