x86 组装pushad/popad,速度有多快?

2024-02-29

我只是想在 x86 汇编中制作非常快速的基于计算的程序 但我需要在调用程序之前推送累加器、计数器和数据寄存器。手动推送它们更快:

push eax
push ecx
push edx

或者只是使用,

pushad

和 pop 一样。谢谢


如果你关心性能,pusha / popa几乎没有用处。它们仅在以牺牲速度为代价来优化代码大小时才有用,例如保存/恢复函数周围的寄存器。但对于非人来说非常不方便void函数,因为它们会重新加载all寄存器,因此您必须将返回值存储在内存中(例如,通过将被加载到的堆栈槽)eax,或其他地方之后重新加载popad).

只压入需要保存的寄存器,或者您想要作为函数参数传递。或者,在内联汇编 /questions/tagged/inline-assembly,只需让编译器通过声明来为您管理寄存器"=r"(dummy1)任何临时寄存器的虚拟输出操作数,或在特定寄存器上使用 clobber。通常,编译器可以选择可以让您破坏而不保存的寄存器。 (或者在笨重的 MSVC 风格的内联汇编中,编译器无法为您分配寄存器,因此您必须手动选择。编译器会解析您的汇编以查找破坏者。)

您通常不需要保存/恢复eax;为了性能你应该mov esi, eax/调用/使用中的值esi,如果您无法计算其中的值esi首先。即使用调用保留寄存器来保存需要保存的值call,因此重要值的存储/重新加载不在关键路径上。相反,存储/重新加载位于您(或编译器)调用者的调用保留寄存器之一的关键路径上push/pop围绕整个函数,在任何循环之外。

查看更多关于调用保留寄存器与调用破坏寄存器 https://stackoverflow.com/questions/9268586/what-are-callee-and-caller-saved-registers/56178078#56178078以及保存/恢复通常如何进行。以及什么是良好的调用约定,例如x86-64 System V 是如何设计的 https://stackoverflow.com/questions/4429398/why-does-windows64-use-a-different-calling-convention-from-all-other-oses-on-x86/35619528#35619528,并且本次问答 https://stackoverflow.com/questions/33707228/why-not-store-function-parameters-in-xmm-vector-registers关于应该在寄存器中传递多少个参数,以及为什么不使用 XMM 寄存器来传递整数参数。当然,辅助函数可以使用自定义调用约定。


pusha / popa在大多数 CPU 上都很慢

即使您确实想推送所有 8 个整数寄存器(包括esp!),使用8个独立的push现代 CPU 上的指令实际上更快。 Pusha/popa 是微编码的,这对于前端来说可能是一个问题 https://stackoverflow.com/questions/26907523/branch-alignment-for-loops-involving-micro-coded-instructions-on-intel-snb-famil。 (尽管 8 个单字节指令也可能对 uop 缓存造成问题。但在实际代码中,您通常只需要推送几个寄存器,而不是全部。)

如果您正在针对过时的 CPU(例如原始的有序 Pentium 和 Pentium II/III)进行优化,则 Pusha/popa 的速度可达 8push r or 8 pop r,实际上更少的微指令,因为他们没有堆栈引擎来消除 ESP 更新微指令。

From Agner Fog 的说明书 http://agner.org/optimize/:现代 CPU 具有单微指令push reg and pop reg,因为编译器始终使用这些指令,因此对性能很重要。推送/弹出吞吐量通常与存储/加载吞吐量相匹配(通常每个时钟 1 次存储或每个时钟 2 次加载)。但pusha / popa编译器不使用它们,因此 CPU 设计者没有特殊的支持来提高它们的速度。popa吞吐量仅限于每个时钟 1 个负载,如果just跑步popa。 (我认为在 Intel CPU 上,测量性能最可能的解释是popa不使用堆栈引擎,因此它的瓶颈在于对esp.)

Intel:

  • 天湖:pusha:11 uop,8c 吞吐量。popa:18 uops / 8c 吞吐量。
  • 珊迪大桥:pusha:16 uops / 8c 吞吐量。popa:18 uops / 9c 吞吐量。
  • 尼哈勒姆:pusha:18 uops / 8c 吞吐量。popa:10 uops / 8c 吞吐量。
  • 西尔弗蒙特/KNL:pusha:10 uops / 10c 吞吐量。popa:17 uops / 14c 吞吐量。
  • 奔腾4:pusha:4/10 uops / 19c 吞吐量。popa:4/16 uops / 14c 吞吐量。
  • P5 Pentium 1 / MMX:5-9 个周期,不可配对。 “如果 SP 能被 4 整除,则为 9(不完美配对)。”

