assembly - 可以在不使用通用寄存器的情况下将 8 位从 XMM 寄存器移动到内存吗?
问题描述
我需要在不使用通用寄存器的情况下将 1 个字节从 xmm 寄存器移动到内存。而且我也不能使用 SSE4.1。有可能的?
=(
解决方案
通常,您首先要避免这种情况。例如,您可以进行更广泛的加载和合并(pand/pandn/por
如果没有的话pblendvb
),而不是进行单独的字节存储,然后将合并结果存储回来吗?
这不是线程安全的(未修改字节的非原子 RMW),但只要您知道要 RMW 的字节不会超出数组或结构的末尾,并且没有其他线程在做同样的事情对于同一数组/结构中的其他元素,这是执行诸如字符串中每个小写字母大写之类的常规方法,同时保留其他字节不变。
单 uop 存储只能来自 4、8、16、32 或 64 字节大小的向量寄存器,但仅 1 个未屏蔽元素的AVX-512BW 屏蔽存储除外。更窄的存储,如pextrb
涉及一个 shuffle uop 以提取要存储的 2 或 1 个字节。
在没有 GP 整数 regs 的情况下真正存储 1 个字节的唯一好方法是使用SSE4.1pextrb [mem], xmm0, 0..15
。0
即使在当前 CPU 上立即运行,这仍然是一个 shuffle + store 。如果您可以在目标位置安全地写入 2 个字节,则 SSE2pextrw
是可用的。
您可以使用SSE2maskmovdqu
字节掩码存储(带有0xff,0,0,...
掩码),但您不想这样做,因为它比movd eax, xmm0
/慢得多mov [mem], al
。例如,在 Skylake 上,10 微指令,每 6 个循环吞吐量 1 个。
如果您想在之后重新加载字节,情况会更糟,因为(与 AVX / AVX-512 屏蔽存储不同)maskmovdqu
具有 NT 语义,例如movntps
(绕过缓存,或者如果之前很热则驱逐缓存行)。
如果您的要求完全是人为的,并且您只想玩愚蠢的计算机技巧(避免将数据保存在寄存器中),您还可以在堆栈上设置暂存空间并使用movsb
它来复制它:
;; with destination address already in RDI
lea rsi, [rsp-4] ; scratch space in the red zone below RSP on non-Windows
movd [rsi], xmm0
movsb ; copy a byte, [rdi] <- [rsi], incrementing RSI and RDI
这显然比正常方式慢,并且需要一个额外的寄存器 (RSI) 用于 tmp 缓冲区地址。而且您需要 RDI 中的确切目标地址,而不是[rel foo]
静态存储或任何其他灵活的寻址模式。
pop
也可以复制 mem-to-mem,但仅适用于 16 位和 64 位操作数大小,因此无法省去您对 RSI 和 RDI 的需求。
由于上述方式需要一个额外的寄存器,因此几乎在所有方面都比正常方式更糟糕:
movd esi, xmm0 ; pick any register.
mov [rdi], sil ; al..dl would avoid needing a REX prefix for low-8
;; or even use a register where you can read the low and high bytes separately
movd eax, xmm0
mov [rdi], al ; no REX prefix needed, more compact than SIL
mov [rsi], ah ; scatter two bytes reasonably efficiently
shr eax, 16 ; bring down the next 2 bytes
(在当前的 Intel CPU 上读取 AH 有一个额外的延迟周期,但这对于吞吐量来说很好,而且我们无论如何都在这里存储,所以延迟并不是一个很大的因素。)
xmm -> GP 整数传输在大多数 CPU 上并不慢。(Bulldozer 系列是异常值,但它仍然与存储/重新加载的延迟相当;Agner Fog 在他的微架构指南(https://agner.org/optimize/)中说,他发现 AMD 的优化手册建议存储/重新加载不是快点。)
很难想象movsb
会有更好的情况,因为您已经需要一个免费的注册器,并且movsb
是多个微指令。movd r32, xmm
如果当前英特尔 CPU上的端口 0 uops 出现瓶颈,可能会出现瓶颈?( https://uops.info/ )
推荐阅读
- agens-graph - 如何中止 AgnsGraph 上的失败语句?
- r - 有条件地在 R 中汇总日期
- javascript - 需要自动生成字母数字键
- scala - 在 Scala 中将 Struct 数据类型转换为 Map 数据类型
- javascript - 如何在 for-each 函数中创建/打印没有输入限制的用户定义(输入)数组
- saml - SimpleSAMLphp:是否可以使用现有的身份验证服务进行身份验证?
- python - 使用opencv python从二进制图像中删除小白点
- android - 折叠工具栏标题重力未设置为底部
- google-api - Google drive apisharingRateLimitExceeded 错误,当尝试使用 google 权限 api 更改驱动器文件时
- terraform - 如何处理损坏的 terraform tfstate 文件