x86_64:IMUL 比 2x SHL + 2x ADD 更快吗?

2024-03-14

当查看 Visual Studio (2015U2) 生成的程序集时/O2(发布)模式我看到这段“手工优化”的 C 代码被翻译回乘法:

int64_t calc(int64_t a) {
  return (a << 6) + (a << 16) - a;
}

集会:

  imul        rdx,qword ptr [a],1003Fh  

所以我想知道这是否真的比按照编写的方式执行要快,例如:

  mov         rbx,qword ptr [a]  
  mov         rax,rbx  
  shl         rax,6  
  mov         rcx,rbx  
  shl         rcx,10h  
  add         rax,rcx  
  sub         rax,rbx  

我一直有这样的印象:乘法总是比几次移位/加法慢?现代 Intel x86_64 处理器不再是这种情况了吗?


没错,现代 x86 CPU(尤其是 Intel)具有非常高性能的乘法器。
imul r, r/m and imul r, r/m, imm在 Intel SnB 系列和 AMD Ryzen 上,两者都是 3 个周期延迟,每 1c 吞吐量一个周期延迟,即使对于 64 位操作数大小也是如此。

在 AMD Bulldozer 系列上,延迟为 4c 或 6c,每 2c 延迟 1 次或每 4c 吞吐量 1 次。 (64 位操作数大小的速度较慢)。

数据来自Agner Fog 的说明书 http://agner.org/optimize/。另请参阅中的其他内容x86 /questions/tagged/x86标签维基。


