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

C++ c+中std::min(int)的效率+;,c++,performance,std,C++,Performance,Std,我的代码中有一个循环,循环次数为1亿次(模拟模型的1亿次复制需要)。对于1亿次迭代中的每一次,我都通过对名为age的整数变量进行索引,从数组(myarray)中检索一个值。由于数组的长度,只能为age=0,…,99索引myarray[age]。然而,age的实际域是0,…,inf 所以,我有下面的函数 int tidx(const int& a) { return std::min(a,99); } 它允许通过myarray[tidx(age)]进行索引 如何才能更有效地执行此

我的代码中有一个循环,循环次数为1亿次(模拟模型的1亿次复制需要)。对于1亿次迭代中的每一次,我都通过对名为
age
的整数变量进行索引,从数组(
myarray
)中检索一个值。由于数组的长度,只能为
age=0,…,99
索引
myarray[age]
。然而,
age
的实际域是
0,…,inf

所以,我有下面的函数

int tidx(const int& a) {
    return std::min(a,99);
}
它允许通过
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
性能记录
,然后是
性能报告

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

如有任何意见,我将不胜感激。我需要学习如何解释/阅读这些信息,因此任何可能帮助我入门的提示都将不胜感激。

为了优化代码,必须首先找出瓶颈所在。要找到瓶颈,必须分析代码。否则的话,很多时间将被浪费在做根本不重要的微优化/错误优化上

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


对于初学者,如果可能的话,尝试展开循环(若编译器并没有为您这样做)。执行25000000次迭代可能比执行100000000次更有效,但在单个循环周期中执行更多。但是在你这样做之前,你必须确保展开循环是有帮助的,而不是有害的。这通常是通过分析来完成的,因此我们回到了这样一个问题:为了优化代码,必须首先找出瓶颈所在。要找到瓶颈。。。哦,等等,我差点进入不定式循环。正在中止。

关于分析,已经给出了很好的建议

min(tx,ty)
应等于
return(x

作为汇编器,这将成为:

mov    x, %eax
cmp    y, %eax
cmovg  y, %eax

或者类似的东西。如果您在gcc中启用-O2,这三条指令肯定应该内联

需要研究的几个想法

  • 检查为函数生成的装配。它可能会也可能不会编译成次优的东西
  • 调用该函数的次数减少
  • 重写该函数,以使用SSE指令一次计算4(或AVX为8)个值的最小值
  • 重新构造函数的调用方式。理想情况下,调用之间的距离不应太远,以至于代码从指令缓存中被逐出
  • 不要通过常量引用传递int。这有什么意义?只需传递一个int的副本
  • 检查是否存在任何别名或虚假共享可能会降低您在呼叫站点的速度

但实际上,这是一个非常简单的函数。仅仅看一下它的实现并没有什么好处。我的大多数建议涉及如何调用函数、从何处调用函数、何时调用函数以及调用函数的数据。这些可能是您需要查看的内容。

记住在启用优化的情况下评测可执行文件。对于性能测试来说,运行未优化的可执行文件是无用的,因为它将具有完全不同的性能特征

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

正如Herb Sutter喜欢说的那样,编写代码时不要“过早的伪化”

定义:过早悲观化是指当您编写的代码速度比需要的慢时,通常是要求进行不必要的额外工作,而相当复杂的代码会更快,并且应该自然地从您的手指中流出

例如,内联可能有帮助,也可能没有帮助,但您可能希望编写代码,以免妨碍内联。稍后,您可以强制或阻止内联,并比较哪些内联更适合您的执行环境。您可能也不想使用对int等小类型的引用。在没有优化的情况下,引用参数可能会使用指针来实现,现在指针通常比int更大、更慢。即使在32位硬件上,引用也不会比int更快

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

然后你可以尝试其他一些优化技术;独立任务是否并行运行?您的数据结构是否具有良好的引用特性局部性?如果事情并行运行,您是否受到虚假共享的影响?您可以使用SIMD或其他数据并行化吗?您还可以使用编译器设置或在代码的特定部分启用特定优化。这就是测试性能变得非常重要的地方,因为不同的硬件可以有完全不同的行为。而且,由于大多数这类优化都会混淆代码,因此您不想为很少或没有回报而付出代价。

许多人犯的第一个错误是盯着代码看,盯着某个东西看,然后想知道他们是否能更快地完成

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

gprof
能够可靠地找到的唯一有用的东西是,您的代码是否是CPU绑定的,以及您编译的代码是否是CPU绑定的。 它不善于发现可以解决的函数调用问题。 它不善于发现您不知道自己在做的I/O问题


很多人使用,而且。

我认为你不能<代码>标准::分钟
很可能是内联的,因此
int tidx(int a) {
    return std::min(a,99);
}