0

如果 L2 没有被填满,CPU 的运行速度似乎要快得多。程序员是否会更好地编写最终会更小的二进制代码,即使部分代码并非一直执行?比如说,只在配置文件中打开的部分代码。

4

1 回答 1

4

真相有点复杂,我会试着为你概述一下。

如果您查看具有多核处理器的现代 PC 中的内存层次结构,您会发现有六个级别:

  1. 预取器,每个内核一个(无延迟)
  2. 每个核心的 L1 缓存,一个或两个(组合或代码和数据,AMD K10 上 2*64K)(延迟说三个 clks)
  3. L2 缓存,每个内核一个(AMD K10 上为 512K)(延迟为 10)
  4. L3 缓存,每个处理器使用一个(AMD K10 上的 ncores*1 MB),所有内核使用(延迟说 30)
  5. 系统 RAM,每个系统一个由所有处理器使用(延迟说 100)
  6. 同步(或总线锁定),所有总线主控设备使用的每个系统的一种方法(如果旧的 PCI 卡正在使用时钟频率为 33 MHz 的总线主控时所有 32 个可用时钟,则延迟至少 300 个周期,最多 1 us - 在3 GHz 处理器,这意味着 3000 个时钟周期)

不要将循环计数视为准确,它们旨在让您了解执行代码时可能产生的惩罚。

我将同步用作内存级别,因为有时您也需要同步内存并且这会花费时间。

您使用的语言将对性能产生很大影响。用 C、C++ 或 ForTran 编写的程序比 Basic、C# 和 Java 等解释程序更小,执行速度更快。在组织数据区域和程序访问它们时,C 和 Fortran 还将为您提供更好的控制。OO 语言(C++、C# 和 Java)中的某些功能,例如标准类的封装和使用,将导致生成更大的代码。

代码的编写方式对性能也有很大的影响——尽管一些不知情的人会说现在编译器非常好,没有必要编写好的源代码。出色的代码将意味着出色的性能,而 Garbage In 将始终导致 Garbage Out。

在你的问题的上下文中,写小通常比不关心更好。如果您习惯于高效编码(小/快代码),那么无论您是编写很少使用的序列还是经常使用的序列,您都会这样做。

缓存很可能不会加载您的整个程序(尽管它可能会),而是从代码中的 32 或 64 字节地址获取的大量 32 或 64 字节块(“缓存线”)数据。访问其中一个块中的信息越多,它将保留它所在的高速缓存行的时间越长。如果核心想要一个不在 L1 中的块,它会在必要时一直搜索到 RAM 并产生惩罚时钟在做的时候循环。

所以一般来说,小的、紧凑的和内联的代码序列会执行得更快,因为它们对缓存的影响较小。对其他代码区域进行大量调用的代码将对缓存产生更大的影响,具有未优化跳转的代码也是如此。分裂是极其有害的,但只会影响核心的执行。显然 AMD 比 intel ( http://gmplib.org/~tege/x86-timing.pdf ) 更擅长这些。

还有数据组织的问题。在这里,将常用数据驻留在物理上较小的区域也更好,这样一次缓存行提取将带来几个常用变量,而不是每次提取仅一个变量(这是常态)。

在访问数据数组或数据结构时,请尝试确保从较低的内存地址到较高的内存地址访问它们。同样,到处访问将对缓存产生负面影响。

最后是向处理器提供数据预取提示的技术,以便它可以指示缓存在数据实际使用之前尽可能远地开始获取数据。

为了有合理的机会理解这些东西,以便您可以在实际水平上使用它们,您有必要测试不同的结构并对其计时,最好使用 rdtsc 计数器(在 stackoverflow 上有很多关于它的信息) 或使用分析器。

于 2011-03-03T14:57:03.037 回答