当前的 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# System.Commandline 行为?

    在我的控制台应用程序中 当未提供控制台参数时 将执行我指定列表 在本例中为参数 3 的任何处理程序 调用该处理程序时 布尔参数设置为 false 但对我来说 根本不调用它更有意义 如何防止这种情况发生并显示帮助文本 using System
  • 注销租约抛出 InvalidOperationException

    我有一个使用插件的应用程序 我在另一个应用程序域中加载插件 我使用 RemoteHandle 类http www pocketsilicon com post Things That Make My Life Hell Part 1 App
  • 确保 StreamReader 不会挂起等待数据

    下面的代码读取从 tcp 客户端流读取的所有内容 并且在下一次迭代中它将仅位于 Read 上 我假设正在等待数据 我如何确保它不会在没有任何内容可供读取时返回 我是否必须设置低超时 并在失败时响应异常 或者有更好的办法吗 TcpClient
  • 提交后禁用按钮

    当用户提交付款表单并且发布表单的代码导致 Firefox 中出现重复发布时 我试图禁用按钮 去掉代码就不会出现这个问题 在firefox以外的任何浏览器中也不会出现这个问题 知道如何防止双重帖子吗 System Text StringBui
  • MVC 在布局代码之前执行视图代码并破坏我的脚本顺序

    我正在尝试将所有 javascript 包含内容移至页面底部 我正在将 MVC 与 Razor 一起使用 我编写了一个辅助方法来注册脚本 它按注册顺序保留脚本 并排除重复的内容 Html RegisterScript scripts som
  • Nasm 点状标签

    我对 TASM 很熟悉 但对 NASM 不太了解 我读过 NASM 允许使用本地标签 这些标签在名称前用点表示 例如 代码 loop some code jmp loop 定义一个名为 loop的局部标号 引用的地址在后面的jmp指令中使用
  • 如何创建包含 IPv4 地址的文本框? [复制]

    这个问题在这里已经有答案了 如何制作一个这样的文本框 我想所有的用户都见过这个并且知道它的功能 您可以使用带有 Mask 的 MaskedTestBox000 000 000 000 欲了解更多信息 请参阅文档 http msdn micr
  • 如何检测表单的任何控件的变化?

    如何检测 C 中表单的任何控件的更改 由于我在一个表单上有许多控件 并且如果表单中的任何控件值发生更改 我需要禁用按钮 我正在寻找一些内置函数 事件处理程序 属性 并且不想为此创建自定义函数 不 我不知道任何时候都会触发任何事件any控制表
  • 如何在 Xaml 文本中添加电子邮件链接?

    我在 Windows Phone 8 应用程序中有一些大文本 我希望其中有电子邮件链接 例如 mailto 功能 这是代码的一部分
  • 使用自定义堆的类似 malloc 的函数

    如果我希望使用自定义预分配堆构造类似 malloc 的功能 那么 C 中最好的方法是什么 我的具体问题是 我有一个可映射 类似内存 的设备 已将其放入我的地址空间中 但我需要获得一种更灵活的方式来使用该内存来存储将随着时间的推移分配和释放的
  • 如何禁用 fread() 中的缓冲?

    我正在使用 fread 和 fwrite 读取和写入套接字 我相信这些函数用于缓冲输入和输出 有什么方法可以在仍然使用这些功能的同时禁用缓冲吗 Edit 我正在构建一个远程桌面应用程序 远程客户端似乎 落后于服务器 我不知道可能是什么原因
  • C# 中的合并运算符?

    我想我记得看到过类似的东西 三元运算符 http msdn microsoft com en us library ty67wk28 28VS 80 29 aspx在 C 中 它只有两部分 如果变量值不为空 则返回变量值 如果为空 则返回默
  • AES 128 CBC 蒙特卡罗测试

    我正在 AES 128 CBC 上执行 MCT 如中所述http csrc nist gov groups STM cavp documents aes AESAVS pdf http csrc nist gov groups STM ca
  • 如何设置 log4net 每天将我的文件记录到不同的文件夹中?

    我想将每天的所有日志保存在名为 YYYYMMdd 的文件夹中 log4net 应该根据系统日期时间处理创建新文件夹 我如何设置它 我想将一天中的所有日志保存到 n 个 1MB 的文件中 我不想重写旧文件 但想真正拥有一天中的所有日志 我该如
  • 动态添加 ASP.Net 控件

    我有一个存储过程 它根据数据库中存储的记录数返回多行 现在我想有一种方法来创建 div 带有包含该行值的控件的标记 如果从数据库返回 10 行 则 10 div 必须创建标签 我有下面的代码来从数据库中获取结果 但我不知道如何从这里继续 S
  • Cmake 链接共享库:包含库中的头文件时“没有这样的文件或目录”

    我正在学习使用 CMake 构建库 构建库的代码结构如下 include Test hpp ITest hpp interface src Test cpp ITest cpp 在 CMakeLists txt 中 我用来构建库的句子是 f
  • 使用 C# 读取 Soap 消息

  • 不同类型指针之间的减法[重复]

    这个问题在这里已经有答案了 我试图找到两个变量之间的内存距离 具体来说 我需要找到 char 数组和 int 之间的距离 char data 5 int a 0 printf p n p n data 5 a long int distan
  • 方法优化 - C#

    我开发了一种方法 允许我通过参数传入表 字符串 列数组 字符串 和值数组 对象 然后使用这些参数创建参数化查询 虽然它工作得很好 但代码的长度以及多个 for 循环散发出一种代码味道 特别是我觉得我用来在列和值之间插入逗号的方法可以用不同的
  • System.IO.FileNotFoundException:找不到网络路径。在 Windows 7 上使用 DirectoryEntry 对象时出现异常

    我正在尝试使用 DirectoryEntry 对象连接到远程 Windows 7 计算机 这是我的代码 DirectoryEntry obDirEntry new DirectoryEntry WinNT hostName hostName

随机推荐

  • 如何修复 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