如何在C/C++应用程序中分析内存访问所花费的时间?

14

一个应用程序中的函数耗费的总时间可以分为两个组成部分:

  1. 实际计算所花费的时间(Tcomp)
  2. 内存访问所花费的时间(Tmem)

通常,性能分析器会提供函数耗费的总时间估计。是否可能根据上述两个部分(Tcomp和Tmem)获得耗费时间的估计值?


回答这个问题有一个非常简单的方法。只需执行“随机暂停”,并查看内存管理中占样本的比例即可。以下是一个示例。一般来说,在最近毕业的程序员编写的大型软件中,花费在内存管理上的时间比例往往在50-99%之间。好消息是,加速因子2-100只需要进行重构。 - Mike Dunlavey
Mike,你好,这不是关于内存管理(malloc/free/new/delete)的问题,而是关于有效利用CPU的问题。随机暂停不能帮助我找到哪些代码受到内存延迟限制,哪些代码不受内存限制并以完整的ALU速度运行。我如何在屋顶线模型(https://en.wikipedia.org/wiki/Roofline_model - https://crd.lbl.gov/departments/computer-science/PAR/research/roofline/)中获得一些任务的指标:它使用30 GFLOPS和15 GBytes/s的内存层次结构请求,其中5 GBytes/s由主RAM提供;内存利用率为15%,ALU利用率为30%,使用AVX2。 - osgx
你可能想看一下 likwid - Henri Menke
4个回答

7

您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Imran
1
屋顶线模型已经完成了。我强烈推荐阅读名为“应用屋顶线模型的实际细节”的论文:http://spiral.ece.cmu.edu:8080/pub-spiral/pubfile/ispass-2013_177.pdf - Manuel Selva
Manuel,你能把论文中最有用的部分扩展并整合到你的回答中吗? - osgx

7
在他最近的博客文章CPU利用率是错误的中,Brendan Gregg建议使用每周期指令PMC。简而言之,如果IPC小于1.0,则可以认为该应用程序受到内存限制。否则,它可以被认为是指令限制。以下是他文章中相关的摘录:
如果您的IPC小于1.0,则可能会出现内存停滞,软件调优策略包括减少内存I/O,并改善CPU缓存和内存本地性,特别是在NUMA系统上。硬件调优包括使用具有更大CPU缓存和更快内存、总线和互连的处理器。
如果您的IPC大于1.0,则可能会受到指令限制。寻找减少代码执行的方法:消除不必要的工作、缓存操作等。CPU火焰图是进行此类调查的好工具。对于硬件调优,尝试更快的时钟速率和更多的核心/超线程。
对于我上面的规则,我以IPC为1.0分割。我从哪里得到的呢?基于我之前使用PMC的工作经验,我自己想出来的。以下是如何获得适合您的系统和运行时的自定义值的方法:编写两个虚拟负载,一个是CPU绑定的,一个是内存绑定的。测量它们的IPC,然后计算它们的中间点。
以下是由压力测试工具生成的一些虚拟工作负载及其IPC示例。
内存受限测试,IPC较低(0.02):
$ perf stat stress --vm 4 -t 3
stress: info: [4520] dispatching hogs: 0 cpu, 0 io, 4 vm, 0 hdd
stress: info: [4520] successful run completed in 3s

 Performance counter stats for 'stress --vm 4 -t 3':

      10767,074968      task-clock:u (msec)       #    3,560 CPUs utilized          
                 0      context-switches:u        #    0,000 K/sec                  
                 0      cpu-migrations:u          #    0,000 K/sec                  
         4 555 919      page-faults:u             #    0,423 M/sec                  
     4 290 929 426      cycles:u                  #    0,399 GHz                    
        67 779 143      instructions:u            #    0,02  insn per cycle         
        18 074 114      branches:u                #    1,679 M/sec                  
             5 398      branch-misses:u           #    0,03% of all branches        

       3,024851934 seconds time elapsed

CPU绑定测试,IPC很高(1.44):

$ perf stat stress --cpu 4 -t 3
stress: info: [4465] dispatching hogs: 4 cpu, 0 io, 0 vm, 0 hdd
stress: info: [4465] successful run completed in 3s

 Performance counter stats for 'stress --cpu 4 -t 3':

      11419,683671      task-clock:u (msec)       #    3,805 CPUs utilized          
                 0      context-switches:u        #    0,000 K/sec                  
                 0      cpu-migrations:u          #    0,000 K/sec                  
               108      page-faults:u             #    0,009 K/sec                  
    30 562 187 954      cycles:u                  #    2,676 GHz                    
    43 995 290 836      instructions:u            #    1,44  insn per cycle         
    13 043 425 872      branches:u                # 1142,188 M/sec                  
        26 312 747      branch-misses:u           #    0,20% of all branches        

       3,001218526 seconds time elapsed

谢谢!你能添加一两个命令来在 Linux 中获得 IPC 吗(可能使用 perf stat 或者 vtune)?有没有某些命令的变体可以定期打印 IPC,和/或系统范围内的 IPC 平均值(在 perf 的 man 手册中找不到周期间隔以便每 1 或 5 秒打印它,像 vmstatiostatsar -n DEV 做的那样 https://medium.com/netflix-techblog/linux-performance-analysis-in-60-000-milliseconds-accc10403c55);tiptop 是什么?有任何关于受内存绑定/延迟绑定程序及其 IPC 的实际示例吗? - osgx
为什么IPC为1是内存/ CPU限制的边界?我认为,在性能中,IPC是按x86指令计算的,但我们中的许多人知道,在时钟周期中要解码3或4个指令组,第一个指令可能会生成许多微操作(uops),而其他指令最多只有1个;CPU的宽执行引擎可以开始执行多达6或8个uops... 嗯,定期打印是pmcarch-https://github.com/brendangregg/pmc-cloud-tools/blob/master/pmcarch(仅适用于Intel上的Linux); tiptop是http://tiptop.gforge.inria.fr - osgx
1
我认为大矩阵乘法会受到内存限制,因为需要从内存读取大量数据。计算π将是CPU限制的,因为不需要从内存读取太多数据。经过测量两个虚拟工作负载后,选择了1的IPC。这可能因测量而异,对于您的系统可以是另一个值。 - ks1322
1
ks1322,DGEMM - 矩阵乘法是BLAS3,需要低内存带宽:对于更大的矩阵大小,有O(N ^ 2)内存访问和O(N ^ 3)CPU操作。 BLAS3在https://crd.lbl.gov/departments/computer-science/PAR/research/roofline/上被标记为算术密度尺度中的稠密线性代数组~每个内存字节的十几个fpu操作; 还请查看http://spiral.ece.cmu.edu:8080/pub-spiral/pubfile/ispass-2013_177.pdf - osgx

6

由于计算在当前处理器架构中与内存访问重叠,所以无法测量这个值(并且做这个也没有意义)。此外,访问内存通常会分解为更多的步骤(访问内存、预取到各种高速缓存级别、实际读取到处理器寄存器)。

您可以使用perf及其硬件计数器(如果您的硬件支持)测量不同高速缓存级别上的高速缓存命中和缺失,以估算您的算法在您的硬件上的效率。


当你正在优化应用程序在所运行的架构上的性能时,它可能看起来没有意义。你是对的,因为缓存未命中/命中是有帮助的,很多工具都提供了这些信息。但是,在我看来,当你需要应用程序的架构无关性能分析时就有意义了。当你在评估应用程序在新兴架构上的性能时,这将非常有用。这样,你可以分别量化改善计算和内存访问的效果。换句话说,它将指出在新架构中改进的重点应该是什么。 - Imran
2
为简单起见,我可以将内存访问时间视为未花费在实际计算中的时间。 - Imran

-1
如果您正在寻找一个获取CPU周期的函数,那么Boost会非常有帮助。我已经使用Boost计时器工具来计算系统调用的CPU周期。
另一方面,您可以将相同的函数放在完整程序中以获取总时间。
我希望这正是您所需要的。-Vijay

Vijay,我不是在寻找以秒为单位计时某些函数(无论是在C还是C++应用程序中),而是要找到查找算术强度的方法-与内存请求指令数量相比完成了多少ALU指令。有些任务受到内存带宽或内存延迟的限制,而其他任务则受到ALU的限制。单一类型的测量(使用Boost Timer进行实时测量)将无法帮助。应该使用硬件性能计数器(PMC),问题是在Intel / AMD CPU上使用哪个PMC。 - osgx
@osgx:我在原帖问题下发表了评论。 - Mike Dunlavey
Mike,你没有评论OP的问题(它是关于内存访问时间,而不是关于内存管理的时间共享),你一直在告诉其他人(其中有400万人)该做什么/学习什么。为什么你没有获得https://stackoverflow.com/help/badges/262/publicist徽章,因为你总是链接到自己的唯一真正解决方案,用于任何在描述中带有性能单词的问题的随机堆栈采样? - osgx

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接