【问题标题】:How can the rep stosb instruction execute faster than the equivalent loop?rep stosb 指令如何比等效循环执行得更快?
【发布时间】:2016-02-02 12:51:43
【问题描述】:

指令rep stosb 怎么能比这段代码执行得更快?

    Clear: mov byte [edi],AL       ; Write the value in AL to memory
           inc edi                 ; Bump EDI to next byte in the buffer
           dec ecx                 ; Decrement ECX by one position
           jnz Clear               ; And loop again until ECX is 0

是否保证在所有现代 CPU 上都是如此?我应该总是更喜欢使用rep stosb 而不是手动编写循环吗?

【问题讨论】:

  • 你期待什么样的答案? rep stosb 恰好是为此目的优化的指令。
  • 您好 Jester 非常感谢您的及时回复。好的,我会这样说.. CPU中有一个加法器用于加法。同样对于指令“rep stosb”,CPU 中是否有单独的电路?

标签: performance assembly optimization x86 micro-optimization


【解决方案1】:

在现代 CPU 中,rep stosbrep movsb 的微编码实现实际上使用了比 1B 更宽的存储,因此它可以比每个时钟一个字节快得多。

(请注意,这适用于 stos 和 movs,不适用于 repe cmpsbrepne scasb。不幸的是,它们仍然很慢,最多 2 个周期在 Skylake 上比较了字节,这对于实现 memcmp 或 memchr 的 AVX2 vpcmpeqb 来说是可悲的。请参阅 https://agner.org/optimize/ 以获取指令表,以及 the x86 tag wiki 中的其他性能链接。

请参阅 Why is this code 6.5x slower with optimizations enabled? 以了解 gcc 不明智地内联 repnz scasb 的示例,或者对于恰好变大的 strlen 的不太糟糕的标量 bithack,以及一个简单的 SIMD 替代方案。)


rep stos/movs 具有显着的启动开销,但对于大型 memset/memcpy 的提升效果很好。 (有关何时使用 rep stos 与矢量化循环用于小缓冲区的讨论,请参阅 Intel/AMD 的优化手册。)但是,如果没有 ERMSB 功能,rep stosb 针对中小型 memset 进行了调整,并且使用 @ 是最佳选择987654341@ 或 rep stosq(如果您不打算使用 SIMD 循环)。

当使用调试器单步执行时,rep stos 只进行一次迭代(ecx/rcx 递减一次),因此微码实现永远不会进行。不要让这愚弄你以为这就是它可以做的一切。

有关英特尔 P6/SnB 系列微架构如何实现 rep movs 的一些详细信息,请参阅 What setup does REP do?

请参阅 Enhanced REP MOVSB for memcpy,了解 rep movsb 与 SSE 或 AVX 循环的内存带宽注意事项,在具有 ERMSB 功能的 Intel CPU 上。 (请特别注意,多核 Xeon CPU 不能仅用一个线程来饱和 DRAM 带宽,因为一次运行的缓存未命中数以及 RFO 与非 RFO 存储协议的限制。)


现代 Intel CPU 应该在每个时钟一次迭代中运行问题中的 asm 循环,但 AMD 推土机系列内核可能甚至无法在每个时钟上管理一个存储。 (处理 inc/dec/branch 指令的两个整数执行端口的瓶颈。如果循环条件是 edi 上的 cmp/jcc,则 AMD 内核可以宏融合比较和分支。)


所谓的快速字符串操作(英特尔 P6 和 SnB 系列 CPU 上的rep movsrep stos)的一个主要特点是,它们在存储到非先前缓存时避免了读取所有权缓存一致性流量内存。所以这就像使用 NT 存储来写入整个缓存行,但仍然是强排序的。(ERMSB 功能确实使用弱排序存储)。

IDK AMD 的实现有多好。


(还有一个更正:我之前说过英特尔 SnB 只能处理每 2 个时钟一个分支的吞吐量,但实际上它可以在每个时钟一个迭代中运行微小的循环。)

查看从 标签 wiki 链接的优化资源(尤其是 Agner Fog 的指南)。


英特尔 IvyBridge 和后来的 ERMSB,它允许 rep stos[b/w/d/q]rep movs[b/w/d/q] 使用弱排序存储(如 movnt),允许存储提交无序缓存。如果不是所有目标都在 L1 缓存中是热的,这是一个优势。我相信,从文档的措辞来看,there's an implicit memory barrier at the end of a fast string op,所以任何重新排序只在由字符串 op 生成的商店之间可见,而不是在它和其他商店之间可见。即你仍然不需要sfence after rep movs.

