如何防止编译器“优化”SIMD常量来查找表?

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

一个例子可能是最好的,所以考虑一下this代码(忽略逻辑,这是无意义的):

#include <emmintrin.h>

auto foo(long long const a[], size_t n)
{
    auto const v = _mm_set_epi64x(0xDEADBEEFDEADBEEF, 0xBAADF00DBAADF00D);
    auto r = v;
    for (size_t i = 0; i < n; i += sizeof(v) / sizeof(*a))
    {
        r = _mm_add_epi64(r, _mm_sub_epi64(_mm_load_si128(
            reinterpret_cast<__m128i const*>(&a[i])), v));
    }
    return r;
}

注意每个编译器都会生成与此类似的内存访问:

.LCPI0_0:
        .quad   -4995072469926809587            # 0xbaadf00dbaadf00d
        .quad   -2401053088876216593            # 0xdeadbeefdeadbeef
...
        movdqa  xmm0, xmmword ptr [rip + .LCPI0_0]

尽管事实上我使用

_mm_set_epi64x
而不是
_mm_load_si128
来初始化变量。

这很容易破坏多个数据缓存行。也感觉很没有必要。

如何可靠地且高效地让编译器停止这样做,以便我可以将内容保留在指令流中并避免不必要的内存访问?

注意:我已经知道我可以引入

volatile
变量来解决这个问题,这不会消除内存访问,但至少引用堆栈内存而不是静态数据。我想知道是否有更好的方法来完全避免内存访问。

c++ visual-c++ x86 clang simd
1个回答
0
投票

一种方法是从函数返回这些值。但将这些函数保留在不同的翻译单元中。保留在同一个翻译单元中并没有帮助,因为这些都是函数,并且 clang 和 gcc 都不尊重

__attribute((noinline))
,这是不幸的。


// a.cpp
__attribute__((noinline))
long dead() {
    return 0xDEADBEEFDEADBEEF;
}

__attribute__((noinline))
long baad() {
    return 0xBAADF00DBAADF00D;
}

// b.cpp

#include <emmintrin.h>

auto foo(long long const a[], size_t n)
{
    auto const v = _mm_set_epi64x(dead(), baad());
    auto r = v;
    for (size_t i = 0; i < n; i += sizeof(v) / sizeof(*a))
    {
        r = _mm_add_epi64(r, _mm_sub_epi64(_mm_load_si128(reinterpret_cast<__m128i const *>(&a[i])), v));
    }
    return r;
}
© www.soinside.com 2019 - 2024. All rights reserved.