当前的 C++ 编译器是否曾发出“rep movsb/w/d”?

2023-11-22

This question让我想知道,当前的现代编译器是否曾经发出过REP MOVSB/W/D操作说明。

基于此讨论,似乎使用REP MOVSB/W/D对当前的 CPU 可能会有好处。

但无论我如何尝试,我都无法使任何当前的编译器(GCC 8、Clang 7、MSVC 2017 和 ICC 18)发出此指令。

对于这个简单的代码,发出可能是合理的REP MOVSB:

void fn(char *dst, const char *src, int l) {
    for (int i=0; i<l; i++) {
        dst[i] = src[i];
    }
}

但是编译器会发出一个未经优化的简单字节复制循环,或者一个巨大的展开循环(基本上是一个内联循环)memmove)。有编译器使用这个指令吗?


GCC 具有 x86 调整选项来控制字符串操作策略以及何时内联与库调用。 (看https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html). -mmemcpy-strategy=strategy takes alg:max_size:dest_align三胞胎,但蛮力方法是-mstringop-strategy=rep_byte

我不得不使用__restrict让 gcc 识别 memcpy 模式,而不是在重叠检查/回退到哑字节循环后进行正常的自动矢量化。 (有趣的事实:gcc -O3 即使使用-mno-sse,使用整数寄存器的全宽度。所以如果你编译的话你只会得到一个愚蠢的字节循环-Os(优化尺寸)或-O2(低于完全优化))。

请注意,如果 src 和 dst 重叠dst > src,结果是not memmove。相反,您将得到长度=的重复模式dst-src. rep movsb即使在重叠的情况下,也必须正确实现确切的字节复制语义,因此它仍然有效(但在当前的 CPU 上速度很慢:我认为微代码只会退回到字节循环)。

gcc 只能到达rep movsb通过识别一个memcpy模式,然后选择内联 memcpy 作为rep movsb.它不会直接从字节复制循环转到rep movsb,这就是为什么可能的混叠会导致优化失效。 (这可能很有趣-Os考虑使用rep movs但是,当别名分析无法证明它是 memcpy 或 memmove 时,在具有快速的 CPU 上直接进行rep movsb.)

void fn(char *__restrict dst, const char *__restrict src, int l) {
    for (int i=0; i<l; i++) {
        dst[i] = src[i];
    }
}

这可能不应该“计数”,因为我可能会not推荐这些调整选项用于除“让编译器使用rep movs”,所以它与内在函数没有什么不同。我没有检查所有-mtune=silvermont / -mtune=skylake / -mtune=bdver2(推土机版本 2 = 打桩机)/等等调整选项,但我怀疑其中任何一个都可以实现这一点。所以这是一个不切实际的测试,因为没有人使用-march=native会得到这个代码生成。

但是上面的C编译与 gcc8.1-xc -O3 -Wall -mstringop-strategy=rep_byte -minline-all-stringops在 Godbolt 编译器资源管理器上将此 asm 用于 x86-64 System V:

fn:
        test    edx, edx
        jle     .L1               # rep movs treats the counter as unsigned, but the source uses signed
        sub     edx, 1            # what the heck, gcc?  mov ecx,edx would be too easy?
        lea     ecx, [rdx+1]

        rep movsb                 # dst=rdi and src=rsi
.L1:                              # matching the calling convention
        ret

有趣的事实:x86-64 SysV 调用约定针对内联进行了优化rep movs不是巧合(为什么 Windows64 使用与 x86-64 上所有其他操作系统不同的调用约定?)。我认为 gcc 在设计调用约定时倾向于这样做,因此它保存了指令。

rep_8byte does a bunch设置来处理不是8的倍数的计数,也许还有对齐,我没有仔细看。

我也没有检查其他编译器。


内联rep movsb如果没有对齐保证,这将是一个糟糕的选择,因此编译器默认情况下不这样做是件好事。 (只要他们这样做某物更好的。) Intel优化手册有一节介绍了带有 SIMD 向量的 memcpy 和 memset 与 SIMD 向量对比。rep movs。也可以看看http://agner.org/optimize/,以及其他性能链接x86 标签维基.

(我怀疑如果你这样做的话 gcc 会做任何不同的事情dst=__builtin_assume_aligned(dst, 64);或者任何其他与编译器通信对齐的方式。例如alignas(64)在某些阵列上。)