因此,对于英特尔 IvB 及更高版本上的大型对齐缓冲区,memsetrep stos 实现可以击败任何其他实现。使用movnt 存储(不会将数据留在缓存中)的存储也应该接近饱和的主内存写入带宽,但实际上可能跟不上。请参阅 cmets 进行讨论,但我找不到任何数字。

对于小缓冲区,不同的方法具有非常不同的开销。微基准可以使 SSE/AVX 复制循环看起来比实际更好,因为每次都以相同的大小和对齐方式进行复制可以避免启动/清理代码中的分支错误预测。 IIRC,建议在 Intel CPU 上对 128B 以下的副本使用矢量化循环(不是rep movs)。阈值可能高于此值,具体取决于 CPU 和周围的代码。

英特尔的优化手册还讨论了不同 memcpy 实现的开销,并且rep movsb 对错位的惩罚比movdqu 更大。


请参阅优化的 memset/memcpy 实现的代码,以了解有关实际操作的更多信息。 (例如 Agner Fog 的图书馆)。

【讨论】:

  • 很好的答案。实际上,我前段时间对不同的方法进行了比较,请参见此处:stackoverflow.com/questions/27940150/… 不幸的是,完整的代码已被 pastebin 删除。
  • 为什么 SNB 被限制为每两个时钟一个字节?更重要的是,我不确定您在 IVB 及以后关于rep stosmovnt 的陈述是否正确。我认为这是相反的方式。请参阅我对 whats-missing-sub-optimal-in-this-memcpy-implementation 的回答,并阅读我对我的回答的 cmets。
  • 废话,我将不得不去修复我最近的所有答案,其中包括关于 SnB 循环吞吐量的错误。
  • 从我指向您的链接中,我写道“所以您的意思是,对于我的 1 GB 机箱,我可以做得比 movntdqa 更好?”然后 Stephen Canon 写道:“是的,在 Ivybridge 和 Haswell 上流式传输到内存时,rep movsb 比 movntdqa 快得多(但请注意,在 Ivybridge 之前它很慢!)”
  • @Brendan:嗯,你是对的术语。在这个答案中,我不应该使用“快速字符串”作为 ERMSB 的同义词。是的,“快速字符串”(rep stosrep movs 的微码实现的广泛存储(但不是比较操作)可以追溯到 PPro(第一个 P6 内核,Pentium II 的祖先)。Andy Glew(英特尔的领导快速字符串架构师)corrected me on this only a week after I posted this answer,同时讨论 rep movs 的一些细节:P
【解决方案2】:

如果您的 CPU 具有 CPUID ERMSB 位,则 rep movsbrep stosb 命令的执行方式与旧处理器不同。

请参阅Intel Optimization Reference Manual,第 3.7.6 节增强的 REP MOVSB 和 REP STOSB 操作 (ERMSB)。

手册和我的测试都表明,与通用 32 位寄存器移动相比,rep stosb 在 Skylake 微架构的 32 位 CPU 上的优势仅出现在大于 128 字节的大内存块上。在较小的块上,例如 5 个字节,您显示的代码(mov byte [edi],alinc edidec ecxjnz Clear)会快得多,因为rep stosb 的启动成本非常高 - 大约 35循环。然而,在 2019 年 9 月推出的 Ice Lake 微架构上,这种速度差异已经缩小,引入了 Fast Short REP MOV (FSRM) 功能。此功能可以通过 CPUID 位进行测试。它旨在让 128 字节和更短的字符串更快,但实际上,使用 rep movsb 使用 64 字节之前的字符串仍然比使用简单的 64 位寄存器复制要慢。除此之外,FSRM 仅在 64 位下实现,而不是在 32 位下实现。至少在我的 i7-1065G7 CPU 上,rep movsb 仅适用于 64 位以下的小字符串,但在 32 位上,字符串必须至少为 4KB,rep movsb 才能开始优于其他方法。

要在具有 CPUID ERMSB 位的处理器上获得rep stosb 的好处,应满足以下条件:

  • 目标缓冲区必须与 16 字节边界对齐;
  • 如果长度是 64 的倍数,则可以产生更高的性能;
  • 方向位应设置为“向前”(由cld 指令设置)。

根据英特尔优化手册,当内存块的长度至少为 128 字节时,ERMSB 通过 Skylake 上的常规寄存器开始优于内存存储。正如我所写的,内部启动 ERMSB 很高——大约 35 个周期。当长度超过 2048 字节时,ERMSB 开始明显优于其他方法,包括 AVX 复制和填充。不过,这主要适用于 Skylake 微架构,其他 CPU 微架构不一定如此。

在某些处理器上,但在其他处理器上,当目标缓冲区为 16 字节对齐时,使用 ERMSB 的 REP STOSB 可以比 SIMD 方法执行得更好,即使用 MMX 或 SSE 寄存器时。当目标缓冲区未对齐时,对于基于英特尔微架构代号 Ivy Bridge 的处理器,使用 ERMSB 的 memset() 性能相对于对齐情况可能会降低约 20%。相比之下,根据英特尔的优化手册,当目标未对齐时,REP STOSB 的 SIMD 实现将经历更可忽略的降级。

基准测试

我做了一些基准测试。代码多次填充同一个固定大小的缓冲区,因此缓冲区停留在缓存中(L1、L2、L3),具体取决于缓冲区的大小。迭代次数是这样的,总执行时间应该是两秒左右。

天空湖

在 2015 年 9 月发布的 Intel Core i5 6600 处理器上,基于 Skylake-S 四核微架构(3.30 GHz 基本频率,3.90 GHz 最大 Turbo 频率),具有 4 x 32K L1 缓存、4 x 256K L2 缓存和 6MB L3 缓存,我可以在 REP STOSB 上获得约 100 GB/秒的 32K 块。

使用REP STOSB的memset()实现:

  • 1297920000 个 16 字节块:13.6022 秒 1455.9909 兆字节/秒
  • 0648960000 个 32 字节块:06.7840 秒 2919.3058 兆字节/秒
  • 1622400000 个 64 字节块:16.9762 秒 5833.0883 兆字节/秒
  • 817587402 个 127 字节的块:8.5698 秒 11554.8914 兆字节/秒
  • 811200000 个 128 字节块:8.5197 秒 11622.9306 兆字节/秒
  • 804911628 个 129 字节的块:9.1513 秒 10820.6427 兆字节/秒
  • 407190588 个 255 字节的块:5.4656 秒 18117.7029 兆字节/秒
  • 405600000 个 256 字节的块:5.0314 秒 19681.1544 兆字节/秒
  • 202800000 个 512 字节的块:2.7403 秒 36135.8273 兆字节/秒
  • 101400000 个 1024 字节的块:1.6704 秒 59279.5229 兆字节/秒
  • 3168750 个 32768 字节的块:0.9525 秒 103957.8488 兆字节/秒 (!),即 10 GB/秒
  • 2028000 块 51200 字节:1.5321 秒 64633.5697 兆字节/秒
  • 413878 块 250880 字节:1.7737 秒 55828.1341 兆字节/秒
  • 19805 块 5242880 字节:2.6009 秒 38073.0694 兆字节/秒

使用MOVDQA [RCX],XMM0的memset()实现:

  • 1297920000 个 16 字节块:3.5795 秒 5532.7798 兆字节/秒
  • 0648960000 个 32 字节的块:5.5538 秒 3565.9727 兆字节/秒
  • 1622400000 个 64 字节块:15.7489 秒 6287.6436 兆字节/秒
  • 817587402 个 127 字节的块:9.6637 秒 10246.9173 兆字节/秒
  • 811200000 个 128 字节块:9.6236 秒 10289.6215 兆字节/秒
  • 804911628 个 129 字节的块:9.4852 秒 10439.7473 兆字节/秒
  • 407190588 个 255 字节的块:6.6156 秒 14968.1754 兆字节/秒
  • 405600000 个 256 字节的块:6.6437 秒 14904.9230 兆字节/秒
  • 202800000 个 512 字节的块:5.0695 秒 19533.2299 兆字节/秒
  • 101400000 个 1024 字节的块:4.3506 秒 22761.0460 兆字节/秒
  • 3168750 个 32768 字节的块:3.7269 秒 26569.8145 兆字节/秒 (!) 即 26 GB/秒
  • 2028000 块 51200 字节:4.0538 秒 24427.4096 兆字节/秒
  • 413878 块 250880 字节:3.9936 秒 24795.5548 兆字节/秒
  • 19805 块 5242880 字节:4.5892 秒 21577.7860 兆字节/秒

请注意,使用 XMM0 寄存器的缺点是它是 128 位(16 字节),而我可以使用 256 位(32 字节)的 YMM0 寄存器。无论如何,stosb 使用非 RFO 协议。自 1996 年的 Pentium Pro (P6) 以来,Intel x86 就拥有“快速字符串”。P6 快速字符串采用 REP MOVSB 或更大,并使用 64 位微码加载和存储以及非 RFO 缓存协议来实现它们。与 Ivy Bridge 中的 ERMSB 不同,它们没有违反内存排序。有关详细信息和来源,请参阅https://stackoverflow.com/a/33905887/6910868

无论如何,即使您只比较我提供的两种方法,即使第二种方法远非理想,如您所见,在 64 位块上rep stosb 速度较慢,但​​从 128 字节开始块,rep stosb 开始优于其他方法,并且从 512 字节块和更长的块开始,差异非常显着,前提是您在缓存中一次又一次地清除相同的内存块。

因此,对于REP STOSB,最大速度为每秒 103957(十万三千九百五十七)兆字节,而使用 MOVDQA [RCX],XMM0 的最大速度仅为 26569(二万六千五百六十六)九)26569。

