gpt4 book ai didi

performance - 与C/C++最相关的性能指标

转载 作者:行者123 更新时间:2023-12-03 16:08:58 25 4
gpt4 key购买 nike

我正在寻找相关的性能指标来基准测试和优化我的C / C ++代码。例如,虚拟内存使用率是一个简单但有效的指标,但我知道有些虚拟内存使用情况更为专业,可以帮助优化特定域:缓存命中/未命中,上下文切换等。

我相信这里是一个列出性能指标,衡量指标以及如何衡量指标的好地方,以帮助希望开始优化程序的人员知道从何开始。

最佳答案

时间是最相关的指标。

这就是为什么大多数分析器默认使用测量/采样时间或核心时钟周期的原因。了解代码在何处花费时间是寻找加速的重要第一步。首先找出缓慢的原因,然后找出为什么缓慢。

您可以找到2种根本上不同的加速方式,时间会帮助您找到它们。


算法上的改进:首先找到减少工作量的方法。这通常是最重要的一种,而Mike Dunlavey的回答集中于此。您绝对不应忽略这一点。缓存重新计算速度很慢的结果非常值得,特别是如果速度足够慢以至于从DRAM加载仍然更快。

在这两种加速之间,使用可以更有效地解决实际CPU问题的数据结构/算法。 (例如,实际上链表通常比数组慢,因为指针追赶延迟是一个瓶颈,除非您最终过于频繁地复制大型数组...)
更有效率地使用蛮力以更少的周期完成相同的工作。 (和/或对程序的其余部分更友好,具有较小的缓存占用空间和/或较少的分支,占用了分支预测变量中的空间,等等。)

通常需要更改数据布局以使其更易于缓存,和/或使用SIMD手动矢量化。或者以更聪明的方式这样做。或编写一个处理普通特殊情况的函数要比普通情况下的函数快。甚至牵手编译器为您的C源代码制作更好的asm。

考虑对现代x86-64上的float数组求和:从延迟绑定的标量添加到具有多个累加器的AVX SIMD,可以使您加速8(每个向量元素)* 8(Skylake的延迟/吞吐量)= 64x对于中等大小的阵列(仍在单个内核/线程上),在理论上最好的情况下,您不会遇到另一个瓶颈(例如,如果您的数据在L1d缓存中不热,则为内存带宽)。 Skylake vaddps / vaddss具有4个周期延迟,并且每2时钟= 0.5c互惠吞吐量。 (https://agner.org/optimize/)。 Why does mulss take only 3 cycles on Haswell, different from Agner's instruction tables?有关隐藏多个FP延迟的多个累加器的更多信息。但这与将总计存储在某个地方相比仍然很困难,甚至可能在您更改元素时使用增量更新总计。 (但是,与整数不同,FP舍入误差会以这种方式累积。)




如果您没有看到明显的算法改进,或者想在进行更改之前了解更多信息,请检查CPU是否停滞在任何事物上,或者是否在效率上仔细考虑了编译器所做的所有工作。

每时钟指令数(IPC)告诉您CPU是否接近其最大指令吞吐量。 (或更准确地说,x86上每个时钟发出的融合域uops,因为例如一个rep movsb指令是一个很大的存储器,并解码为许多uops。cmp/ jcc从2条指令融合到1 uop,从而增加了IPC,但是管道宽度仍然是固定的。)

每条指令完成的工作也是一个因素,但是您不能使用探查器来衡量:如果您有专门知识,请查看编译器生成的asm,以查看用更少的指令进行相同的工作是否可行。如果编译器没有自动矢量化或效率低下,则根据问题,通过使用SIMD内部函数手动矢量化,您可能会为每条指令完成更多工作。或通过调整C源代码以使汇编语言自然地进行处理,从而使编译器能够发出更好的汇编语言。例如What is the efficient way to count set bits at a position or lower?。并参见C++ code for testing the Collatz conjecture faster than hand-written assembly - why?



如果发现IPC较低,请通过考虑诸如高速缓存未命中或分支未命中或较长的依赖链之类的可能性(通常是由于前端或内存没有瓶颈而导致IPC较低的原因)来找出原因。

或者,您可能会发现它已经接近最佳地应用CPU的可用蛮力了(不太可能,但是对于某些问题可能)。在那种情况下,您唯一的希望是改进算法以减少工作量。

(CPU频率不是固定的,但是核心时钟周期是一个很好的代理。如果您的程序没有花时间等待I / O,则测量核心时钟周期可能更有用。)

多线程程序的大部分串行部分可能很难检测到;当其他线程被阻塞时,大多数工具没有一种使用循环来查找线程的简便方法。



但是,花费在函数上的时间并不是唯一的指标。一个函数可以通过触摸大量内存来使程序的其余部分变慢,从而导致从缓存中逐出其他有用数据。因此,这种效果是可能的。或者某个地方有很多分支可能会占用CPU的某些分支预测能力,从而导致其他地方出现更多分支未命中的情况。



但是请注意,在大型代码库中,包含热点的函数可以有多个调用者,仅查找CPU在哪里花费大量时间执行并不是最有用的。例如在memcpy中花费大量时间并不意味着您需要加快memcpy的速度,这意味着您需要找到哪个调用方经常调用memcpy。依此类推,备份呼叫树。

使用可以记录堆栈快照的探查器,或者仅在调试器中按Control-C并几次查看调用堆栈。如果某个函数通常出现在调用堆栈中,则说明正在进行昂贵的调用。

相关:linux perf: how to interpret and find hotspots,尤其是Mike Dunlavey在这里的回答说明了这一点。



从根本上避免进行工作的算法改进通常比更有效地完成相同工作更有价值。

但是,如果您发现某些工作的IPC值很低,您还没有想出如何避免的方法,那么一定要看一下如何重新安排数据结构以更好地进行缓存,或者避免分支预测错误。

或者,如果高IPC仍需要很长时间,则手动向量化循环可能会有所帮助,每条指令执行4倍或更多的工作。

关于performance - 与C/C++最相关的性能指标,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55650592/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com