英特尔的 IceLake 微架构将具有“短重复”功能,可能会减少启动开销rep movs / rep stos,使它们对于小计数更有用。 (现在rep字符串微码具有显着的启动开销:REP 执行什么设置?)


memmove / memcpy 策略:

顺便说一句,glibc 的 memcpy 对小输入使用了一个非常好的策略,该策略对重叠不敏感:两个加载 -> 两个可能重叠的存储,最多复制 2 个寄存器宽。例如,这意味着来自 4..7 字节的任何输入都会以相同的方式分支。

Glibc 的 asm 源代码有一个很好的评论描述了该策略:https://code.woboq.org/userspace/glibc/sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S.html#19.

对于大输入,它使用 SSE XMM 寄存器、AVX YMM 寄存器或rep movsb(在检查 glibc 初始化自身时基于 CPU 检测设置的内部配置变量之后)。我不确定它实际会使用哪些 CPUrep movsb开启(如果有),但支持将其用于大型副本。


rep movsb对于小代码大小和不可怕的缩放,对于像这样的字节循环的计数来说,可能是一个相当合理的选择,对不太可能发生的重叠情况进行安全处理。

不过,在当前的 CPU 上,将微代码用于通常较小的副本时,微代码启动开销是一个大问题。

如果当前 CPU 上的平均副本大小可能为 8 到 16 字节,并且/或不同的计数导致分支预测错误很多,那么它可能比字节循环更好。它不是good,但还好。

某种最后的窥视孔优化,用于将字节循环变成rep movsb如果在不进行自动矢量化的情况下进行编译,这可能是一个好主意。(或者对于像 MSVC 这样的编译器,即使在完全优化的情况下也会产生字节循环。)

如果编译器更直接地了解它并考虑将它用于-Os使用增强型 Rep Movs/Stos Byte (ERMSB) 功能调整 CPU 时(针对代码大小进行优化而不是针对速度进行优化)。 (也可以看看memcpy 的增强型 REP MOVSB有关 x86 内存带宽单线程与所有核心、避免 RFO 的 NT 存储的许多好东西,以及rep movs使用避免 RFO 的缓存协议...)。

在较旧的 CPU 上,rep movsb对于大型副本来说不太好,所以推荐的策略是rep movsd or movsq对最后几个计数进行特殊处理。 (假设你要使用rep movs根本,例如在内核代码中,您无法触及 SIMD 向量寄存器。)

The -mno-sse使用整数寄存器的自动向量化比rep movs对于在 L1d 或 L2 缓存中很热的中型副本,因此 gcc 绝对应该使用rep movsb or rep movsq检查重叠后,不是 qword 复制循环,除非它期望小输入(如 64 字节)是常见的。


字节循环的唯一优点是代码尺寸小;它几乎是桶的底部;对于较小但未知的副本大小,像 glibc 这样的智能策略会更好。但是内联的代码太多了,并且函数调用确实有一些成本(溢出调用破坏的寄存器并破坏红色区域,加上call / ret指令和动态链接间接)。

特别是在不经常运行的“冷”函数中(因此您不想在其上花费大量代码大小,增加程序的 I-cache 占用空间、TLB 局部性、从磁盘加载的页面等) 。如果手动编写汇编,您通常会更多地了解预期的大小分布,并且能够内联快速路径并回退到其他内容。

请记住,编译器将对一个程序中可能存在的多个循环做出决定,并且大多数程序中的大多数代码都在热循环之外。它不应该让它们全部膨胀。这就是为什么 gcc 默认为-fno-unroll-loops除非启用了配置文件引导优化。 (自动矢量化启用于-O3但是,并且可以为像这样的一些小循环创建大量代码。这是非常愚蠢的,gcc 在循环序言/结尾处花费了大量的代码大小,但在实际循环上花费了很少的代码大小;据它所知,每次外部代码运行时,循环都会运行数百万次迭代。)

不幸的是,gcc 的自动矢量化代码并不非常高效或紧凑。它在 16 字节 SSE 情况的循环清理代码上花费了大量代码(完全展开 15 字节副本)。使用 32 字节 AVX 向量,我们得到一个汇总字节loop来处理剩余的元素。 (对于 17 字节副本,这与 1 XMM 向量 + 1 字节或 glibc 样式重叠 16 字节副本相比非常糟糕)。对于 gcc7 及更早版本,它会执行相同的完全展开,直到对齐边界作为循环序言,因此它会膨胀两倍。