如您所见,最高性能是在 32K 块上,相当于我进行基准测试的 CPU 的 32K L1 缓存。

冰湖

REP STOSB 与 AVX-512 商店

我还在 2019 年 8 月发布的 Intel i7 1065G7 CPU(Ice Lake/Sunny Cove 微架构)上进行了测试,基本频率:1.3 GHz,最大 Turbo 频率 3.90 GHz。它支持 AVX512F 指令集。它具有 4 x 32K L1 指令缓存和 4 x 48K 数据缓存、4x512K L2 缓存和 8 MB L3 缓存。

目标对齐

在由rep stosb 归零的 32K 块上,性能从 175231 MB/s 的目标未对齐 1 字节(例如 $7FF4FDCFFFFF)迅速上升到 219464 MB/s 以对齐 64 字节(例如 $7FF4FDCFFFC0),并且然后逐渐上升到 222424 MB/秒,以 256 字节对齐的目的地(对齐到 256 字节,即 $7FF4FDCFFF00)。之后,速度并没有提高,即使目标对齐了 32KB(例如 $7FF4FDD00000),仍然是 224850 MB/秒。

rep stosbrep stosq 的速度没有差异。

在以 32K 对齐的缓冲区上,AVX-512 存储的速度与 rep stosb 的速度完全相同,对于从循环中的 2 个存储开始的循环 (227777 MB/秒) 并且对于展开的循环没有增长4 家甚至 16 家商店。但是,对于只有 1 个存储的循环,速度会稍低一些 - 203145 MB/秒。

