CPU指令与持续时间

CPU-Instructions vs. Duration

本文关键字:持续时间 指令 CPU      更新时间:2023-10-16

我有两个用C++编写的用于解决数学问题的不同程序(程序AB

CCD_ 3执行循环(就持续时间而言)比CCD_ 4好大约10倍。现在,我通过valgrind工具callgrind统计了执行的CPU指令,并意识到程序A只需要程序B执行的指令的1/3。我本以为这个系数在1/10左右。

我知道有些CPU指令占用更多的CPU周期(比如内存访问),但从设计上讲,A应该比B包含更多这样昂贵的指令。此外,我不知道callgrind是如何计算这些说明的(在文档中找不到任何关于这方面的信息)
有人能对这种行为给出一个合理的解释吗?TIA-

编辑:(由于评论)
不幸的是,代码是全面的,张贴在这里。。。两个程序都在同一台机器上执行。两者都是完全并行的(每个线程运行一个独立的程序副本,只需要告诉其他线程他什么时候找到了解决方案)。但是指令的计数是在一个线程上完成的,因为callgrind无论如何都在对程序进行排序。如前所述,A需要比B多得多的存储器
我不希望得到一个正确的答案,只要给我一些可能导致这个问题的提示就好了。

您也没有指定运行它的平台,每个CPU都有自己的一组注意事项。

例如,在x86上,指令数量和总执行时间之间的相关性很小,如:

  1. x86首先将指令转换为内部微操作码(uop),然后运行这些代码,因此它基本上执行的代码与可执行文件中人类可见的机器代码完全不同。它还对uop进行了重新排序,所以即使您模拟翻译,也无法确定它们的执行顺序(除非您模拟CPU、缓存和内存的整个体系结构)
  2. 多个uop可能在一个时钟周期内执行,或者换句话说,单个uop可能会在几个时钟周期内阻塞CPU,因此任何两条x86指令的执行时间都可能相差100倍以上(即使是两次运行中的同一条指令,如果因缓存未命中而暂停,其执行时间也可能相差很大(约100倍))
  3. 内存访问。内存比CPU慢得多,因此以可预测的模式顺序读取内存并使数据尽可能紧凑(以充分重用缓存)与代码速度比指令数量(算法)更相关。在某些极端情况下,设计良好的数据结构甚至可以击败更好的算法,比如对于约100k个项目的std::vector<int>,使用随机插入/删除比列表快得多,尽管向量插入/删除是O(n^2),而列表只有O(n)。CPU手中唯一可访问的内存是L0缓存,L1就像在街上,L2就像去其他城市旅行,L3不同的国家。记忆本身就像在月球上一样
  4. 其他I/O访问。。若内存很慢,那个么访问光盘就像去太阳(SSD),或者超越太阳系(HDD)

正如你所看到的,在极端情况下,x86 CPU甚至可以执行具有约200条指令的代码,处理30倍多的内存,速度与具有约10条指令的不同例程一样快。在角落的情况下,差异可能非常极端,人类很难通过阅读来源来想象。这就是为什么在x86上,证明代码优化的唯一有效方法是用足够接近真实数据的数据来评测代码。仅仅通过理论、big-o符号和"直觉"进行"优化"很容易适得其反,这在10-20年前是有效的,即使在那时,我们也用工具对结果进行了分析,以验证收益。

当然,大量的指令本身更容易从缓存中掉下来,使指令读取停滞,但如果你能创建更好的数据结构,那么即使是30kiB与1kiB的代码也可以被证明是合理的(尽管在被操作系统中断时有很多缓存未命中的风险)。

callgrind web表示:"缓存模拟和/或分支预测(类似于Cachegrind)可以生成有关应用程序运行时行为的进一步信息。"。