IDK 如果配置文件引导优化可以优化 gcc 的策略,例如当每次调用的计数都很小时,倾向于更小/更简单的代码,因此无法达到自动向量化代码。或者,如果代码“冷”并且每次运行整个程序只运行一次或根本不运行,则更改策略。或者,如果计数通常为 16 或 24 等,则最后一个的标量n % 32bytes 很糟糕,所以理想情况下 PGO 会将其转换为特殊情况下较小的计数。 (但我不太乐观。)

我可能会为此报告一个 GCC 错过优化的错误,即在重叠检查后检测 memcpy,而不是完全将其留给自动矢量化器。和/或关于使用rep movs for -Os,也许与-mtune=icelake如果有关于该 uarch 的更多信息。

很多软件只用编译-O2,所以有一个窥视孔rep movs除了自动矢量化器之外,其他功能也可能有所不同。 (但问题是是正差还是负差)!

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

当前的 C++ 编译器是否曾发出“rep movsb/w/d”? 的相关文章

  • C# 创建函数队列

    我写了一个名为 QueueManager 的类 class QueueManager Queue functionsQueue public bool IsEmpty get if functionsQueue Count 0 return
  • 如何从RichTextBox中获取显示的文本?

    如何获得显示的RichTextBox 中的文本 我的意思是 如果 RichTextBox 滚动到末尾 我只想接收那些对我来说可见的行 P S 获得第一个显示的字符串就足够了 您想使用 RichTextBox GetCharIndexFrom
  • 为什么 VB.NET 和 C# 中针对值检查 null 存在差异?

    In VB NET http en wikipedia org wiki Visual Basic NET有时候是这样的 Dim x As System Nullable Of Decimal Nothing Dim y As System
  • 如何将十六进制字符串转换为十六进制数字[重复]

    这个问题在这里已经有答案了 可能的重复 如何将十六进制字符串转换为有符号整数 https stackoverflow com questions 3705429 how do i convert hex string into signed
  • 键盘加速器在 UWP 应用中停止工作

    我正在尝试将键盘加速器添加到 UWP 应用程序中的 CommandBar 菜单项 当应用程序启动时 这工作正常 但在我第一次打开溢出菜单后 加速器停止工作 这似乎不会发生在主要命令 菜单之外 上 只有溢出菜单内的辅助命令才会发生 此外 单击
  • 使用API​​隐藏程序标题栏

    它可以使用 c 和 windows api 删除窗口控制台标题栏 如果是的话如何 请 这个简单的应用程序隐藏并显示其所在控制台的标题栏 它会立即将控制台标题更改为 guid 以查找窗口句柄 然后 它使用 ToggleTitleBar 使用找
  • 存储过程上的 OdbcCommand - 输出参数上出现“未提供参数”错误

    我正在尝试执行存储过程 通过 ODBC 驱动程序针对 SQL Server 2005 但收到以下错误 过程或函数 GetNodeID 需要参数 ID 但未提供该参数 ID 是我的过程的 OUTPUT 参数 在存储过程中指定了一个输入 mac
  • 在 MATLAB 中创建共享库

    一位研究人员在 MATLAB 中创建了一个小型仿真 我们希望其他人也能使用它 我的计划是进行模拟 清理一些东西并将其变成一组函数 然后我打算将其编译成C库并使用SWIG https en wikipedia org wiki SWIG创建一
  • C# datagridview 列转入数组

    我正在用 C 构建一个程序 并在其中包含一个 datagridview 组件 datagridview 有固定数量的列 2 我想将其保存到两个单独的数组中 但行数确实发生了变化 我怎么能这样做呢 假设一个名为 dataGridView1 的
  • 指向字节数组的指针

    由于 Misra C 的要求 我的一位同事想要使用指针声明 但我遇到了一些问题 Misra 安全关键指南 不会让我们纯粹的程序员使用指针 但会让我们对数组字节进行操作 他打算获取一个指向字节数组的指针 因此我们不会在堆栈上传递实际的数组 T
  • 操纵 setter 以避免 null

    通常我们有 public string code get set 如果最终有人将代码设置为 null 我需要避免空引用异常 我尝试这个想法 有什么帮助吗 public string code get set if code null cod
  • 更改 IdentityServer4 实体框架表名称

    我正在尝试更改由 IdentityServer4 的 PersistedGrantDb 和 ConfigurationDb 创建的默认表名称 并让实体框架生成正确的 SQL 例如 而不是使用实体IdentityServer4 EntityF
  • 错误左值需要作为赋值C++的左操作数

    整个程序基本上只允许用户移动光标 如果用户位于给定的坐标范围 2 2 内 则允许用户键入输入 我刚刚提供了一些我认为足以解决问题的代码 我不知道是什么导致了这个问题 你能解释一下为什么会发生吗 void goToXY int int 创建一
  • 不兼容的类型 - 是因为数组已经是指针吗?

    在下面的代码中 我创建一个基于书籍结构的对象 并让它保存多个 书籍 我设置的是一个数组 即定义 启动的对象 然而 每当我去测试我对指针的了解 实践有帮助 并尝试创建一个指向创建的对象的指针时 它都会给我错误 C Users Justin D
  • 从 C 线程调用 Python 代码

    我对从 C 或 C 线程调用 Python 代码时如何确保线程安全感到非常困惑 The Python 文档 http docs python org c api init html non python created threads似乎是
  • 在 C++ 和 Windows 中使用 XmlRpc

    我需要在 Windows 平台上使用 C 中的 XmlRpc 尽管我的朋友向我保证 XmlRpc 是一种 广泛可用的标准技术 但可用的库并不多 事实上 我只找到一个库可以在 Windows 上执行此操作 另外一个库声称 您必须做很多工作才能
  • C++ [Windows] 可执行文件所在文件夹的路径[重复]

    这个问题在这里已经有答案了 我需要访问一些文件fstream在我的 Windows 上的 C 应用程序中 这些文件都位于我的exe文件所在文件夹的子文件夹中 获取当前可执行文件的文件夹路径的最简单且更重要的 最安全的方法是什么 Use 获取
  • C# 粘贴到文本框时检查剪贴板中的字符

    有没有一些方法可以在粘贴到文本框 C 之前仅检查剪贴板中的字符 Ctrl V 和右键单击 gt 粘贴 但不使用 MaskedTextbox 在文本框文本更改中添加规则以仅接受数字 例如 private string value privat
  • 启动画面后主窗口出现在其他窗口后面

    我有一个带有启动屏幕的 Windows 窗体应用程序 当我运行该应用程序时 启动屏幕显示正常 消失并加载应用程序的主窗体 但是 当我加载主窗体时 它出现在包含该应用程序的 Windows 资源管理器目录下 这是运行启动画面然后运行主窗体的代
  • 无法使 Polly 超时策略覆盖 HttpClient 默认超时

    我正在使用 Polly 重试策略 并且正如预期的那样 在重试过程中HttpClient达到 100 秒超时 我尝试了几种不同的方法来合并 Polly 超时策略 将超时移至每次重试而不是总计 但 100 秒超时仍然会触发 我读过大约 5 个