但是,如果目标缓冲区仅错位 1 个字节,则 AVX512 存储的速度会急剧下降,即 2 倍以上,降至 93811 MB/秒,而在类似缓冲区上的 rep stosb 则为 175231 MB /秒。

缓冲区大小

  • 对于 1K(1024 字节)块,AVX-512 (205039 KB/s) 比 rep stosb (71817 MB/s) 快 3 倍
  • 对于 512 字节块,AVX-512 性能始终与较大块类型 (194181 MB/s) 相同,而 rep stosb 下降到 38682 MB/s。在这种块类型中,AVX-512 的差异是 5 倍。
  • 对于 2K (2048) 块,AVX-512 有 210696 MB/s,而对于 rep stosb,它是 123207 MB/s,几乎慢了两倍。同样,rep stosbrep stosq 之间没有区别。
  • 对于 4K (4096) 块,AVX-512 的速度为 225179 MB/s,而 rep stosb:180384 MB/s,几乎迎头赶上。
  • 对于 8K (8192) 块,AVX-512 有 222259 MB/s,而 rep stosb:194358 MB/s,接近!
  • 对于 32K (32768) 块,AVX-512 有 228432 MB/srep stosb: 220515 MB/s - 现在终于!我们正在接近我的 CPU 的 L0 数据缓存大小 - 48Kb!这是每秒 220 GB!
  • 对于 64K (65536) 块,AVX-512 有 61405 MB/s,rep stosb: 70395 MB/s!
  • 当我们用完 L0 缓存时,降幅如此巨大!而且,很明显,从这一点开始,rep stosb 的表现开始优于 AVX-512 商店。
  • 现在让我们检查 L1 缓存大小。对于 512K 块,AVX-512 达到 62907 MB/s,rep stosb 达到 70653 MB/s。这就是rep stosb 开始优于 AVX-512 的地方。差异尚不显着,但缓冲区越大,差异越大。
  • 现在让我们使用 1GB (1073741824) 的巨大缓冲区。使用 AVX-512,速度为 14319 MB/s,rep stosb 为 27412 MB/s,即 AVX-512 的两倍!

