x86-64 汇编的性能优化 - 对齐和分支预测

2024-01-12

我目前正在编写一些 C99 标准库字符串函数的高度优化版本,例如strlen(), memset()等,使用带有 SSE-2 指令的 x86-64 汇编。

到目前为止,我已经在性能方面取得了出色的结果,但当我尝试进一步优化时,有时会出现奇怪的行为。

例如,添加甚至删除一些简单的指令,或者简单地重新组织一些与跳转一起使用的本地标签会完全降低整体性能。就代码而言,完全没有理由。

所以我的猜测是,代码对齐和/或分支预测存在一些问题。

我知道,即使采用相同的架构(x86-64),不同的CPU也有不同的分支预测算法。

但是,在 x86-64 上进行高性能开发时,是否有一些关于代码对齐和分支预测的一般建议?

特别是,关于对齐,我是否应该确保跳转指令使用的所有标签都在 DWORD 上对齐?

_func:
    ; ... Some code ...
    test rax, rax
    jz   .label
    ; ... Some code ...
    ret
    .label:
        ; ... Some code ...
        ret

在前面的代码中,我应该在之前使用对齐指令吗.label:, like:

align 4
.label:

如果是这样,在使用 SSE-2 时对齐 DWORD 是否足够?

关于分支预测,是否有一种“首选”方式来组织跳转指令使用的标签,以帮助 CPU,或者当今的 CPU 是否足够智能,可以在运行时通过计算分支发生的次数来确定这一点?

EDIT

好的,这是一个具体的例子 - 这是开始strlen()使用 SSE-2:

_strlen64_sse2:
    mov         rsi,    rdi
    and         rdi,    -16
    pxor        xmm0,   xmm0
    pcmpeqb     xmm0,   [ rdi ]
    pmovmskb    rdx,    xmm0
    ; ...

使用 1000 个字符串运行 10'000'000 次大约需要 0.48 秒,这还不错。
但它不检查 NULL 字符串输入。显然,我将添加一个简单的检查:

_strlen64_sse2:
    test       rdi,    rdi
    jz          .null
    ; ...

同样的测试,现在运行时间为 0.59 秒。但是如果我在检查后对齐代码:

_strlen64_sse2:
    test       rdi,    rdi
    jz          .null
    align      8
    ; ...

原来的表演又回来了。我使用 8 进行对齐,因为 4 不会改变任何内容。
谁能解释一下这一点,并就何时对齐或不对齐代码部分提供一些建议?

EDIT 2

当然,这并不像对齐每个分支目标那么简单。如果我这样做,性能通常会变得更糟,除非像上面这样的某些特定情况。


对齐优化

1. Use .p2align <abs-expr> <abs-expr> <abs-expr> http://sourceware.org/binutils/docs/as/P2align.html代替align.

使用 3 个参数进行细粒度控制

  • param1- 与什么边界对齐。
  • param2- 用什么(零或NOPs).
  • param3- 如果填充超出指定的字节数,则不对齐。

2. 将常用代码块的开头与缓存行大小边界对齐。

  • 这增加了整个代码块位于单个缓存行中的机会。一旦加载到 L1 高速缓存中,就可以完全运行,无需访问 RAM 来获取指令。这对于具有大量迭代的循环非常有益。

3.使用多字节NOPs 用于填充到减少执行时间NOPs https://softwareengineering.stackexchange.com/questions/158624/are-some-nop-codes-treated-differently-than-others.

  /* nop */
  static const char nop_1[] = { 0x90 };

  /* xchg %ax,%ax */
  static const char nop_2[] = { 0x66, 0x90 };

  /* nopl (%[re]ax) */
  static const char nop_3[] = { 0x0f, 0x1f, 0x00 };

  /* nopl 0(%[re]ax) */
  static const char nop_4[] = { 0x0f, 0x1f, 0x40, 0x00 };

  /* nopl 0(%[re]ax,%[re]ax,1) */
  static const char nop_5[] = { 0x0f, 0x1f, 0x44, 0x00, 0x00 };

  /* nopw 0(%[re]ax,%[re]ax,1) */
  static const char nop_6[] = { 0x66, 0x0f, 0x1f, 0x44, 0x00, 0x00 };

  /* nopl 0L(%[re]ax) */
  static const char nop_7[] = { 0x0f, 0x1f, 0x80, 0x00, 0x00, 0x00, 0x00 };

  /* nopl 0L(%[re]ax,%[re]ax,1) */
  static const char nop_8[] =
    { 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00};

  /* nopw 0L(%[re]ax,%[re]ax,1) */
  static const char nop_9[] =
    { 0x66, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 };

  /* nopw %cs:0L(%[re]ax,%[re]ax,1) */
  static const char nop_10[] =
    { 0x66, 0x2e, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 };