现代 CPU 中的晶体管预算相当庞大,并允许以如此低的延迟执行 64 位乘法所需的硬件并行量。 (这需要一个lot加法器的数量 https://en.wikipedia.org/wiki/Dadda_multiplier做一个大快速乘法器 https://en.wikipedia.org/wiki/Binary_multiplier#Implementations. 现代 X86 处理器实际上如何计算乘法? https://stackoverflow.com/questions/26370287/how-modern-x86-processors-actually-compute-multiplications/62117473#62117473).

受到功率预算而不是晶体管预算的限制,意味着可以为许多不同的功能提供专用硬件,只要它们不能同时切换(https://en.wikipedia.org/wiki/Dark_silicon https://en.wikipedia.org/wiki/Dark_silicon)。例如你不能饱和pext/pdep单元、整数乘法器和向量 FMA 单元同时在 Intel CPU 上运行,因为它们中的许多单元位于相同的执行端口上。

有趣的事实:imul r64也是 3c,因此您可以在 3 个周期内获得完整的 64*64 => 128b 乘法结果。imul r32不过,是 4c 延迟和额外的 uop。我的猜测是,额外的微操作/周期将常规 64 位乘法器的 64 位结果分成两个 32 位一半。


编译器通常会针对延迟进行优化,并且通常不知道如何优化短的独立依赖链以提高吞吐量,而不是优化延迟瓶颈的长循环承载依赖链。

gcc 和 clang3.8 及更高版本最多使用两个LEA指令而不是imul r, r/m, imm。我认为 gcc 会使用imul如果替代方案是 3 个或更多指令(不包括mov), 尽管。

这是一个合理的调整选择,因为 3 指令 dep 链的长度与imul关于英特尔。使用两条 1 周期指令会花费额外的 uop,将延迟缩短 1 周期。

clang3.7 及更早版本往往受到青睐imul只需要单个 LEA 或移位的乘法器除外。因此,clang 最近改为针对延迟进行优化,而不是针对乘以小常数的吞吐量进行优化。 (或者可能是出于其他原因,例如不与仅与乘法器位于同一端口上的其他事物竞争。)

e.g. Godbolt 编译器浏览器上的这段代码 http://gcc.godbolt.org/#compilers:!((compiler:g6,options:'-xc+-std%3Dgnu11+-Wall+-Wextra+-fverbose-asm+-O3+-march%3Dhaswell',source:'int+foo+(int+a)+%7B+return+a+*+63%3B+%7D')),filterAsm:(commentOnly:!t,directives:!t,intel:!t,labels:!t),version:3:

int foo (int a) { return a * 63; }
    # gcc 6.1 -O3 -march=haswell (and clang actually does the same here)
    mov     eax, edi  # tmp91, a
    sal     eax, 6    # tmp91,
    sub     eax, edi  # tmp92, a
    ret

clang3.8 及更高版本生成相同的代码。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

x86_64:IMUL 比 2x SHL + 2x ADD 更快吗? 的相关文章

  • 在Python中为什么ifrank:比ifrank!= 0更快:

    当我改变的时候 for i in range 0 100 rank ranks i if rank 0 pass to for i in range 0 100 rank ranks i if rank pass 我发现第二个代码效率更高
  • MSMQ 慢速队列读取

    我正在使用一个开源 Net 库 它在底层使用 MSMQ 大约一两周后 服务速度变慢 时间不准确 但一般猜测 看来发生的情况是来自 MSMQ 的消息每 10 秒才被读取一次 通常 它们会立即被读取 因此 它们将在 T 10 秒 T 20 秒
  • 为什么比较匹配的字符串比比较不匹配的字符串更快? [复制]

    这个问题在这里已经有答案了 这里有两个测量值 timeit timeit toto 1234 number 100000000 1 8320042459999968 timeit timeit toto toto number 100000
  • 内容长度标头与分块编码

    我正在尝试权衡设置的利弊Content LengthHTTP 标头与使用分块编码从我的服务器返回 可能 大文件的比较 使用持久连接需要其中之一来符合 HTTP 1 1 规范 我看到了的优点Content Length标头是 下载对话框可以显
  • JMeter:tearDown Thread Group的目的是什么

    我想了解JMeter中tearDown Thread Group的实际用法 在什么场景下可以使用tearDown Thread Group 根据提供的帮助JMeter 拆解线程组 http jmeter apache org userman
  • 如何提高QNX6下Eclipse IDE的性能

    我们在 VMWare 环境中通过 QNX6 运行 Eclipse 速度非常慢 Eclipse 是这样启动的 usr qnx630 host qnx6 x86 usr qde eclipse eclipse data root workspa
  • Python:多重分配与单独分配速度

    我一直在寻求从我的代码中挤出更多的性能 最近 在浏览时这个 Python 维基页面 https wiki python org moin PythonSpeed 我发现了这个说法 多重分配比单独分配慢 例如 x y a b 比 x a y
  • PrintStream是有缓冲的,但是flush不会降低性能,而BufferedOutputStream会加速性能

    我预计由于 PrintStream 是缓冲的 通过在每次 print 之后添加刷新操作 速度性能应该会显着降低 但事实并非如此 如下面的代码片段所示 此外 将 PrintStream 包裹在 BufferedOutputStream 周围可
  • 有没有办法使用 i387 fsqrt 指令获得正确的舍入?

    有没有办法使用 i387 fsqrt 指令获得正确的舍入 除了改变精确模式在 x87 控制字中 我知道这是可能的 但这不是一个合理的解决方案 因为它存在令人讨厌的重入型问题 如果 sqrt 操作中断 精度模式将出错 我正在处理的问题如下 x
  • “rep stos”x86 汇编指令序列有什么作用?

    我最近偶然发现了以下汇编指令序列 rep stos dword ptr edi For ecx重复 存储内容eax到哪里edi指向 递增或递减edi 取决于方向标志 每次 4 个字节 通常 这用于memset型操作 通常 该指令简单地写成r
  • 如何知道寄存器是否是“通用寄存器”?

    我试图了解寄存器必须具备什么标准才能被称为 通用寄存器 我相信通用寄存器是一个可以用于任何用途的寄存器 用于计算 将数据移入 移出等 并且是一个没有特殊用途的寄存器 现在我读到了ESP寄存器是通用寄存器 我猜是ESP寄存器可以用于任何事情
  • 更改二维数组元素的值会更改整个列

    当我打印我的arrvalue 我得到了 2D 数组的正确值 但是当我退出 while 循环时 我的值都是错误的 我不确定我做错了什么 num runs n 4 x np linspace 1 1 n y np linspace 1 1 n
  • AVX-512CD(冲突检测)与原子变量访问有何不同?

    所以我在看他们展示了如何 void Histogram const float age int const hist const int n const float group width const int m const float o
  • 访问特征矩阵的行向量时复制或引用

    我正在使用的代码Eigen http eigen tuxfamily org index php title Main Page矩阵库 我注意到在整个代码中 有如下访问器 RowVector3f V size t vertex index
  • php字符串是值类型吗?

    为什么php的string是值类型 每次将参数传递给函数时 每次进行赋值时 每次连接都会导致字符串被复制时 它都会被复制到各处 我的 NET 经验告诉我 它似乎效率低下 迫使我几乎在任何地方都使用引用 考虑以下替代方案 替代方案1 This
  • CPU寄存器和多任务处理

    我目前正在学习汇编 我很困惑 CPU 寄存器如何与多任务一起工作 所以在多任务系统中 CPU可以随时暂停某个程序的执行并运行另一个程序 那么在这一步中寄存器值是如何保存的呢 寄存器是压入堆栈还是以其他方式 CPU 寄存器如何与多任务一起工作
  • 我应该增强客户端上的 Jquery Mobile 元素还是发送带有 data-enhance="false" 的增强标记?

    我有一个产品搜索 我正在发送回结果 每个结果都包含两个按钮 JQM 控制组 我一次发送 24 条记录 因此需要增强 24 个控制组 如下所示 div class submitButton linkBox div
  • 阴影空间示例

    EDIT 我接受了下面的答案 并添加了我自己的代码的最终修订版 希望它向人们展示影子空间分配的实际示例 而不是更多的文字 编辑 2 我还设法在 YouTube 视频 所有内容 的注释中找到了一个调用约定 PDF 的链接 其中有一些关于 Li
  • 展开 std::reference_wrapper 的成本

    Given include
  • 如何在 C++ 中对静态缓冲区执行字符串格式化?

    我正在处理一段对性能要求非常高的代码 我需要执行一些格式化的字符串操作 但我试图避免内存分配 甚至是内部库的内存分配 在过去 我会做类似以下的事情 假设是 C 11 constexpr int BUFFER SIZE 200 char bu

随机推荐