除了 Cashgrind 之外,每个 Valgrind 模块的信息都很明确。正如我从 maunals 中了解的那样
使用中的模块
- 收集有关进入处理器数据和程序指令缓存的统计信息。
- 统计程序中分支预测模块的运行情况。
问题出现了
我知道需要处理器缓存,以便它以某种方式自动优化并通过在缓存中保存一些信息来更快地做一些事情。但!我们如何影响这一点?从 Cashgrind 是一个分析器这一事实来看,理论上它可以以某种方式完成,已经收到信息,但是如何?以及我们如何理解我们有可能通过我们的程序增加处理器缓存的负载(即告诉处理器在使用我们的程序或其他东西时更积极地使用缓存)
如果有关于分支模块操作的信息,以某种方式告诉我们这个模块工作得不好,那么我们该如何改变这种情况呢?
无论是在缓存的情况下,还是在分支预测块的情况下,都需要了解这些模块的典型算法和结构。对于我自己的细节,我建议转向经典,Tanenbaum - “计算机架构”。但问题不在于他们的设备,所以在这里我将把自己限制在简单的例子中,你可以看到他们的动作。
关于缓存
缓存对实时执行有多种影响。在这方面的程序优化几乎总是归结为使用那些与之匹配的算法。这里最重要的参数是数据的局部性,但还有许多其他与缓存设备相关的效果会产生类似的问题。
一个不适合缓存的算法的简单示例是按列遍历矩阵:
如果只是简单地交换外循环和内循环,那么执行时间将大大减少。(有时,编译器可以自己做到这一点。)
一般来说,需要随机访问大量数据的整个任务类与缓存的结合很差:这包括许多与使用树、图、哈希表等相关的算法;通常,为了纠正这种情况,您可以选择不同的算法或更改当前的算法,有时您可以通过重新分配内存中的数据来改善这种情况,例如,通过创建一个特殊的分配器;通常两者都需要额外的研究。在实践中,以前很少有人接触到这一点。
关于分支预测块。
这里的自由度要低得多,收益取决于架构。
使用静态预测,在编译时进行适当的代码准备会有所帮助。特别是(例如
gcc
):-fprofile-arcs
和-fbranch-probabilities
)。来自 'a 的 AFAIK 数据valgrind
还不能直接输入gcc
。if-then-else
块then
中通常被认为是最有可能的。但是在具有动态预测的架构上(例如,i686+ [是的,纠正我,也许是在 i586 上]),这些技巧的影响是微乎其微的,仅在相对很少调用但对时间要求极高的代码中使用它们是有意义的,例如,在中断处理程序中。对于通用代码,除了数据预处理之外,通常没有什么可以/没有任何意义。例如,由于预测块的原因,以下代码在 [几乎] 排序数组上的运行速度明显快于在随机数组上运行的速度:
关于
valgrind
'e的注意事项值得注意的是,它
valgrind
不依赖于向处理器请求任何统计信息,而只是模拟缓存和预测块的操作,因此其工作结果可能与在真实 CPU 上的执行结果有所不同,但在一定程度上可以肯定的是,它们可以用于评估和优化。