随机推荐

  • 如何修复 EF Core 迁移“证书链由不受信任的机构颁发”

    连接字符串 Data Source
  • 如何最好地告诉 CMake 在哪里可以找到 dll

    我有一个简单的项目结构 源自令人惊叹的教程 https rix0r nl blog 2015 08 13 cmake guide 它看起来如下 src CMakeLists txt mylib include mylib mylibclas
  • 从 Android 应用程序打开本机浏览器

    我有一部安装了多个浏览器的 Android 手机 我可能会或可能不会将浏览器设置为默认值 所以 我的问题是 从我的应用程序中 如何仅在NATIVE安卓浏览器 有没有办法知道是否有浏览器设置为默认浏览器 从我的应用程序中 如何仅在 NATIV
  • 这是使用 History.js 的正确方法吗?

    我能够使用三个链接来组合一个简化的完整 History js 示例 从整个页面加载内容片段 而无需更新页面 同时更新浏览器历史记录 这是相关的代码片段 完整的工作示例在这里http jsfiddle net PT7qx show a hre
  • .NET MAUI 应用程序上 iOS 上覆盖表单元素的键盘

    我正在将我的 Xamarin 应用程序转换为 NET MAUI 看起来像
  • 如何在sequelize中实现多对多关联

    我有两个表 书籍和文章 它们之间具有多对多关系 连接表是 BookArticles 模型 books js module exports function sequelize DataTypes return Food sequelize
  • 根据外部架构文件验证 Jackson 的 JSON 架构合规性

    我想使用杰克逊图书馆 https github com FasterXML jackson 来处理 Java 中的 JSON 文件 这些文件由 JSON 模式文件描述 现在 我想验证解析的 JSON 是否符合由其自身解析的 JSON 模式文
  • 将元素添加到具有间隙数字键的数组以形成索引数组/列表

    我有一个像这样的数组 array 0 gt Apple 2 gt Orange 5 gt Pear 8 gt Pear 有没有办法用默认值 例如 空字符串或null 我想将新元素插入到数组中的以下键 1 3 4 6 7 我的结果应该是 0
  • Scala 错误处理:尝试还是选择?

    给定 UserService 中的一个方法 update 这里处理错误 异常的最佳方法是什么 选项A def update Try User 这样 我需要定义我的自定义例外并在需要时将它们扔到函数体内 这些异常大多数是业务错误 例如 use
  • 使用静态用户登录 Flask 总是会产生 401-未经授权

    我正在尝试构建一个超级简单的网络应用程序供我自己使用 我将是唯一的用户 所以我觉得不需要涉及数据库来进行用户管理 我正在尝试使用烧瓶登录 但即使我打电话给login user成功了 在重定向到带有以下内容的页面后 我仍然遇到 401 未经授
  • 如何在 TypeScript 中声明仅包含对象而不包含函数的类型

    在 TypeScript 中是否可以以某种方式定义一个类型 使其只包含对象而不包含函数 Example type T name string any How to modify this to only accepts objects co
  • Python如何在处理完类对象后释放其内存?

    I use None 关键字删除类对象 class demo class members obj demo some processing obj None 通过使用None 内存会被释放吗 我找到del也可以用于删除类对象 或者我应该使用
  • javascript 帮助中的概率?

    抱歉 我是 JS 新手 似乎无法弄清楚 我该如何做概率 我完全不知道 但我想做点什么 100 的机会 也许 0 7 的机会执行函数e 和 30 的机会执行函数d 等等 它们将精确地加起来 100 每个函数都有不同的函数 但我还没有弄清楚如何
  • 模拟实体框架数据库

    我正在使用实体框架 4 是否可以使用一些随机数据从模式自动创建一些 MOCK 数据库 在整数的位置 放置整数 在字符串的位置 放置一些 GUID 或其他 这对我调试我的应用程序有很大帮助 谢谢你 詹姆斯 我发现这个很棒的工具与 EF 4 一
  • DotNetOpenAuth 获取 Facebook 电子邮件地址

    我有以下代码 它获取名字 姓氏 我意识到电子邮件是扩展权限 但我需要修改什么才能请求扩展权限 如何通过以下方式获取经过身份验证的 Facebook 用户的电子邮件DotNetOpenAuth fbClient new FacebookCli
  • 是否可以配置 UITableView 以允许多重选择?

    对于iPhone 是否可以配置UITableView以允许多重选择 我尝试过覆盖 setSelected animated 对于每个 UITableViewCell 但尝试捏造所需的行为是很棘手的 因为很难将真正的取消选择与 UITable
  • PHP7.1 json_encode() 浮点问题

    这不是一个问题 因为它更多的是一个意识 我更新了一个使用的应用程序json encode 到 PHP7 1 1 我发现浮点数有时会扩展到 17 位数字的问题 根据文档 PHP 7 1 x开始使用serialize precision而不是编
  • Android,如何在旋转设备时不破坏活动?

    我有一个仅在纵向模式下工作的应用程序 并且我已在清单文件中将每个活动的方向更改为纵向 但是当我旋转设备时 活动会再次重新创建 如何才能不破坏活动呢 For API 12 及以下 add android configChanges orien
  • 如何将大型 AngularJS 项目拆分为模块

    我来自 Backbone 和 JavascriptMVC 的世界 但我真的很想切换到 AngularJS 到目前为止 我有一个大问题阻止我转变 我创建单页应用程序 假设它包含一个选项卡模块 一个文件上传模块和一个文件列表模块 我在 Back
  • 当前的 C++ 编译器是否曾发出“rep movsb/w/d”?

    This question让我想知道 当前的现代编译器是否曾经发出过REP MOVSB W D操作说明 基于此讨论 似乎使用REP MOVSB W D对当前的 CPU 可能会有好处 但无论我如何尝试 我都无法使任何当前的编译器 GCC 8