AMD: pusha/popa在某些 AMD CPU 上表现出奇的好,尤其是 K8。

  • Ryzen: pusha:9 uop,8c 吞吐量。popa: 9 个微指令,4c 吞吐量。 (与英特尔不同的是,AMD 的新设计popa不低于8倍pop.)
  • Jaguar: pusha:9 uops / 8c 吞吐量。popa:9 uops / 8c 吞吐量。 (Jaguar 通常每个时钟只能执行一次负载。)
  • 打桩机:pusha:9 uops / 9c 吞吐量。popa:14 uops / 8c 吞吐量。 (阿格纳列出了常规pop regBulldozer 系列的吞吐量为每个时钟 1,尽管我认为他们确实有一个堆栈引擎并且每个时钟可以执行 2 个负载。也许堆栈引擎一次只能处理一条堆栈指令?)
  • K8: pusha:9 uops / 4c 吞吐量!! (不知道这是怎么可能的,要么这是表中的错误或拼写错误,要么 K8 合并了 32 位寄存器并进行了四个 64 位存储)。popa:9 uops / 4c 吞吐量。这些数字看起来确实是真实的:InstLatx86 测量 http://users.atw.hu/instlatx64/AuthenticAMD0000F4A_K8_Clawhammer_InstLatX86.txt同意 4c 吞吐量pushad / popadClawhammer(第一代 K8 微架构)。很明显AMD在优化上付出了一些努力pushad.

您标记了此内联汇编 /questions/tagged/inline-assembly。通常你应该避免使用call在内联汇编中,因此 C 编译器知道该调用。

让编译器关心寄存器;只需告诉它你修改了哪些(GNU Casm("..." ::: "eax", "ecx")或其他),或者在 MSVC 风格的内联汇编中,它会解析您的汇编并知道写入了哪些寄存器。如果其中包含任何调用保留的寄存器,编译器将在整个函数的开始/结束处保存/恢复这些寄存器,即使 asm 语句处于循环中也是如此。 (它可能需要在asm语句或块之前/之后溢出和/或重新加载一些本地变量,但会使用mov,而不是push/pop。)

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