我还尝试使用非临时指令来填充 32K 缓冲区 vmovntdq [rcx], zmm31,但性能比 vmovdqa64 [rcx], zmm31 慢了大约 4 倍。在填充内存缓冲区时如何利用vmovntdq?是否应该有一些特定大小的缓冲区以便vmovntdq 发挥优势?

此外,如果目标缓冲区至少按 64 位对齐,vmovdqa64vmovdqu64 之间没有性能差异。因此,我确实有一个问题:当我们有vmovdqu64 时,指令vmovdqa64 是否仅用于调试和安全?

图 1:迭代存储到同一缓冲区的速度,MB/s

block     AVX   stosb
-----   -----  ------
 0.5K  194181   38682
   1K  205039  205039
   2K  210696  123207
   4K  225179  180384
   8K  222259  194358 
  32K  228432  220515 
  64K   61405   70395 
 512K   62907   70653 
   1G   14319   27412

缓存内多次清空同一内存块的性能总结

Ice Lake CPU 上的rep stosb 开始优于 AVX-512 存储,仅用于重复清除大于 L0 缓存大小的相同内存缓冲区,即 Intel i7 1065G7 CPU 上的 48K。在小内存缓冲区上,AVX-512 存储速度要快得多:1KB - 快 3 倍,512 字节 - 快 5 倍。

但是,AVX-512 存储区容易受到未对齐缓冲区的影响,而rep stosb 对未对齐则不那么敏感。

因此,我发现 rep stosb 开始优于 AVX-512 存储,仅在超过 L0 数据缓存大小或 48KB 的缓冲区上,如 Intel i7 1065G7 CPU。这个结论至少在 Ice Lake CPU 上是有效的。较早的英特尔建议,字符串复制从 2KB 缓冲区开始优于 AVX 复制,也应该针对较新的微架构重新测试。

清空不同的内存缓冲区,每个只用一次

我之前的基准测试是连续多次填充同一个缓冲区。更好的基准可能是分配许多不同的缓冲区,并且每个缓冲区只填充一次,以免干扰缓存。

在这种情况下,rep stosb 和 AVX-512 商店之间没有太大区别。唯一的区别是,在 Windows 10 64 位下,所有数据都没有接近物理内存限制。在以下基准测试中,总数据大小低于 8 GB,总物理内存为 16 GB。当我分配大约 12 GB 时,无论使用哪种方法,性能都会下降大约 20 倍。 Windows 开始丢弃已清除的内存页面,并且可能在内存快满时做了一些其他的事情。 i7 1065G7 CPU 上 8MB 的 L3 高速缓存大小似乎与基准测试无关。重要的是您不必接近物理内存限制,这取决于您的操作系统如何处理这种情况。正如我所说,在 Windows 10 下,如果我只占用一半的物理内存,那还可以,但我占用了 3/4 的可用内存,我的基准测试速度减慢了 20 倍。我什至没有尝试超过 3/4。正如我所说,总内存大小为 16 GB。根据任务管理器的说法,可用容量为 12 GB。

这是在具有 16 GB 总内存的 i7 1065G7 CPU 上填充总计 8 GB 的各种内存块的速度基准测试(以 MB/秒为单位),总内存为 16 GB,单线程。 “AVX”是指“AVX-512”普通商店,“s​​tosb”是指“rep stosb”。

图 2:存储到多个缓冲区的速度,每个缓冲区一次,MB/s

block    AVX  stosb
-----   ----   ----
 0.5K   3641   2759
   1K   4709   3963
   2K  12133  13163
   4K   8239  10295
   8K   3534   4675
  16K   3396   3242
  32K   3738   3581
  64K   2953   3006
 128K   3150   2857
 256K   3773   3914
 512K   3204   3680
1024K   3897   4593
2048K   4379   3234
4096K   3568   4970
8192K   4477   5339

关于清除缓存内内存的结论

如果缓存中不存在您的内存,则当您需要用零填充内存时,AVX-512 存储和rep stosb 的性能大致相同。重要的是缓存,而不是这两种方法之间的选择。

使用non-temporal store清除不在缓存中的内存

