5

我能有这样的工会吗

  union eight_floats_t
  {
    __m256 a;
    __m128 b[2];
  };
  eight_floats_t eight_floats;

即时访问 256 位寄存器的两个 128 位部分?

编辑:我想了解这种方法对性能的影响。

4

2 回答 2

11

你当然可以这样做。C 和 C++ 语言允许您这样做。它很可能会做你想做的事。

但是,您使用 AVX 的事实意味着您关心性能。因此,了解这是 SSE 程序员陷入的最常见(性能)陷阱之一可能会很有用。(很多人没有注意到)

问题1:

当前的编译器使用内存位置实现这样的联合。所以这是第一个问题,每次您从不同的字段访问联合时,它都会将数据强制到内存并读回。这是一个减速。

以下是 MSVC2010 生成的内容(经过优化):

eight_floats a;
a.a = vecA[0];

__m128 fvecA = a.b[0];
__m128 fvecB = a.b[1];
fvecA = _mm_add_ps(fvecA,fvecB);

vmovaps YMMWORD PTR a$[rbp], ymm0
movaps  xmm1, XMMWORD PTR a$[rbp+16]
addps   xmm1, XMMWORD PTR a$[rbp]
movaps  XMMWORD PTR fvecA$[rbp], xmm1
movss   xmm1, DWORD PTR fvecA$[rbp]

您可以看到它正在被刷新到内存中。

问题2:

第二次放缓甚至更糟。当您将某些内容写入内存并立即以不同的字长访问它时,您可能会触发存储到加载的停顿。(通常大约 > 10 个周期)

这是因为当前处理器上的加载存储队列通常不是为处理这种(不寻常的)情况而设计的。所以他们通过简单地将队列刷新到内存来处理它。


访问 AVX 数据类型的下半部分和上半部分的“正确”方法是使用:

  • _mm256_extractf128_ps()
  • _mm256_insertf128_ps()
  • _mm256_castps256_ps128()

和家人。对于其他数据类型也是如此。

也就是说,编译器可能足够聪明,可以识别您在做什么并使用这些指令。(至少 MSVC2010 没有。)

于 2012-11-01T18:42:51.350 回答
2

是的你可以。你试过了吗?

请注意,C 标准规定访问不是最近写入的联合成员是未指定的行为 - 具体而言,如果您写入一个成员然后读取另一个成员,则另一个未指定值(C99 §6.2.6.1/7)。但是,它是一个非常常见的习惯用法,并且得到所有主要编译器的良好支持。实际上,以任何顺序对工会的任何成员进行读写都是可以接受的做法(来源)。

于 2012-11-01T18:26:19.287 回答