1

经典的乘加运算是a = a + b*c. 但我目前想知道是否存在允许在 1 个时钟周期内对整数执行以下操作的指令:(a 和 b 是无符号 64 位整数unsigned long long int:)

a = a*2-1
a = a*2+b

目前,我使用:

a *= 2
--a

对于第一个和

a *= 2
a += b

对于第二个。而且我认为在 ASM 中每个都被翻译成 2 条指令。但是有没有办法使用 1 条 ASM 指令(以及英特尔 CPU 上的哪个指令集扩展)?

(我搜索那个是因为我做了数十亿次这个操作)

4

2 回答 2

7
  1. 对于 Intel CPU,请参阅LEA说明。它可以在一条指令中完成你的两项任务(虽然不确定周期)。(例如。LEA EAX, [EAX*2+EBX])。请注意,这并不是真正的乘加,因此它的名字很有趣(加载有效地址)。

  2. 在 C 和 C++ 中,你不应该打扰。编译器会做它认为最好的事情,你可能会阻碍它的努力。我会和老好人呆在一起a = a*2-1

PS:如果您认为某些内容被翻译为两条指令,没有什么比查看程序集更容易的了。那你就知道了

于 2012-02-11T17:08:53.003 回答
1

有很多架构可以在一条指令中执行此类操作。例如a*2 + b编译为

  • lea eax, [rsi+rdi*2]在 x86-64 上
  • add r0, r1, r0, lsl #1在 ARM 上
  • add w0, w1, w0, lsl 1在 ARM64 上
  • lda16 r0, r1[r0]在 xcore 上

编译器将适当地优化表达式。没有理由做这样的事情a *= 2; a += b,在许多情况下会降低可读性

您可以在Compiler Explorer上查看演示


但是,如果您仅仅因为您执行此操作数十亿次而问这个问题,那么这本质上是一个XY 问题,因为更改 C 版本不是正确的方法,并且减少指令数量并不是减少运行时间的方式。您不会通过指令数来衡量性能

现代 CPU 是超标量的,并且一些指令是微编码的,因此单个复杂指令可能比可以并行执行的多个简单指令慢。编译器显然知道这一点,并且会在编译时考虑延迟。真正的解决方案是使用多线程和 SIMD

例如 Clang 在 AVX-512 的主循环中发出以下指令

vpaddd  zmm0, zmm0, zmm0                            ; a *= 2
vpaddd  zmm1, zmm1, zmm1
vpaddd  zmm2, zmm2, zmm2
vpaddd  zmm3, zmm3, zmm3
vpaddd  zmm0, zmm0, zmmword ptr [rsi + 4*rdx]       ; a += b
vpaddd  zmm1, zmm1, zmmword ptr [rsi + 4*rdx + 64]
vpaddd  zmm2, zmm2, zmmword ptr [rsi + 4*rdx + 128]
vpaddd  zmm3, zmm3, zmmword ptr [rsi + 4*rdx + 192]

这涉及循环展开自动矢量化。每条指令一次可以处理16 个32 位整数。当然,如果您使用 64 位int,那么它一次只能处理 8 个。此外,每条相同的指令都可以独立完成,因此如果 CPU 有足够的执行端口,它可以int并行增加 64 秒。现在这就是我们所说的“快速”

GCC 在循环展开和使用vpslld后跟vpaddd. 但这仍然比标量版本快。在带有霓虹灯的 ARM 上,您可以看到shl v0.4s, v0.4s, 1; add v0.4s, v0.4s, v1.4s它已被使用。这是编译器资源管理器演示链接

与比“优化”快得多的多线程相结合

于 2019-04-05T17:26:46.640 回答