我正在将 6-10 GB 的内存归零,这些内存按 64 字节对齐的缓冲区序列划分。没有缓冲区被归零两次。较小的缓冲区有一些开销,而且我只有 16 GB 的物理内存,所以我用较小的缓冲区归零了更少的内存。我对从 256 字节到每个缓冲区最多 8 GB 的缓冲区进行了各种测试。 我采用了 3 种不同的方法:

  1. vmovdqa64 [rcx+imm], zmm31 的普通 AVX-512 存储(循环 4 个存储然后比较计数器);
  2. vmovntdq [rcx+imm], zmm31 存储的非临时 AVX-512(4 个存储的相同循环);
  3. rep stosb.

对于小缓冲区,普通的 AVX-512 存储是赢家。然后,从 4KB 开始,non-temporal store 领先,而rep stosb 仍然落后。

然后,从 256KB 开始,rep stosb 的性能优于 AVX-512,但不是非临时存储,从那以后,情况没有改变。获胜者是一家非临时性 AVX-512 商店,然后是 rep stosb,然后是普通的 AVX-512 商店。

图 3. 存储到多个缓冲区的速度,每个缓冲区一次,MB/s 通过三种不同的方法:普通 AVX-512 存储、非临时 AVX-512 存储和 rep stosb。

Zeroized 6.67 GB: 27962026 blocks of 256 bytes for 2.90s, 2.30 GB/s by normal AVX-512 store
Zeroized 6.67 GB: 27962026 blocks of 256 bytes for 3.05s, 2.18 GB/s by nontemporal AVX-512 store
Zeroized 6.67 GB: 27962026 blocks of 256 bytes for 3.05s, 2.18 GB/s by rep stosb

Zeroized 8.00 GB: 16777216 blocks of 512 bytes for 3.06s, 2.62 GB/s by normal AVX-512 store
Zeroized 8.00 GB: 16777216 blocks of 512 bytes for 3.02s, 2.65 GB/s by nontemporal AVX-512 store
Zeroized 8.00 GB: 16777216 blocks of 512 bytes for 3.66s, 2.18 GB/s by rep stosb

Zeroized 8.89 GB: 9320675 blocks of 1 KB for 3.10s, 2.87 GB/s by normal AVX-512 store
Zeroized 8.89 GB: 9320675 blocks of 1 KB for 3.37s, 2.64 GB/s by nontemporal AVX-512 store
Zeroized 8.89 GB: 9320675 blocks of 1 KB for 4.85s, 1.83 GB/s by rep stosb

Zeroized 9.41 GB: 4934475 blocks of 2 KB for 3.45s, 2.73 GB/s by normal AVX-512 store
Zeroized 9.41 GB: 4934475 blocks of 2 KB for 3.79s, 2.48 GB/s by nontemporal AVX-512 store
Zeroized 9.41 GB: 4934475 blocks of 2 KB for 4.83s, 1.95 GB/s by rep stosb

Zeroized 9.70 GB: 2542002 blocks of 4 KB for 4.40s, 2.20 GB/s by normal AVX-512 store
Zeroized 9.70 GB: 2542002 blocks of 4 KB for 3.46s, 2.81 GB/s by nontemporal AVX-512 store
Zeroized 9.70 GB: 2542002 blocks of 4 KB for 4.40s, 2.20 GB/s by rep stosb

Zeroized 9.85 GB: 1290555 blocks of 8 KB for 3.24s, 3.04 GB/s by normal AVX-512 store
Zeroized 9.85 GB: 1290555 blocks of 8 KB for 2.65s, 3.71 GB/s by nontemporal AVX-512 store
Zeroized 9.85 GB: 1290555 blocks of 8 KB for 3.35s, 2.94 GB/s by rep stosb

Zeroized 9.92 GB: 650279 blocks of 16 KB for 3.37s, 2.94 GB/s by normal AVX-512 store
Zeroized 9.92 GB: 650279 blocks of 16 KB for 2.73s, 3.63 GB/s by nontemporal AVX-512 store
Zeroized 9.92 GB: 650279 blocks of 16 KB for 3.53s, 2.81 GB/s by rep stosb

Zeroized 9.96 GB: 326404 blocks of 32 KB for 3.19s, 3.12 GB/s by normal AVX-512 store
Zeroized 9.96 GB: 326404 blocks of 32 KB for 2.64s, 3.77 GB/s by nontemporal AVX-512 store
Zeroized 9.96 GB: 326404 blocks of 32 KB for 3.44s, 2.90 GB/s by rep stosb

