用内在函数初始化__m128i的最快方法?

问题描述 投票:0回答:1

当前,我有一个__m128i变量,我们称它为X。我想用一个恒定的128位值对其进行异或运算,然后将该值保存回X。因此,对于某些常数X ^= C,基本上是C

当前,我正在按照以下方式进行操作:

X = _mm_xor_si128(X, _mm_set_epi64x(C_a, C_b))

C的两个64位部分用于xor来构建__m128i。

我的问题是,这似乎不是初始化Xor的__m128i常数的最有效方法。尝试从对齐的数组中进行加载会更好吗?还是其他方法?

我目前在Visual Studio中使用MSVC。

c visual-c++ sse intrinsics micro-optimization
1个回答
0
投票

对于常量C_aC_b,我认为即使MSVC在通过该初始化程序进行常量传播方面也做得很好。因此,编写诸如SSE Error - Using m128i_i32 to define fields of a __m128i variable

的特定于实现的初始化程序没有任何优势

请记住,性能的真正决定因素是可以诱使编译器生成的程序集,而不是真正用来执行此操作的内在函数。

#include <immintrin.h>

__m128i xor_const(__m128i v) {
    return _mm_xor_si128(v, _mm_set_epi64x(0x789abc, 0x123456));
}

使用x64 MSVC -O2 Gv编译(on Godbolt)(使用vectorcall,以便我们可以看到向量已经在寄存器中时的行为,例如此内联时),我们得到了这个相当愚蠢的asm希望内联后在更大的函数中不会有这么糟糕:

;; MSVC 19.10
__xmm@0000000000789abc0000000000123456 DB 'V4', 012H, 00H, 00H, 00H, 00H, 00H
        DB      0bcH, 09aH, 'x', 00H, 00H, 00H, 00H, 00H

xor_const@@16 PROC                                  ; COMDAT
        movdqa  xmm1, XMMWORD PTR __xmm@0000000000789abc0000000000123456
        pxor    xmm1, xmm0
        movdqa  xmm0, xmm1
        ret     0
xor_const@@16 ENDP

我们可以看到,_mm_set内部函数在静态存储中被编译为16字节常数,就像我们想要的那样。未能使用pxor xmm0, xmm1令人惊讶,但是MSVC以asm闻名,与GCC和/或clang相比,它通常不那么出色。同样,作为大型函数的一部分,当它可以选择寄存器时,我们可能没有多余的movdqa。而且,如果xor处于循环中,那么无论如何我们都希望在循环外加载一次。这不是最新的MSVC版本。 Godbolt仅为C ++(不是C)安装了最新的MSVC版本,但您标记了此C。


相比之下,GCC9.2 -O3编译为在所有CPU上均有效的预期内存源PXOR。

xor_const:
        pxor    xmm0, XMMWORD PTR .LC0[rip]
        ret

.section .rodata    # Godbolt strips out stuff like section directive; re-added manually
.LC0:
        .quad   1193046
        .quad   7903932

[您可能会让编译器发出相同的asm,并带有一个包含常量的静态alignas(16)数组,并从中获取_mm_load_si128()。但是为什么要打扰呢?

要避免[avoid的一件事是在编写static const __m128i C = _mm_set...-编译器对此非常笨拙,不会将_mm_set折叠为__m128i的静态常量初始化程序。 C编译器将拒绝编译非常量静态初始化器。 C ++编译器将保留一些BSS空间,并运行类似于构造函数的函数,以将只读常量复制到该BSS空间中,因为在这种情况下_mm_set不能完全优化。

© www.soinside.com 2019 - 2024. All rights reserved.