如何使用GNU C Vector Extensions从/向数组加载/存储?

时间:2016-08-24 03:55:19

标签: c gcc x86 vectorization simd

我正在使用GNU C Vector Extensions,而不是英特尔的_mm_*内在函数。

我想做与英特尔_m256_loadu_pd内在相同的事情。逐个分配值很慢:gcc生成的代码有4个加载指令,而不是单个vmovupd_m256_loadu_pd确实生成)。

typedef double vector __attribute__((vector_size(4 * sizeof(double))));

int main(int argc, char **argv) {
    double a[4] = {1.0, 2.0, 3.0, 4.0};
    vector v;

    /* I currently do this */
    v[0] = a[0];
    v[1] = a[1];
    v[2] = a[2];
    v[3] = a[3];
}

我想要这样的事情:

v = (vector)(a);

v = *((vector*)(a));
但是没有工作。第一个失败,“无法将值转换为向量”,而第二个导致段错误。

2 个答案:

答案 0 :(得分:4)

更新:我看到你使用的是GNU C的本机矢量语法,而不是Intel内在函数。您是否因为非x86的可移植性而避免使用Intel内在函数? gcc目前编写的代码使用的GNU C向量比目标机器支持的要宽。 (你希望它只使用两个128b向量并分别对它们进行操作,但显然它比这更糟糕。)

无论如何,这个答案显示了如何使用Intel x86内在函数将数据加载到GNU C矢量语法类型

首先,如果您正在尝试学习编译为优秀代码的内容,那么查看低于-O2的编译器输出是浪费时间。您的main()将在-O2时优化为ret

除此之外,通过一次分配一个向量的元素,你会感到不好意外。

除此之外:普通人会调用类型v4df(4 Double Float的向量)或其他内容,而不是vector,所以当与C ++ std::vector一起使用时,他们不会疯狂。对于单精度,v8sf。 IIRC,gcc在__m256d内部使用类似名称。

在x86上,Intel内在类型(如__m256d)是在GNU C向量语法的基础上实现的(这就是为什么你可以在GNU C中做v1 * v2而不是写_mm256_mul_pd(v1, v2)) 。您可以自由地从__m256d转换为v4df,就像我在这里完成的那样。

我已经在功能中包含了两种理智的方法,所以我们可以看看他们的asm 。注意我们是如何从我们在同一个函数中定义的数组加载的,所以编译器不会对它进行优化。

我将它们放在Godbolt compiler explorer上,以便您可以使用各种编译选项和编译器版本查看asm。

typedef double v4df __attribute__((vector_size(4 * sizeof(double))));

#include <immintrin.h>

// note the return types.  gcc6.1 compiles with no warnings, even at -Wall -Wextra
v4df load_4_doubles_intel(const double *p) { return _mm256_loadu_pd(p); }
    vmovupd ymm0, YMMWORD PTR [rdi]   # tmp89,* p
    ret

v4df avx_constant() { return _mm256_setr_pd( 1.0, 2.0, 3.0, 4.0 ); }
    vmovapd ymm0, YMMWORD PTR .LC0[rip]
    ret

如果_mm_set*内在函数的args不是编译时常量,编译器将尽其所能制作有效的代码,将所有元素放入单个向量。通常最好这样做而不是将存储的C写入tmp数组并从中加载,因为这并不总是最好的策略。 (转发到宽负载的多个窄存储上的存储转发故障会在通常的存储转发延迟之上花费额外的~10个周期(IIRC)延迟。如果double已经在寄存器中,通常是最好将它们混合在一起。)

另请参阅Is it possible to cast floats directly to __m128 if they are 16 byte alligned?以获取将单个标量转换为向量的各种内在函数的列表。 标记wiki包含指向英特尔手册及其内在函数查找器的链接。

加载/存储没有Intel内在函数的GNU C向量:

我不确定你是如何“应该”那样做的。 This Q&A建议投射指向要加载的内存的指针,并使用typedef char __attribute__ ((vector_size (16),aligned (1))) unaligned_byte16;之类的矢量类型(请注意aligned(1)属性)。

您从*(v4df *)a获得了段错误,因为大概a未在32字节边界上对齐,但您使用的是假定自然对齐的向量类型。 (就像__m256d一样,如果你取消引用它的指针,而不是使用加载/存储内在函数将对齐信息传递给编译器。)

答案 1 :(得分:1)

您可以使用gcc中的等效内在函数来获取x86:__ builtin_ia32_loadupd256(helper method)。

类似于:

SetLayoutParams In View Cannot be Applied to int