Zeroized 9.98 GB: 163520 blocks of 64 KB for 3.08s, 3.24 GB/s by normal AVX-512 store
Zeroized 9.98 GB: 163520 blocks of 64 KB for 2.58s, 3.86 GB/s by nontemporal AVX-512 store
Zeroized 9.98 GB: 163520 blocks of 64 KB for 3.29s, 3.03 GB/s by rep stosb

Zeroized 9.99 GB: 81840 blocks of 128 KB for 3.22s, 3.10 GB/s by normal AVX-512 store
Zeroized 9.99 GB: 81840 blocks of 128 KB for 2.49s, 4.01 GB/s by nontemporal AVX-512 store
Zeroized 9.99 GB: 81840 blocks of 128 KB for 3.26s, 3.07 GB/s by rep stosb

Zeroized 10.00 GB: 40940 blocks of 256 KB for 2.52s, 3.97 GB/s by normal AVX-512 store
Zeroized 10.00 GB: 40940 blocks of 256 KB for 1.98s, 5.06 GB/s by nontemporal AVX-512 store
Zeroized 10.00 GB: 40940 blocks of 256 KB for 2.43s, 4.11 GB/s by rep stosb

Zeroized 10.00 GB: 20475 blocks of 512 KB for 2.15s, 4.65 GB/s by normal AVX-512 store
Zeroized 10.00 GB: 20475 blocks of 512 KB for 1.70s, 5.87 GB/s by nontemporal AVX-512 store
Zeroized 10.00 GB: 20475 blocks of 512 KB for 1.81s, 5.53 GB/s by rep stosb

Zeroized 10.00 GB: 10238 blocks of 1 MB for 2.18s, 4.59 GB/s by normal AVX-512 store
Zeroized 10.00 GB: 10238 blocks of 1 MB for 1.50s, 6.68 GB/s by nontemporal AVX-512 store
Zeroized 10.00 GB: 10238 blocks of 1 MB for 1.63s, 6.13 GB/s by rep stosb

Zeroized 10.00 GB: 5119 blocks of 2 MB for 2.02s, 4.96 GB/s by normal AVX-512 store
Zeroized 10.00 GB: 5119 blocks of 2 MB for 1.59s, 6.30 GB/s by nontemporal AVX-512 store
Zeroized 10.00 GB: 5119 blocks of 2 MB for 1.54s, 6.50 GB/s by rep stosb

Zeroized 10.00 GB: 2559 blocks of 4 MB for 1.90s, 5.26 GB/s by normal AVX-512 store
Zeroized 10.00 GB: 2559 blocks of 4 MB for 1.37s, 7.29 GB/s by nontemporal AVX-512 store
Zeroized 10.00 GB: 2559 blocks of 4 MB for 1.47s, 6.81 GB/s by rep stosb

Zeroized 9.99 GB: 1279 blocks of 8 MB for 2.04s, 4.90 GB/s by normal AVX-512 store
Zeroized 9.99 GB: 1279 blocks of 8 MB for 1.51s, 6.63 GB/s by nontemporal AVX-512 store
Zeroized 9.99 GB: 1279 blocks of 8 MB for 1.56s, 6.41 GB/s by rep stosb

Zeroized 9.98 GB: 639 blocks of 16 MB for 1.93s, 5.18 GB/s by normal AVX-512 store
Zeroized 9.98 GB: 639 blocks of 16 MB for 1.37s, 7.30 GB/s by nontemporal AVX-512 store
Zeroized 9.98 GB: 639 blocks of 16 MB for 1.45s, 6.89 GB/s by rep stosb

Zeroized 9.97 GB: 319 blocks of 32 MB for 1.95s, 5.11 GB/s by normal AVX-512 store
Zeroized 9.97 GB: 319 blocks of 32 MB for 1.41s, 7.06 GB/s by nontemporal AVX-512 store
Zeroized 9.97 GB: 319 blocks of 32 MB for 1.42s, 7.02 GB/s by rep stosb

Zeroized 9.94 GB: 159 blocks of 64 MB for 1.85s, 5.38 GB/s by normal AVX-512 store
Zeroized 9.94 GB: 159 blocks of 64 MB for 1.33s, 7.47 GB/s by nontemporal AVX-512 store
Zeroized 9.94 GB: 159 blocks of 64 MB for 1.40s, 7.09 GB/s by rep stosb

