英特尔 AVX 不一致 _mm256_load_si256 C 中的整数运算
Intel AVX inconsistent _mm256_load_si256 integer operation in C
为了并行化我基于数组的代码,我试图弄清楚如何利用英特尔 AVX 内在函数对大型数组执行并行操作。
我从文档中了解到,256 位 AVX 向量将支持最多 8 个并行的 32 位整数/32 位浮点数或最多 4 个并行的 64 位双精度数。浮点部分没有给我任何问题并且工作正常,但是整数 AVX 函数让我头疼,让我用下面的代码来演示:
命令行选项 -mavx 与兼容 AVX 的英特尔处理器结合使用。我不会使用 AVX2 功能。编译将在 Ubuntu 16.04.
上使用 GNU99 C 完成
AVX FP:
#include <stdio.h>
#include <stdlib.h>
#include <immintrin.h>
int main()
{
float data[8] = {1.f,2.f,3.f,4.f,5.f,6.f,7.f,8.f};
__m256 points = _mm256_loadu_ps(&data[0]);
for(int i = 0; i < 8; i++)
printf("%f\n",points[i]);
return 0;
}
输出:
1.000000
2.000000
3.000000
4.000000
5.000000
6.000000
7.000000
8.000000
这是应该的,但是使用整数加载 AVX 函数时情况并非如此:
AVX INT:
#include <stdio.h>
#include <stdlib.h>
#include <immintrin.h>
int main()
{
int data[8] = {1,2,3,4,5,6,7,8};
__m256i points = _mm256_loadu_si256((__m256i *)&data[0]);
for(int i = 0; i < 8; i++)
printf("%d\n",points[i]);
return 0;
}
输出:
1
3
5
7
1048576 [ out of bounds ]
0 [ out of bounds ]
1 [ out of bounds ]
3 [ out of bounds ]
如你所见,加载只产生__m256i类型变量中的4个元素,其中只有第一个、第三个、第五个和第七个元素是从
原始数组。超出第四个元素,引用越界。
如何生成将整个数据集按顺序加载到整数 AVX 数据类型(与 AVX 浮点数据类型非常相似)的预期结果?
您正在使用 GNU C 扩展通过 []
索引向量,而不是将其存储回数组。 Intel 的内在函数文档对此无话可说,而且并非所有编译器都支持它(例如 MSVC 不支持)。
GCC 将 __m256i
定义为 GNU C native vector of long long
。 <immintrin.h>
没有为 int
或 short
的 SIMD 向量定义不同的 __m256i
类型,并且 __m256i
不记得它来自哪里/如何它被设置了。 (与 ps
和 pd
有单独的 C 类型的 FP 向量不同,所以如果你想在 shufpd
或 unpcklpd
一个 ps
向量)
您可以自己 typedef
像 v8si
这样的原生矢量类型(请参阅之前的 link 到 gcc 文档),或者使用库 like Agner Fog's VCL 为您提供像Vec8i
(8 个有符号 int
)或 Vec32uc
(32 个无符号 char
)。它们具有运算符重载,可让您根据类型编写 a + b
而不是 _mm256_add_epi32(a, b)
或 _mm256_add_epi8(a,b)
。或者使用 []
而不是 _mm_extract_epi32
/ epi8 / epi16 / epi64.
请参阅 print a __m128i variable 了解可移植和 safe/correct 循环/打印出 Intel 固有 SIMD 变量的元素的方法。 TL:DR: _mm_store
/ _mm256_store
到 tmp 数组并对其进行索引。它是可移植的,并且它进行了优化(对于整数为 pextrd
或者对于 FP 只是一个混洗),在简单的情况下没有实际的 store/reload。
为了并行化我基于数组的代码,我试图弄清楚如何利用英特尔 AVX 内在函数对大型数组执行并行操作。
我从文档中了解到,256 位 AVX 向量将支持最多 8 个并行的 32 位整数/32 位浮点数或最多 4 个并行的 64 位双精度数。浮点部分没有给我任何问题并且工作正常,但是整数 AVX 函数让我头疼,让我用下面的代码来演示:
命令行选项 -mavx 与兼容 AVX 的英特尔处理器结合使用。我不会使用 AVX2 功能。编译将在 Ubuntu 16.04.
上使用 GNU99 C 完成AVX FP:
#include <stdio.h>
#include <stdlib.h>
#include <immintrin.h>
int main()
{
float data[8] = {1.f,2.f,3.f,4.f,5.f,6.f,7.f,8.f};
__m256 points = _mm256_loadu_ps(&data[0]);
for(int i = 0; i < 8; i++)
printf("%f\n",points[i]);
return 0;
}
输出:
1.000000
2.000000
3.000000
4.000000
5.000000
6.000000
7.000000
8.000000
这是应该的,但是使用整数加载 AVX 函数时情况并非如此:
AVX INT:
#include <stdio.h>
#include <stdlib.h>
#include <immintrin.h>
int main()
{
int data[8] = {1,2,3,4,5,6,7,8};
__m256i points = _mm256_loadu_si256((__m256i *)&data[0]);
for(int i = 0; i < 8; i++)
printf("%d\n",points[i]);
return 0;
}
输出:
1
3
5
7
1048576 [ out of bounds ]
0 [ out of bounds ]
1 [ out of bounds ]
3 [ out of bounds ]
如你所见,加载只产生__m256i类型变量中的4个元素,其中只有第一个、第三个、第五个和第七个元素是从 原始数组。超出第四个元素,引用越界。
如何生成将整个数据集按顺序加载到整数 AVX 数据类型(与 AVX 浮点数据类型非常相似)的预期结果?
您正在使用 GNU C 扩展通过 []
索引向量,而不是将其存储回数组。 Intel 的内在函数文档对此无话可说,而且并非所有编译器都支持它(例如 MSVC 不支持)。
GCC 将 __m256i
定义为 GNU C native vector of long long
。 <immintrin.h>
没有为 int
或 short
的 SIMD 向量定义不同的 __m256i
类型,并且 __m256i
不记得它来自哪里/如何它被设置了。 (与 ps
和 pd
有单独的 C 类型的 FP 向量不同,所以如果你想在 shufpd
或 unpcklpd
一个 ps
向量)
您可以自己 typedef
像 v8si
这样的原生矢量类型(请参阅之前的 link 到 gcc 文档),或者使用库 like Agner Fog's VCL 为您提供像Vec8i
(8 个有符号 int
)或 Vec32uc
(32 个无符号 char
)。它们具有运算符重载,可让您根据类型编写 a + b
而不是 _mm256_add_epi32(a, b)
或 _mm256_add_epi8(a,b)
。或者使用 []
而不是 _mm_extract_epi32
/ epi8 / epi16 / epi64.
请参阅 print a __m128i variable 了解可移植和 safe/correct 循环/打印出 Intel 固有 SIMD 变量的元素的方法。 TL:DR: _mm_store
/ _mm256_store
到 tmp 数组并对其进行索引。它是可移植的,并且它进行了优化(对于整数为 pextrd
或者对于 FP 只是一个混洗),在简单的情况下没有实际的 store/reload。