【问题标题】:Why should you not access the __m128i fields directly?为什么不应该直接访问 __m128i 字段?
【发布时间】:2014-05-17 03:54:54
【问题描述】:

我正在阅读this on MSDN,它说

您不应直接访问 __m128i 字段。但是,您可以在调试器中查看这些类型。 __m128i 类型的变量映射到 XMM[0-7] 寄存器。

但是,它没有解释原因。为什么?例如,以下是“坏”:

void func(unsigned short x, unsigned short y)
{
    __m128i a;
    a.m128i_i64[0] = x;

    __m128i b;
    b.m128i_i64[0] = y;

    // Now do something with a and b ...
}

应该使用某种load 函数,而不是像上面示例中那样进行分配吗?

【问题讨论】:

  • 这些字段是 Microsoft 特定的。当然,他们并不关心这一点,因为他们会喜欢将您锁定在他们的编译器中。真正的原因是性能。没有有效的方法来访问 SSE 寄存器的各个元素。 SSE4.1 有说明,但索引必须是编译时常量。

标签: c++ sse intrinsics


【解决方案1】:

字段m128i_i64 和family 是Microsoft 编译器特定的扩展。它们在大多数其他编译器中不存在。

尽管如此,它们对于测试目的很有用。


避免使用它们的真正原因是性能。硬件无法有效地访问 SIMD 向量的各个元素。

  • 没有可让您直接访问单个元素的说明。 (SSE4.1 可以,但它需要一个编译时常量索引。)
  • 由于store forwarding 失败,遍历内存可能会导致非常大的损失。

AVX 和 AVX2 不扩展 SSE4.1 指令以允许访问 256 位向量中的元素。据我所知,AVX512 不会支持 512 位向量。

同样,集合内在函数(例如_mm256_set_pd())也存在同样的问题。它们被实现为一系列数据混洗操作。或者通过记忆并承担商店转发摊位。


这引出了一个问题:有没有一种有效的方法可以从标量组件填充 SIMD 向量? (或将 SIMD 向量分离为标量分量)

简短回答:并非如此。当您使用 SIMD 时,您需要以矢量化形式完成大量工作。所以初始化开销应该无关紧要。

【讨论】:

  • 很高兴再次看到您在 SIMD 上的神秘回答。商店转发的 wiki 链接很有趣。
  • 是的。存储转发在现代处理器上是一个相当大的问题。没有它,您需要为写入后读取支付 20 多个周期的罚款。不幸的是,当您尝试使用不同大小的写入内存来读取内存时,它往往会失败。较新的处理器更好,因为只要它完全包含在待处理的写入中,您就可以读取。但是设置内在函数则相反。并且存储单元目前无法将较小的存储合并成一个较大的存储,以便将其转发给更大的负载。
  • 谢谢!所以在我的代码示例中,应该如何将参数加载到 __m128i 类型中?从其他一些问题中,我可以看到如何使用数组来做到这一点。但是,仅加载一个简单的整数似乎会给我带来访问冲突。这可能是对齐问题,但我不确定如何以非 MS 特定的方式修复它...
  • @user3475799 如果您需要从不同的标量源加载 SIMD 类型,则设置内在函数通常是最好的方法。编译器(通常)会选择坏处并生成最快的代码。此外,它不应该崩溃。如果它在堆栈上,编译器应该自动对齐__m128i。如果你在堆上分配它,那就另当别论了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-15
  • 1970-01-01
  • 1970-01-01
  • 2019-01-24
相关资源
最近更新 更多