2012-11-01 49 views
3

阵列我能有这样的联盟与__m256和两个__m128

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

工会不得不注册一个256位的两个128位部分的即时访问?

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

+2

你当然可以。但是如果编译器不知道如何优化它,那么你会付出性能损失。 – Mysticial

回答

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没有。)

+0

值得注意的是,这实际上不应该在当前μarches上存储转发停顿; 32B商店被破解为两个16B商店μops,每个商店都没有危险地转发到相应的加载操作。然而,这不应该让你的将军“不要这样做”。 –

+0

很高兴知道。我不知道英特尔也是如此。虽然我想在未来,32字节的商店可能会变成“本地”。 – Mysticial

+0

@Mystical:即使它们是原生的,我也希望转发能够继续工作(在实际运行中,英特尔已经投入了大量的精力来完成转发工作,而这些转发工作并非病理性错位 - 例如,最近的μarches转发16B存储到任何不通过8B边界的较小负载,以及明显的16B负载 - 顺便说一下,这些都记录在其优化手册中)。 –

2

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

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

+0

你确定这是UB吗?海湾合作委员会手册实际上建议这种做法,以避免类型打字指针 – hirschhornsalz

+0

我试过了,但我想了解它的性能影响,正如Mysticial所假设的那样。谢谢。 –

+0

@hirschhornsalz:我仔细一看,你说得对 - 这不是UB。 C99§6.2.6.1/ 7说:“当一个值存储在union类型的对象的成员中时,对象表示的字节与该成员不对应,但与其他成员相对应的字节取未指定的值。” –