AVX2 1GB长阵列
AVX2 1GB long array
我在 .bin 文件中有一个 1gb 长的数组,其中包含浮点数。在我阅读它之后,如何使用 avx2 指令对元素求和,并打印结果?
我用 Jake 'Alquimista' LEE 的回答编辑了我的代码。
问题是结果比实际要小得多。还有一个问题,我如何为从 .bin 文件中读取的每个数字添加一个常量?
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <immintrin.h>
inline float sumf(const float *pSrc, uint32_t len)
{
__m256 sum, in;
float sumr;
uint32_t sumi;
uint32_t lenr = len & 7;
while (len--)
len >>= 3;
sum = _mm256_set1_ps(0.0f);
{
in = _mm256_loadu_ps(pSrc++);
sum = _mm256_add_ps(in, sum);
}
sum = _mm256_hadd_ps(sum, in);
sum = _mm256_hadd_ps(sum, in);
sum = _mm256_hadd_ps(sum, in);
sumi = _mm256_extract_epi32(*(__m256i *)&sum, 0);
sumr = *(float *)&sumi;
while (lenr--)
{
sumr += *pSrc++;
}
return sumr;
}
int main(void)
{
FILE *file;
float *buffer2;
uint32_t fileLen;
if((file = fopen("example.bin","rb"))==NULL)
{
printf("Error! opening file");
exit(1);
}
fseek(file, 0, SEEK_END);
fileLen=ftell(file);
fseek(file, 0, SEEK_SET);
buffer2=(float *)malloc(fileLen+1);
if (!buffer2)
{
fprintf(stderr, "Memory error!");
fclose(file);
return 0;
}
fread(buffer2, fileLen, 1, file);
fclose(file);
printf( "File size : %lu Bits \n", fileLen );
for(int i = 0; i<10; i++)
printf("%f \n", buffer2[i]);
float sum =sumf(buffer2,fileLen);
printf("%f\n",s);
free(buffer2);
return 0;
}
inline float sumf(const float *pSrc, uint32_t len)
{
__m256 sum, in;
float sumr;
uint32_t sumi;
uint32_t lenr = len & 7;
len >>= 3;
sum = _mm256_set1_ps(0.0f);
while (len--)
{
in = _mm256_loadu_ps(pSrc++);
sum = _mm256_add_ps(in, sum);
}
in = *(__m256 *)&_mm256_permute4x64_pd(*(__m256d *)&sum, 0b01001110);
sum = _mm256_hadd_ps(sum, in);
sum = _mm256_hadd_ps(sum, in);
sum = _mm256_hadd_ps(sum, in);
sumi = _mm256_extract_epi32(*(__m256i *)&sum, 0);
sumr = *(float *)&sumi;
while (lenr--)
{
sumr += *pSrc++;
}
return sumr;
}
上面的函数就可以了。但是,我不认为它会带来很大的性能提升(如果有的话),因为它非常微不足道,而且编译器无论如何都会对其进行自动矢量化。
请注意,当您将它们作为参数传递时,您必须将指针类型转换为 float *
,并将 filelen
除以 sizeof(float)
。
这是(很可能)您的错误:
while (len--)
len >>= 3;
这是一个 while 循环。只要 len != 0,您就可以将 len 替换为 (len - 1) >> 3。然后将其更改为 -1。看不到循环。
将 1GB 文件读入内存是大内存和 I/O 开销。虽然我对 AVX2
不是很熟悉,但我阅读了网上的文章 & 我可以想出以下解决方案,该解决方案经过实际测试并证明有效。
我的解决方案包括将文件读取为 512 字节的块(128 个浮点数的块),然后将向量对相加(每个块总共 16 个向量),这样最后我们得到一个最终的 __m256
向量,通过强制转换float*
我们可以将其各个组成部分相加得到最终结果。
文件不是 128 浮点数对齐的情况在最后一个 for
循环中通过对单个浮点数求和来处理。
代码已被注释,但如果您有任何建议为答案添加更多解释,请随时这样做。
#include <immintrin.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int make_floatf(char *, int);
float avx_sfadd(char*);
char error_buf[1024];
#define PERROR() \
do { \
strerror_r(errno, error_buf, 1024); \
printf("Error: %s\n", error_buf); \
fclose(fp); \
return -1; \
} while(0)
/* This function generates a .bin file containing blocks
* of 128 floating point numbers
*/
int make_floatf(char *filename, int nblocks)
{
FILE *fp = NULL;
if(!(fp = fopen(filename, "wb+")))
PERROR();
float *block_ptr = malloc(sizeof(float) * 128); /* 512 Bytes block of 128 floats */
if(!block_ptr)
PERROR();
int j, i;
for(j = 0; j < nblocks; j++)
{
for(i = 0; i < 128; i++)
block_ptr[i] = 1.0;
int ret = fwrite(block_ptr, sizeof(float), 128, fp);
if(ret < 128)
{
free(block_ptr);
PERROR();
}
}
free(block_ptr);
fclose(fp);
return 0;
}
/* This function reads the .bin file as chuncks of 512B
* blocks (128 floating point numbers) and calculates thier sum.
* The final sum in a form of vector is looped through and its
* components are summed up to get the final result.
*/
float avx_sfadd(char *filename)
{
FILE *fp = NULL;
__m256 v1;
__m256 v2;
__m256 sum = _mm256_setzero_ps();
if(!(fp = fopen(filename, "rb")))
PERROR();
struct stat stat_buf;
stat(filename, &stat_buf);
size_t fsize = stat_buf.st_size;
size_t nblocks = fsize / (sizeof(float) * 128);
size_t rem_size = fsize - nblocks * sizeof(float) * 128;
size_t rem_floats = rem_size / (sizeof(float));
printf("File size: %ld\nnblocks:%ld\nnremfloats: %ld\n",\
fsize, nblocks, rem_floats);
/* This memory area will hold the 128 floating point numbers per block */
float *block_ptr = malloc(sizeof(float) * 128);
if(!block_ptr)
PERROR();
int i;
for(i = 0; i < nblocks; i++)
{
int ret = fread(block_ptr, sizeof(float), 128, fp);
if(ret < 128)
PERROR();
/* Summing up vectors in a block of 16 vectors (128 floats) */
int j;
for(j = 0; j < 16; j += 2)
{
v1 = _mm256_loadu_ps(block_ptr + j*8);
v2 = _mm256_loadu_ps(block_ptr + (j+1)*8);
sum += _mm256_add_ps(v1, v2);
}
}
/* Handling the case if the last chunck of the file doesn't make
* a complete block.
*/
float rem_sum = 0;
if(rem_size > 0)
{
int ret = fread(block_ptr, 1, rem_size, fp);
if(ret < rem_floats)
PERROR();
int j;
for(j = 0; j < rem_floats; j++)
rem_sum += block_ptr[j];
}
float final_sum = rem_sum;
float *sum_ptr = (float*)∑ /* The final vector hold the sum of all vectors */
/* Summing up the values of the last vector to get the final result */
int k;
for(k = 0; k < 8; k++)
final_sum += sum_ptr[k];
free(block_ptr);
fclose(fp);
return final_sum;
}
int main(int argc, char **argv)
{
if(argc < 2){
puts("./main filename [nblocks]");
return 0;
}
/* ./main filename number_of_block_to_create (eg. ./main floats.bin 1024 )*/
else if(argc == 3){
if(!make_floatf(argv[1], atoi(argv[2])))
puts("File has been created sucessfully\n");
}
/* ./main filename (eg. ./main floats.bin) to calculate sum*/
else
printf("avx_sum = %f\n", avx_sfadd(argv[1])) :
return 0;
}
我在 .bin 文件中有一个 1gb 长的数组,其中包含浮点数。在我阅读它之后,如何使用 avx2 指令对元素求和,并打印结果?
我用 Jake 'Alquimista' LEE 的回答编辑了我的代码。 问题是结果比实际要小得多。还有一个问题,我如何为从 .bin 文件中读取的每个数字添加一个常量?
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <immintrin.h>
inline float sumf(const float *pSrc, uint32_t len)
{
__m256 sum, in;
float sumr;
uint32_t sumi;
uint32_t lenr = len & 7;
while (len--)
len >>= 3;
sum = _mm256_set1_ps(0.0f);
{
in = _mm256_loadu_ps(pSrc++);
sum = _mm256_add_ps(in, sum);
}
sum = _mm256_hadd_ps(sum, in);
sum = _mm256_hadd_ps(sum, in);
sum = _mm256_hadd_ps(sum, in);
sumi = _mm256_extract_epi32(*(__m256i *)&sum, 0);
sumr = *(float *)&sumi;
while (lenr--)
{
sumr += *pSrc++;
}
return sumr;
}
int main(void)
{
FILE *file;
float *buffer2;
uint32_t fileLen;
if((file = fopen("example.bin","rb"))==NULL)
{
printf("Error! opening file");
exit(1);
}
fseek(file, 0, SEEK_END);
fileLen=ftell(file);
fseek(file, 0, SEEK_SET);
buffer2=(float *)malloc(fileLen+1);
if (!buffer2)
{
fprintf(stderr, "Memory error!");
fclose(file);
return 0;
}
fread(buffer2, fileLen, 1, file);
fclose(file);
printf( "File size : %lu Bits \n", fileLen );
for(int i = 0; i<10; i++)
printf("%f \n", buffer2[i]);
float sum =sumf(buffer2,fileLen);
printf("%f\n",s);
free(buffer2);
return 0;
}
inline float sumf(const float *pSrc, uint32_t len)
{
__m256 sum, in;
float sumr;
uint32_t sumi;
uint32_t lenr = len & 7;
len >>= 3;
sum = _mm256_set1_ps(0.0f);
while (len--)
{
in = _mm256_loadu_ps(pSrc++);
sum = _mm256_add_ps(in, sum);
}
in = *(__m256 *)&_mm256_permute4x64_pd(*(__m256d *)&sum, 0b01001110);
sum = _mm256_hadd_ps(sum, in);
sum = _mm256_hadd_ps(sum, in);
sum = _mm256_hadd_ps(sum, in);
sumi = _mm256_extract_epi32(*(__m256i *)&sum, 0);
sumr = *(float *)&sumi;
while (lenr--)
{
sumr += *pSrc++;
}
return sumr;
}
上面的函数就可以了。但是,我不认为它会带来很大的性能提升(如果有的话),因为它非常微不足道,而且编译器无论如何都会对其进行自动矢量化。
请注意,当您将它们作为参数传递时,您必须将指针类型转换为 float *
,并将 filelen
除以 sizeof(float)
。
这是(很可能)您的错误:
while (len--)
len >>= 3;
这是一个 while 循环。只要 len != 0,您就可以将 len 替换为 (len - 1) >> 3。然后将其更改为 -1。看不到循环。
将 1GB 文件读入内存是大内存和 I/O 开销。虽然我对 AVX2
不是很熟悉,但我阅读了网上的文章 & 我可以想出以下解决方案,该解决方案经过实际测试并证明有效。
我的解决方案包括将文件读取为 512 字节的块(128 个浮点数的块),然后将向量对相加(每个块总共 16 个向量),这样最后我们得到一个最终的 __m256
向量,通过强制转换float*
我们可以将其各个组成部分相加得到最终结果。
文件不是 128 浮点数对齐的情况在最后一个 for
循环中通过对单个浮点数求和来处理。
代码已被注释,但如果您有任何建议为答案添加更多解释,请随时这样做。
#include <immintrin.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int make_floatf(char *, int);
float avx_sfadd(char*);
char error_buf[1024];
#define PERROR() \
do { \
strerror_r(errno, error_buf, 1024); \
printf("Error: %s\n", error_buf); \
fclose(fp); \
return -1; \
} while(0)
/* This function generates a .bin file containing blocks
* of 128 floating point numbers
*/
int make_floatf(char *filename, int nblocks)
{
FILE *fp = NULL;
if(!(fp = fopen(filename, "wb+")))
PERROR();
float *block_ptr = malloc(sizeof(float) * 128); /* 512 Bytes block of 128 floats */
if(!block_ptr)
PERROR();
int j, i;
for(j = 0; j < nblocks; j++)
{
for(i = 0; i < 128; i++)
block_ptr[i] = 1.0;
int ret = fwrite(block_ptr, sizeof(float), 128, fp);
if(ret < 128)
{
free(block_ptr);
PERROR();
}
}
free(block_ptr);
fclose(fp);
return 0;
}
/* This function reads the .bin file as chuncks of 512B
* blocks (128 floating point numbers) and calculates thier sum.
* The final sum in a form of vector is looped through and its
* components are summed up to get the final result.
*/
float avx_sfadd(char *filename)
{
FILE *fp = NULL;
__m256 v1;
__m256 v2;
__m256 sum = _mm256_setzero_ps();
if(!(fp = fopen(filename, "rb")))
PERROR();
struct stat stat_buf;
stat(filename, &stat_buf);
size_t fsize = stat_buf.st_size;
size_t nblocks = fsize / (sizeof(float) * 128);
size_t rem_size = fsize - nblocks * sizeof(float) * 128;
size_t rem_floats = rem_size / (sizeof(float));
printf("File size: %ld\nnblocks:%ld\nnremfloats: %ld\n",\
fsize, nblocks, rem_floats);
/* This memory area will hold the 128 floating point numbers per block */
float *block_ptr = malloc(sizeof(float) * 128);
if(!block_ptr)
PERROR();
int i;
for(i = 0; i < nblocks; i++)
{
int ret = fread(block_ptr, sizeof(float), 128, fp);
if(ret < 128)
PERROR();
/* Summing up vectors in a block of 16 vectors (128 floats) */
int j;
for(j = 0; j < 16; j += 2)
{
v1 = _mm256_loadu_ps(block_ptr + j*8);
v2 = _mm256_loadu_ps(block_ptr + (j+1)*8);
sum += _mm256_add_ps(v1, v2);
}
}
/* Handling the case if the last chunck of the file doesn't make
* a complete block.
*/
float rem_sum = 0;
if(rem_size > 0)
{
int ret = fread(block_ptr, 1, rem_size, fp);
if(ret < rem_floats)
PERROR();
int j;
for(j = 0; j < rem_floats; j++)
rem_sum += block_ptr[j];
}
float final_sum = rem_sum;
float *sum_ptr = (float*)∑ /* The final vector hold the sum of all vectors */
/* Summing up the values of the last vector to get the final result */
int k;
for(k = 0; k < 8; k++)
final_sum += sum_ptr[k];
free(block_ptr);
fclose(fp);
return final_sum;
}
int main(int argc, char **argv)
{
if(argc < 2){
puts("./main filename [nblocks]");
return 0;
}
/* ./main filename number_of_block_to_create (eg. ./main floats.bin 1024 )*/
else if(argc == 3){
if(!make_floatf(argv[1], atoi(argv[2])))
puts("File has been created sucessfully\n");
}
/* ./main filename (eg. ./main floats.bin) to calculate sum*/
else
printf("avx_sum = %f\n", avx_sfadd(argv[1])) :
return 0;
}