Zeroized 9.88 GB: 79 blocks of 128 MB for 1.99s, 4.96 GB/s by normal AVX-512 store
Zeroized 9.88 GB: 79 blocks of 128 MB for 1.42s, 6.97 GB/s by nontemporal AVX-512 store
Zeroized 9.88 GB: 79 blocks of 128 MB for 1.55s, 6.37 GB/s by rep stosb

Zeroized 9.75 GB: 39 blocks of 256 MB for 1.83s, 5.32 GB/s by normal AVX-512 store
Zeroized 9.75 GB: 39 blocks of 256 MB for 1.32s, 7.38 GB/s by nontemporal AVX-512 store
Zeroized 9.75 GB: 39 blocks of 256 MB for 1.64s, 5.93 GB/s by rep stosb

Zeroized 9.50 GB: 19 blocks of 512 MB for 1.89s, 5.02 GB/s by normal AVX-512 store
Zeroized 9.50 GB: 19 blocks of 512 MB for 1.31s, 7.27 GB/s by nontemporal AVX-512 store
Zeroized 9.50 GB: 19 blocks of 512 MB for 1.42s, 6.71 GB/s by rep stosb

Zeroized 9.00 GB: 9 blocks of 1 GB for 1.76s, 5.13 GB/s by normal AVX-512 store
Zeroized 9.00 GB: 9 blocks of 1 GB for 1.26s, 7.12 GB/s by nontemporal AVX-512 store
Zeroized 9.00 GB: 9 blocks of 1 GB for 1.29s, 7.00 GB/s by rep stosb

Zeroized 8.00 GB: 4 blocks of 2 GB for 1.48s, 5.42 GB/s by normal AVX-512 store
Zeroized 8.00 GB: 4 blocks of 2 GB for 1.07s, 7.49 GB/s by nontemporal AVX-512 store
Zeroized 8.00 GB: 4 blocks of 2 GB for 1.15s, 6.94 GB/s by rep stosb

Zeroized 8.00 GB: 2 blocks of 4 GB for 1.48s, 5.40 GB/s by normal AVX-512 store
Zeroized 8.00 GB: 2 blocks of 4 GB for 1.08s, 7.40 GB/s by nontemporal AVX-512 store
Zeroized 8.00 GB: 2 blocks of 4 GB for 1.14s, 7.00 GB/s by rep stosb

Zeroized 8.00 GB: 1 blocks of 8 GB for 1.50s, 5.35 GB/s by normal AVX-512 store
Zeroized 8.00 GB: 1 blocks of 8 GB for 1.07s, 7.47 GB/s by nontemporal AVX-512 store
Zeroized 8.00 GB: 1 blocks of 8 GB for 1.21s, 6.63 GB/s by rep stosb

避免 AVX-SSE 转换惩罚

对于所有的 AVX-512 代码,我使用了ZMM31 寄存器,因为 SSE 寄存器来自 0 到 15,所以 AVX-512 寄存器 16 到 31 没有对应的 SSE,因此没有招致过渡惩罚。

【讨论】:

  • 比其他方法 - 好吧,与您测试的 one 其他方法相比,显然每隔一个时钟周期只存储 16 个字节。 (~104GB/s 是我假设 ~3.3GHz CPU 上的 32B/​​c。)手动 memset 循环应该在每个周期实现一次存储,并在 L1d 缓存中命中,因此您的测试循环使 SSE 看起来很糟糕。如果您使用过 AVX,您应该能够将 memset 与中小型块相匹配。 (缓存命中,我们不想使用 movnt 或任何无 RFO 协议 rep stos 可能使用的地方。)
  • @PeterCordes - 我添加了一个关于 AVX-512 商店的部分,请查看。
  • 在填充内存缓冲区时如何利用 vmovntdq?是否应该有一些特定大小的缓冲区以便 vmovntdq 发挥优势? - 是的,像 glibc 这样的现实世界的 memcpy / memset 实现有一个调整变量,它们与大小进行比较以确定是否使用 NT 存储大副本。
  • @PeterCordes 这些早期性能提高 60 倍的案例一遍又一遍地在同一个缓冲区上,而后来的案例只使用每个缓冲区一次。这就是性能差异的原因。
  • @DanM。我已经对 SkyLake 和后来的微架构进行了测试,stosbstosd 都是一样的。在某些情况下stosd (stosq) 比stosb 快,但差异可以忽略不计,我什至认为这是很小的随机差异。我无法证明stosq 总是更快。但无论如何stosq 从来没有比stosb 慢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-10
  • 2012-10-10
  • 2011-12-18
  • 1970-01-01
相关资源
最近更新 更多