与__m256联合和两个__m128的数组

时间:2012-11-01 18:24:04

标签: c performance sse vectorization avx

我可以拥有这样的联盟

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

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

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

2 个答案:

答案 0 :(得分:10)

你当然可以做到这一点。 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没有。)

答案 1 :(得分:2)

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

请注意,C标准表示访问联盟成员的行为是未指定的行为,而该联盟的成员不是最近写的那个 - 具体来说,如果你写一个成员然后读另一个成员,另一个成员一个具有未指定的值(C99§6.2.6.1/ 7)。然而,这是一个非常普遍的习惯用语,并得到所有主要编译器的良好支持。实际上,以任何顺序读取和写入工会的任何成员都是可以接受的做法(source)。