(upto 10byte NOPs 代表 x86。来源binutils-2.2.3 https://android.googlesource.com/toolchain/binutils/+/f226517827d64cc8f9dccb0952731601ac13ef2a/binutils-2.23/bfd/cpu-i386.c.)


分支预测优化

Lot of variations between x86_64 micro-architectures/generations. However a common set of guidelines that are applicable for all of them can be summarised as follows. Reference : .

1. 展开循环以避免迭代计数稍微过高。

  • 循环检测逻辑保证仅适用于具有< 64迭代。这是因为如果分支指令单向运行,则该分支指令将被识别为具有循环行为n-1次,然后走另一条路1时间,对于任何n最多 64 个。

    这并不真正适用于 Haswell 及更高版本中的预测器,它们使用 TAGE 预测器,并且没有针对特定分支的专用循环检测逻辑。在 Skylake 上,对于没有其他分支的紧密外循环内的内循环来说,迭代计数约为 23 可能是最坏的情况:内循环的退出大多数时候都会错误预测,但行程计数非常低,因此经常发生。展开可以通过缩短模式来提供帮助,但对于非常高的循环行程计数,最终的单个错误预测会在多次行程中摊销,并且需要不合理的展开量才能对此采取任何措施。

2.坚持近跳/短跳。

  • 无法预测远跳转,即管道总是在远跳转到新代码段 (CS:RIP) 时停止。无论如何,基本上没有理由使用远跳,所以这基本上是不相关的。

    在大多数 CPU 上,通常可以预测具有任意 64 位绝对地址的间接跳转。

    但是,当目标距离超过 4GB 时,Silvermont(Intel 的低功耗 CPU)在预测间接跳转方面存在一些限制,因此通过在低 32 位虚拟地址空间中加载/映射可执行文件和共享库可以避免这种情况的发生。 。例如在 GNU/Linux 上通过设置环境变量LD_PREFER_MAP_32BIT_EXEC http://man7.org/linux/man-pages/man8/ld.so.8.html。有关更多信息,请参阅英特尔的优化手册。

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

x86-64 汇编的性能优化 - 对齐和分支预测 的相关文章

  • 如何清除 APC 缓存而不使 Apache 崩溃?

    如果 APC 存储大量条目 清除它们会导致 httpd 崩溃 如果 apc clear cache user 花费的时间超过 phps max execution time 调用 apc clear cache 的脚本 将在之前被 php
  • 如何优化分割重叠范围?

    我编写的这个 Python 脚本用于将重叠范围拆分为唯一范围 最后一次迭代 https codereview stackexchange com questions 285932 python script to split overlap
  • 如何改变HTML5视频的播放速度?

    如何更改 HTML5 中的视频播放速度 我查过视频标签的属性 https www w3schools com html html5 video asp在 w3school 但无法做到这一点 根据这个网站 http www chipwreck
  • 如何将 Browserify 与外部依赖项一起使用?

    我正在尝试慢慢地将 Browserify 引入我的网站 但我不想重写所有 js 也不希望 jquery 和其他库的重复实例与我的 Browserify 版本捆绑在一起 如果我构建将 jquery 列为外部依赖项的模块 那么如何将其指向我的全
  • Android复杂布局线性和相对

    I have to implement a layout like shown in the diagram and I do not know the best combination to achieve the required de
  • 具有独特矩阵转置问题的 2D 分块

    我有类型的复杂值数据struct complex double real 0 0 double imag 0 0 以 3 阶张量的形式组织 底层容器具有与内存页边界对齐的连续内存布局 The natural slicing directio
  • 调用可以是 cdecl 或 stdcall 的函数

    我需要编写调用外部函数的代码 该函数可以是 32 位 Windows 应用程序中的 stdcall 调用或 cdecl 我的代码 调用者 无法提前知道其中的哪一个 现在 如果我尝试从定义为 stdcall 的调用站点调用 cdecl 函数
  • IN 运算符对 SQL 查询性能的影响有多大?

    我的 SQL 查询需要 9 个小时才能执行 见下文 Select Field1 Field2 From A Where Field3 IN 45 unique values here 当我将此查询拆分为 3 个完全相同的查询 仅每个 IN
  • IDA pro asm 指令更改

    我只是想知道我怎样才能 更改IDA视图A中的asm指令 如何编辑指令 对于 实例 jnz 到 jmp 如何插入新指令 call func1 调用 func2 插入到现有的 代码 我知道如何制作 diff 文件 我知道如何在我的 DLL 上应
  • 基于范围的 for 循环对性能有益吗?

    阅读 Stack Overflow 上有关 C 迭代器和性能 的各种问题后 我开始想知道是否for auto elem container 被编译器 扩展 成最好的版本 就像auto 编译器立即将其推断为正确的类型 因此永远不会更慢 有时甚
  • 每个 mmap/access/munmap 两次 TLB 未命中

    for int i 0 i lt 100000 i int page mmap NULL PAGE SIZE PROT READ PROT WRITE MAP ANONYMOUS MAP PRIVATE 1 0 page 0 0 munma
  • 哪些mysql设置影响LOAD DATA INFILE的速度?

    让我来介绍一下情况 我们尝试将适量的行数 大约每天 10 20M 插入到宽度适中的 MyISAM 表中 Field Type Null Key Default Extra blah1 varchar 255 NO PRI blah2 var
  • Blob 的簇生长

    考虑以下来自 Mathworks 的图像 我已经用标签标记了斑点 L num bwlabel I 如何迭代连接所有斑点 即从一个斑点开始 找到离它最近的一个 考虑最左边的两个斑点 可以从一个斑点的许多点绘制许多条线来连接到另一个斑点blob
  • 在哪里可以找到Python内置序列类型的时间和空间复杂度

    我一直无法找到此信息的来源 无法亲自查看 Python 源代码来确定这些对象是如何工作的 有谁知道我可以在网上找到这个吗 结帐时间复杂度 http wiki python org moin TimeComplexitypy dot org
  • 为什么将 std::endl 与 ostringstream 一起使用会影响输出速度?

    我正在计算将文本打印到标准输出的各种方法之间的差异 我正在测试cout printf and ostringstream两者同时使用 n and std endl 我期望std endl有所作为cout 确实如此 但我没想到它会减慢输出速度
  • badoo.com 用户搜索 - 如何做到这一点?

    Badoo com 拥有 56 000 000 个用户个人资料 个人资料可以按性别 年龄 发色 生肖 学历等进行搜索 再加上距家乡的距离 在线状态和注册日期 到目前为止 这似乎是可行的 即使它是对巨大表 56m 成员 的相当多的查询 它也可
  • 在 x86 程序集中打印寄存器值的简单方法

    我需要在 8086 Assembly 中编写一个程序 接收来自用户的数据 进行一些数学计算并在屏幕上打印答案 我已经编写了程序的所有部分并且一切正常 但我不知道如何打印号码显示到屏幕上 在我所有计算结束时 答案是 AX 它被视为无符号 16
  • 难以理解汇编命令“加载有效地址”[重复]

    这个问题在这里已经有答案了 可能的重复 LEA 指令的目的是什么 https stackoverflow com questions 1658294 whats the purpose of the lea instruction LEA指
  • 网页优化:为什么组合文件速度更快?

    我读过 将所有 css 文件合并为一个大文件 或将所有脚本文件合并为一个脚本文件 可以减少 HTTP 请求的数量 从而加快下载速度 但我不明白这一点 我认为如果你有多个文件 最多有一个限制 我相信在现代浏览器上是 10 个 浏览器会并行下载
  • 有条件减少 CUDA

    我需要总结一下100000值存储在数组中 但带有条件 有没有办法在 CUDA 中做到这一点以快速产生结果 任何人都可以发布一个小代码来做到这一点吗 我认为 要执行条件约简 您可以直接将条件引入为乘法0 假 或1 真 加数 换句话说 假设您希

随机推荐

  • 而是触发器或计算列?哪个更好?

    我想知道下面两种方法之间是否存在性能差异 基本上 问题是我们允许在 id 中使用空格和破折号 但某些遗留应用程序无法使用它们 因此它们被删除 据我所知 最简洁的方法是在触发器中或作为计算列 SQL 如下所示 已清理并匿名 因此如果出现错误
  • Django模型继承和管理系统

    我正在尝试构建一个系统来管理页面中各种类型的内容 例如 页面可以具有文本内容 超链接内容 视频内容等 在我的建模代码中 我有一个基类 class ContentItem models Model title models CharField
  • TypeError: 恰好需要 1 个参数(给定 2 个)Python 错误

    问题是我有这个功能 def fuerza repulsion x area 100 100 k math sqrt area len self grafo 0 return k 2 x c2 在这一行中 f mod self fuerza
  • 指定数据库时出现身份验证失败错误

    我正在尝试连接到mongodb来自我的网络应用程序 但是 我得到一个auth failed error当我指定要连接的数据库时 来自 mongo 如果我不指定数据库 则连接成功 我已经检查了拼写以及数据库是否使用 mongo 命令行退出sh
  • Pandas(python):列中的最大值定义新列中的新值

    我有一个大约有 50 列的 df Product ID Cat1 Cat2 Cat3 other columns 8937456 0 5 10 8497534 25 3 0 8754392 4 15 7 Cat 表示该产品有多少数量属于某个
  • 如何使用 RSA256 在 PHP 中创建有效的 JWT

    我也遇到过类似的困难question https stackoverflow com questions 35314629 how to sign data in php with hmac using a pem private key
  • 如何使用 dependency.yml 引用本地模块?

    如何使用引用本地模块依赖 yml 我想我会放弃这个问题 因为它定期在论坛中显示 答案应该如下 采用以下应用程序层次结构 myplayapp myfirstmodule mysecondmodule 我正在使用运行我的应用程序播放运行 myp
  • 如何通过引用分配lua变量

    如何在Lua中通过引用将一个变量分配给另一个变量 例如 想要执行相当于 a b 的操作 其中 a 将是指向 b 的指针 背景 有一个案例 我实际上有这样的事情 local a b c d e f g lots of variables if
  • 从容器重建 docker run 命令参数

    最好的重建方法是什么docker run来自现有 docker 容器的命令参数 我可以用docker inspect并使用在那里找到的信息 还有更好的办法吗 不是很容易 但是您可以通过格式化输出来做到这一点docker inspect 对于
  • C++ list::sort 的自定义比较函数

    您好 我在编译一段简单的代码时遇到问题 我正在创建一个实现一副纸牌的类 并且我想使用 list short 方法创建一个洗牌方法 相关代码 deck h ifndef DECK H define DECK H include
  • Git:配置 difftool 和 mergetool 的模式

    在 Mercurial 中 可以为外部差异和合并工具定义一种模式 以便仅针对与指定模式匹配的文件调用它们 diff patterns ext difftool merge patterns ext mergetool 如何在 Git 中定义
  • HasDbFunction,没有映射实体类的表值函数

    到目前为止我发现的所有内容 如果您调用表值函数 则返回值必须是 IQueryable 例如 public IQueryable
  • 使用 Scapy 与 C 套接字对话

    我有一个 UDP 连接并在端口 localhost 上侦听 并且我也尝试从 localhost 发送 Scapy 数据包 由于某种原因 我的 C 代码从未真正捕获数据包 但我可以很好地看到数据包显示在 Wireshark 中 我已经有一段时
  • 浮点计算使用 float 给出的结果与使用 double 给出的结果不同

    我有以下代码行 hero gt onBeingHit ENEMY ATTACK POINT 1 0 hero gt getDefensePercent void onBeingHit int decHP 方法接受整数并更新健康点 float
  • WPF ListView 在处理大数据时性能非常差

    我在 WPF 中使用 ListView 时遇到性能非常差的情况 使用大约 30000 条记录 据我所知 虚拟化应该打开 因为这是默认设置 我什至在 XAML 中显式打开它 表现不佳的表现是这样的 初始绑定非常慢 几分钟 滚动速度非常慢 超过
  • 单击将中止进一步执行,因为 Python 3 被配置为使用 ASCII 作为环境编码

    我将 Quokka Python Flask CMS 下载到 CentOS7 服务器上 通过命令一切正常 sudo python3 manage py runserver host 0 0 0 0 port 80 然后我创建一个文件 etc
  • 根据屏幕尺寸更改标签尺寸 xamarin 表单

    我正在使用 xamarin forms 来做一个应用程序 我发现使用相同的大小来向 ios 设备发送字母是行不通的 在 7 plus 上它运行良好 但在 iphone 5 上它对于屏幕大小来说是一个大字母 有人知道如何制作具有可缩放尺寸的字
  • 如何让javascript忽略转义(\)字符? [复制]

    这个问题在这里已经有答案了 qAnswersR 90430 qAnswersR 90430 push math k frac x 20 2 256 frac y 15 2 81 1 math 我需要将值放入变量中 但是当我像这样 conso
  • rtrim 函数不适用于结尾 h 字母[重复]

    这个问题在这里已经有答案了 file refinish php folder rtrim file php echo folder refinis 哪里结束h 我尝试了其他一些结尾字母 没关系 rtrim 不会删除您在第二个参数中指定的字符
  • x86-64 汇编的性能优化 - 对齐和分支预测

    我目前正在编写一些 C99 标准库字符串函数的高度优化版本 例如strlen memset 等 使用带有 SSE 2 指令的 x86 64 汇编 到目前为止 我已经在性能方面取得了出色的结果 但当我尝试进一步优化时 有时会出现奇怪的行为 例