【问题标题】:Pointer to result of AVX load (_mm256_load_si256)指向 AVX 加载结果的指针 (_mm256_load_si256)
【发布时间】:2021-12-20 12:50:16
【问题描述】:

如果我有一些类,其字段类似于__m256i* loaded_v,方法类似于:

void load() {
    loaded_v = &_mm256_load_si256(reinterpret_cast<const __m256i*>(vector));
}

loaded_v 将成为有效指针多久?由于寄存器数量有限,我想最终loaded_v 将引用不同的值,或者会发生其他一些奇怪的行为。但是,我想减少我执行的加载次数。

我正在编写一个压缩位数组类,​​我想使用 AVX 内部函数来提高性能。但是,每次我做一些操作(和、或、异或等)时加载我的位数组是低效的。因此,我希望能够在执行一些操作之前显式调用load()。但是,我不明白 AVX 寄存器是如何处理的。任何人都可以帮助我,或者指向我有关此问题的一些文档吗?

【问题讨论】:

  • 这段代码甚至没有编译,你正在获取一个右值的地址。
  • @MarcGlisse 是将整个 __m256i 值作为字段存储在我的班级中的唯一选择吗?
  • __m256iintfloat 没有本质区别,编译器仍然需要为它们分配寄存器。负载内在函数基本上只是用于传达对齐与否,并作为演员表。 __m256i 不是仅寄存器或类似的东西,从数组或结构访问__m256i 通常会涉及加载指令,除非数组优化掉。 Understanding how the instrinsic functions for SSE use memory

标签: sse intrinsics avx2


【解决方案1】:

优化编译器会自动使用寄存器。

它可能会将__m256 变量放入内存或寄存器中,或者可能在代码的一部分中使用寄存器,并将其溢出到另一部分中。这不仅可以使用独立的自动存储(堆栈)变量来完成,还可以使用类的成员来完成,尤其是当类实例本身就是一个自动存储变量时。

如果使用寄存器,__m256 变量将对应 ymm 寄存器之一(x86-64 中的 16 个之一,32 位编译中的 8 个之一,或带有 AVX512 的 x86-64 中的 32 个之一),有无需间接引用。

_mm256_load_si256 内在函数不一定编译为 vmovdqa。例如这段代码:

#include <immintrin.h>

__m256i f(__m256i a, const void* p)
{
    __m256i  b = _mm256_load_si256(reinterpret_cast<const __m256i*>(p));
    return _mm256_xor_si256(a, b);
}

编译如下(https://godbolt.org/z/ve67YPn4T):

        vpxor   ymm0, ymm0, YMMWORD PTR [rdx]
        ret     0

C 和 C++ 是高级语言;内在函数应该被视为向编译器传达语义的一种方式,而不是指令助记符。

您应该将值加载到变量中,

__m256i loaded_v;
loaded_v = _mm256_load_si256(reinterpret_cast<const __m256i*>(vector));

或临时:

__m256_whatever_operation(_mm256_load_si256(reinterpret_cast<const __m256i*>(vector)), other_operand);

您应该遵循通常的 C 或 C++ 规则。

如果您反复从指针加载间接值,将其缓存在变量中可能会有所帮助,这样编译器会看到加载之间的值没有变化,并将其用作优化机会。当然,编译器无论如何都可能错过这个机会,或者即使没有缓存变量也能找到它(可能借助严格的别名规则)。

【讨论】:

  • 谢谢,这很有趣也很有帮助。我仍然有点担心相信编译器会自动进行此类优化,但我会先检查这些优化是否正在发生,然后再尝试明确处理此类事情
猜你喜欢
  • 2013-02-23
  • 2014-07-29
  • 1970-01-01
  • 1970-01-01
  • 2018-05-16
  • 2016-03-21
  • 2012-03-28
  • 1970-01-01
相关资源
最近更新 更多