x86 组装pushad/popad,速度有多快? 的相关文章

  • Asp.net Mvc OutputCache属性和滑动过期

    Calling http foo home cachetest for UrlRoute Path home cachetest OutputCache Duration 10 VaryByParam none public ActionR
  • 方法与管道

    在 Angular 应用程序中的模板插值中使用管道和方法有区别吗 例如 h1 name toLowerCase h1 vs h1 name lowercase h1 就性能而言 是有真正的收获还是只是个人喜好 我知道调用模板中的方法通常会降
  • 系数函数速度慢

    请考虑 Clear x expr Sum x i i 15 30 CoefficientList expr x Timing Coefficient Expand expr x 234 Timing Coefficient expr x 2
  • 如何将 asm 着色器编译为 fxo 文件?

    我有一个已编译的 fxo 着色器 我正在尝试对其进行稍微编辑 仅调整一些常量 使用 fxdis https code google com archive p fxdis d3d1x https code google com archiv
  • 将以下机器语言代码(0x2237FFF1)翻译成MIPS汇编

    到目前为止我已经翻译了这段代码 但我不明白的是如何计算 计算 16 位立即地址的数量 0x2237FFF1 转为二进制 0010 0010 0011 0111 1111 1111 1111 0001 现在我正在读取操作码 001000 并知
  • JS中函数声明速度差异

    我运行了一个简单的 jsperf 测试 在 Firefox 中运行时一切都按预期进行 但当我在 Google Chrome 中运行测试时却感到困惑 该测试正在测试在 JavaScript 中声明函数然后调用它们的不同方式 我的猜测是 Chr
  • Python(和 Java)中最快的数据打包

    Sometimes http www codinghorror com blog 2009 01 the sad tragedy of micro optimization theater html our host is wrong na
  • 在 qemu 中将扇区加载到 RAM

    我编写了一个简单的程序 将扇区 扇区编号 2 加载到 RAM 但什么也没打印 首先 我尝试了以下引导扇区代码 org 0x7c00 mov ax 0x1000 ES BX 1000 0000 mov es ax mov bx 0x00 Lo
  • 从 Golang 调用 C 函数

    我想在 Golang 中编写控制器逻辑并处理 json 和数据库 同时在 C 中使用我的数学处理模型 在我看来 调用 C 函数的开销必须尽可能低 就像设置寄存器 rcx rdx rsi rdi 一样 执行一些操作fastcall 并获取 r
  • 如何优化 R 中的 sapply 来计算数据帧上的运行总计

    我在 R 中编写了一个函数来按月份计算累积总数 但随着数据集变大 我的方法的执行时间呈指数增长 我是一名 R 程序员新手 你能帮我提高效率吗 该函数以及我调用该函数的方式 accumulate lt function recordnum d
  • 将 numpy 数组写入文本文件的速度

    我需要将一个非常 高 的两列数组写入文本文件 而且速度非常慢 我发现如果我将数组改造成更宽的数组 写入速度会快得多 例如 import time import numpy as np dataMat1 np random rand 1000
  • 调整 Oracle 数据库以加快启动速度(闪回)

    我正在使用 Oracle 数据库 11 2 我有一个场景 我发出FLASHBACK DATABASE经常 似乎有一个FLASHBACK DATABASECycle 会重新启动数据库实例 大约需要 1 分钟 我的设置花了 7 秒 数据库很小
  • INT 13h 无法读取超出特定扇区的数据

    我正在为我的操作系统编写内核 在将磁盘扇区加载到内存时遇到问题 以下是从磁盘加载扇区的函数代码部分 mov ax 0x3000 mov es ax mov ax 0x0201 mov bx word ptr bp 6 bx 0x000 0x
  • NASM:如何正确访问SSD驱动器?

    我需要使用 NASM 16 位代码访问 SSD 驱动器 访问普通硬盘时 需要设置寄存器AX DX CX来选择柱面 磁道 扇区 扇区数 AH 选择读扇区功能 DL 选择驱动器号 CH 选择气缸 DH 选择磁盘上的一侧 CL 选择步入正轨的部门
  • 在 R 中,为什么 sum 与其他方法(例如 cumsum)相比如此慢?

    我正在尝试实现一个需要非常快的函数 主要是因为它一遍又一遍地处理巨大的数据帧 R 总是让我感到困惑 为什么它有时有点慢 而有时又慢得离谱 不幸的是 它从来都不快 不管怎样 我一直认为 如果可能的话 当以某种方式推入 apply sapply
  • 每个存储桶的最大沙发底座视图数

    假设存储桶中有大量数据 gt 100GB gt 100M 文档 gt 12 种文档类型 并且假设每个视图仅适用于一种文档类型 那么每个存储桶有多少视图就太多了 或者以另一种方式问 在什么时候应该将某些文档类型拆分到单独的存储桶中 以节省处理
  • 如何阅读英特尔操作码符号

    我正在阅读一些引用的材料Intel vol 2 SDM x86 手册 https www intel com content www us en developer articles technical intel sdm html关于汇编
  • 如何在 PHP 数组中的另一个已知(通过键或指针)元素之后有效地插入元素?

    给定一个数组 a array abc 123 k1 gt v1 k2 gt v2 78 tt k3 gt v3 当其内部指针指向其元素之一时 如何在当前元素之后插入元素 如何在键已知元素 例如 k1 之后插入元素 表现护理 您可以通过使用拆
  • 为什么 ConcurrentHashMap::putIfAbsent 比 ConcurrentHashMap::computeIfAbsent 更快?

    使用 ConcurrentHashMap 我发现computeIfAbsent 比putIfAbsent 慢两倍 这是简单的测试 import java util ArrayList import java util List import
  • 为什么这个函数在额外读取内存时运行速度如此之快?

    我目前正在尝试了解 x86 64 上某些循环的性能属性 特别是我的 Intel R Core TM i3 8145U CPU 2 10GHz 处理器 具体来说 在循环体内添加一条额外的指令来读取内存几乎可以使性能提高一倍 而细节并不是特别重

随机推荐