页面错误 maskmovdqu / _mm_maskmoveu_si128 - 如何避免?

page faulting maskmovdqu / _mm_maskmoveu_si128 - how to avoid?

我有一个流出结构化数据的功能。数据为 Vec4/Vec3/Vec2/float-structures,因此每个结构的最大大小为 16 字节。现在可能会发生,正在从结构内部开始读取流。简单的解决方案:加载结构,构建存储掩码,将目标数据指针减少调用要开始读取的结构中的字节数。

假设当前项目类型是 Vec2,我们在这个结构中有 4 个字节:

xmm0 = 00000000-00000000-dadadada-dadadada
xmm1 = 00000000-00000000-ffffffff-00000000
result_data_ptr = 13450000
-> RDI = 1344fffc
maskmovdqu xmm0, xmm1

=> 结果是页面错误异常。

有什么方法可以检测到这个页面错误会发生吗?连上一页的记忆都不会被触及...

maskmovdqu 不进行故障抑制,这与 AVX vmaskmovps 或 AVX512 屏蔽存储不同。这些会解决你的问题,虽然可能仍然不是最有效的方法。

As documented in Intel's ISA ref manual,带有全零掩码(因此没有任何内容存储到内存中)与寻址内存和页面错误相关的异常可能仍会发出信号(取决于实现)。

使用非零掩码,我假设如果 16 字节包含任何不可写页面,它可以保证 页面错误。或者即使某些存储确实发生(未映射页面中为零,但其他地方为非零)

,某些实现也可能会抑制错误

无论如何在真正的 CPU 上它都不是一条快速指令。

maskmovdqu 有时在单核 Pentium 4(或非 IDK)上表现不错,and/or 它的 MMX 前身在顺序 Pentium 上可能有用。屏蔽缓存绕过存储在现代 CPU 上的用处要小得多,其中 L3 是正常的后盾,而且缓存很大。也许更重要的是,在单个内核和内存控制器之间有更多的机器,因为即使另一个内核 did 在某个时候重新加载该内存,一切都必须正常工作,所以部分 - line write 的效率可能更低。

如果您真的只存储总共 8 或 12 个字节,这通常是一个糟糕的选择。 (基本上与不写整行的 NT 存储相同)。特别是如果您使用多个狭窄的商店来获取数据片段并将它们放入一个连续的流中。我会 而不是 假设多个重叠的 maskmovdqu 存储将在您最终完成一个缓存行后产生整个缓存行的单个有效存储,即使掩码意味着实际上没有字节写了两次。

L1d 缓存非常适合在最终完成之前缓冲对缓存行的多次小写入;除非你可以几乎背靠背地做几个 NT 存储,否则使用普通存储。

要存储 XMM 寄存器的前 8 个字节,请使用 movhps

写入缓存也可以很好地进行重叠存储,例如 movdqu。因此,您可以通过将每个 12 字节对象混洗到 XMM 寄存器的底部(或首先以这种方式加载它们)来连接几个 12 字节对象,然后使用 movdqu 存储到 [rdi][rdi+12], [rdi+24], 等。4 字节重叠完全没问题;存储缓冲区中的合并可能会在它提交到 L1d 缓存之前吸收它,或者如果没有,那么 L1d 缓存仍然非常快。


在开始编写大型数组时,如果您不知道对齐方式,可以对输出的前 16 个字节进行未对齐 movdqu。然后执行第一个 16 字节对齐的存储,可能与它重叠。如果您的总输出大小始终 >= 16 字节,则此策略不需要大量分支即可让您对其中的大部分内容进行对齐存储。最后,您可以对最终可能未对齐的向量执行相同的操作,该向量可能与最后对齐的向量部分重叠。 (或者如果数组 对齐的,那么就没有重叠并且它也是对齐的。如果地址对齐,movdqumovdqa 一样快,在现代CPU。)

好吧,似乎没有什么好的方法可以预测页面错误,所以我走了另一条路。这是一个直接的 asm 解决方案:

首先,我们使用table将结果根据bytes_inside进行移位。然后我们找出要写入多少字节。由于最多需要写入 15 个字节,因此这是一个 4 阶段过程。我们简单地测试 bytes_to_write 的位 - 如果设置了“8”位(即位 3),我们使用 movq。位 2 需要 movd,位 1 需要 pextrw,位 0 需要 pextrb。每次存储后,数据指针相应递增,数据寄存器相应移位。

登记:

  • r10: result_data_ptr
  • r11: bytes_inside
  • xmm0.word[6]:数据项的大小
  • xmm2:我们的数据项
  • shuf_inside:数据 table 用于使用 pshufb 逐字节旋转 xmm 寄存器(psrldq 仅允许立即字节移位计数)
.DATA
ALIGN 16
shuf_inside   byte 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,0
              byte 2,3,4,5,6,7,8,9,10,11,12,13,14,15,0,1
              byte 3,4,5,6,7,8,9,10,11,12,13,14,15,0,1,2
              byte 4,5,6,7,8,9,10,11,12,13,14,15,0,1,2,3
              byte 5,6,7,8,9,10,11,12,13,14,15,0,1,2,3,4
              byte 6,7,8,9,10,11,12,13,14,15,0,1,2,3,4,5
              byte 7,8,9,10,11,12,13,14,15,0,1,2,3,4,5,6
              byte 8,9,10,11,12,13,14,15,0,1,2,3,4,5,6,7
              byte 9,10,11,12,13,14,15,0,1,2,3,4,5,6,7,8
              byte 10,11,12,13,14,15,0,1,2,3,4,5,6,7,8,9
              byte 11,12,13,14,15,0,1,2,3,4,5,6,7,8,9,10
              byte 12,13,14,15,0,1,2,3,4,5,6,7,8,9,10,11
              byte 13,14,15,0,1,2,3,4,5,6,7,8,9,10,11,12
              byte 14,15,0,1,2,3,4,5,6,7,8,9,10,11,12,13
              byte 15,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14
.CODE
[...]
        lea             rax,        [ shuf_inside ]
        shl             r11,        4
        pshufb          xmm2,       [ rax + r11 - 16 ]
        shr             r11,        4
        pextrw          rax,        xmm0,       6           ;reducedStrideWithPadding - i.e. size of item
        sub             rax,        r11                     ;bytes_to_write
        ;
        test            rax,        8
        jz              lessThan8
        movq            qword ptr [r10], xmm2
        psrldq          xmm2,       8
        add             r10,        8
        lessThan8:
        test            rax,        4
        jz              lessThan4
        movd            dword ptr [r10], xmm2
        psrldq          xmm2,       4
        add             r10,        4
        lessThan4:
        test            rax,        2
        jz              lessThan2
        pextrw          word ptr [r10], xmm2, 0
        psrldq          xmm2,       2
        add             r10,        2
        lessThan2:
        test            rax,        1
        jz              lessThan1
        pextrb          byte ptr [r10], xmm2, 0
        lessThan1: