哪种方法在汇编语言中执行得更快:通过变量添加或通过立即值添加?为什么?

时间:2012-01-07 03:04:10

标签: variables optimization memory assembly

例如:

; Method 1
.data
val1 DWORD 10000h
.code
add eax,val1

v.s:

; Method 2
.code
add eax,10000h

编译(汇编)后哪种方法执行得更快? 我认为方法2会产生更快的代码,因为在添加到eax寄存器之前,CPU不必从主存储器读取值。我的回答不太清楚,有人可以帮忙吗?

4 个答案:

答案 0 :(得分:5)

很可能,情况会有所不同,差异可能甚至不明显。

out-of-order execution等因素可能会隐藏任何类型的固有"缓慢"任何版本,除非确实存在瓶颈。

那就是说,如果我们必须选择哪个更快,那么你是正确的,第二种情况可能会更快。

如果我们查看所有当前x86处理器的Agner Fog's tables

核心2:

add/sub    r, r/i    Latency = 1        , 1/Throughput = 0.33
add/sub    r, m      Latency = unknown  , 1/Throughput = 1

<强>的Nehalem:

add/sub    r, r/i    Latency = 1        , 1/Throughput = 0.33
add/sub    r, m      Latency = unknown  , 1/Throughput = 1

Sandy Bridge:

add/sub    r, r/i    Latency = 1        , 1/Throughput = 0.33
add/sub    r, m      Latency = unknown  , 1/Throughput = 0.5

<强> K10:

add/sub    r, r/i    Latency = 1        , 1/Throughput = 0.33
add/sub    r, m      Latency = unknown  , 1/Throughput = 0.5

在所有情况下,内存操作数版本的吞吐量都较低。在所有情况下,延迟都是未知的,但几乎肯定会超过1个周期。因此,所有因素都会更糟。

内存操作数版本使用与立即版本相同的所有执行端口+它还需要一个内存读取端口。这只会使情况变得更糟。事实上,这就是内存操作数的吞吐量较低的原因 - 内存端口只能维持1或2次读取/循环,而加法器可以维持一个完整的3 /周期。

此外,这假设数据在L1缓存中。如果不是,则内存操作数版本将 MUCH 更慢。


更进一步,我们可以检查编码指令的大小:

add eax,val1    ->   03 05 14 00 00 00
add eax,10000h  ->   05 00 00 01 00

第一个的编码可能会略有不同,具体取决于val1的地址。我在这里展示的例子来自我的特定测试用例。

因此,内存访问版本需要一个额外的字节进行编码 - 这意味着代码大小稍大 - 并且可能会在极端情况下出现更多的i-cache错误。


总而言之,如果版本之间存在性能差异,则立即可能会更快,因为:

  1. 延迟较低。
  2. 它具有更高的吞吐量。
  3. 编码较短。
  4. 它不需要访问数据缓存 - 这可能是缓存未命中。

答案 1 :(得分:3)

添加一个立即(你的神奇十六进制值)确实更快(至少我知道的架构)。

我认为问题是,多少。现在我认为这取决于val1是否被缓存的事实。

如果它没有被缓存,那么它非常慢,因为访问内存比访问缓存更慢(无论什么级别,缓存l1确实是最快的)。

如果它确实被缓存,结果在我最不同意见中非常接近。

答案 2 :(得分:3)

10000h将从内存中读取,无论是从数据存储器中的位置,还是从指令存储器中的位置读取。对于较小的常量值,CPU提供的特殊指令不需要额外的空间来添加值,但这取决于特定的体系结构。由于缓存,add immediate可能会更快:在解码指令时,常量将在缓存中,并且添加速度非常快。

小偏离主题说明:您的示例显示了优化C编译器生成比手写汇编更快的代码的情况:优化器可以将上半字增加1,而不是添加10000h。低半字也是如此。

答案 3 :(得分:0)

我有一段时间没有完成装配,但我相信这段代码并不等同。

在方法1中,将val1的地址添加到eax,在方法2中,将常量值10000h添加到eax ...要添加变量的内容,您必须执行

add eax,[val1]

这会慢一些,因为它会触发内存读取。这段代码甚至可能不合法。你不应该这样做:

mov ecx, val1
add eax, [ecx]

正如我所说,我的英特尔组装非常生锈:)