最近,实验室同学要写一个计算异或校验的代码,用在raid6里,他说kernel里面用的avx,于是我参考网上一些教程和Intel.org的资料,花了4,5天,踏平了一个大坑之后完成一个简单的对比测试。IDE 用的qt creator,gcc 需要加 -mavx2
代码在我的github上 avx2_c
网上的博客很多,就不介绍基础了,讲些最重要的。
OS: 本人是 win10 和 centos7,其他的linux应该都差不多
gcc: 低版本的gcc 不支持 -mavx2 参数,具体是在哪个版本加进来的不知道,试试就知道了。
CPU: 其实处理器也需要支持这个指令集,比如avx512需要Intel四代之后才有,可以去对应厂家的官网上查询,或者直接跑代码,看下耗时。
mingw里面有这个头文件,包含了各种位数(128bit, 256bit, etc)的类型定义、函数封装。下面我用到的是256bit的,类型定义在avxintrin.h,函数在avx2intrin.h
typedef float __m256 __attribute__ ((__vector_size__ (32),
__may_alias__));
typedef long long __m256i __attribute__ ((__vector_size__ (32),
__may_alias__));
typedef double __m256d __attribute__ ((__vector_size__ (32),
__may_alias__));
float, long long, double 指的是构成256bit(32Byte)的unit 类型,add,sub这类算数运算需要注意,and,or,xor这些位运算应该就无所谓了(没有测试过)。
看avxintrin.h里面,这三个类型的上面其实还有从char 到double的定义,我还没有使用过,不清楚情况。
下面的代码都以double为例,其他几个类似
把连续32字节的内存复制到__m256d 类型变量里
extern __inline __m256d __attribute__((__gnu_inline__, __always_inline__, __artificial__))
_mm256_load_pd (double const *__P)
{
return *(__m256d *)__P;
}
extern __inline __m256d __attribute__((__gnu_inline__, __always_inline__, __artificial__))
_mm256_loadu_pd (double const *__P)
{
return (__m256d) __builtin_ia32_loadupd256 (__P);
}
两个函数的区别在: 前者没有u,后者有u
带u的表示即使需要复制的内存起始地址不是32的倍数也没事,应该是汇编那层给处理了,至少不会出错;
不带u的,如果起始地址不是32的倍数,运行的时候就会segfault,这个错误卡了我三天,根本不知道错在哪儿,在stackoverflow上搜到一个类似的,外加 Intel org 的视频,看到 _mm_malloc(申请的内存起始地址是32的倍数) 函数才想到,网上的博客都没有提过这个。
带u和不带u这个两个函数是此前 qt creator 的自动补全显示出来的,当时以为没什么区别,就先用了不带u的。
extern __inline __m256d __attribute__((__gnu_inline__, __always_inline__, __artificial__))
_mm256_xor_pd (__m256d __A, __m256d __B)
{
return (__m256d) __builtin_ia32_xorpd256 ((__v4df)__A, (__v4df)__B);
}
目前我就使用过xor,两个加数,返回和,没有碰到什么坑
这是最后一步,整个流程看起来确实很像汇编。
extern __inline void __attribute__((__gnu_inline__, __always_inline__, __artificial__))
_mm256_store_pd (double *__P, __m256d __A)
{
*(__m256d *)__P = __A;
}
extern __inline void __attribute__((__gnu_inline__, __always_inline__, __artificial__))
_mm256_storeu_pd (double *__P, __m256d __A)
{
__builtin_ia32_storeupd256 (__P, (__v4df)__A);
}
和前面load一样也是内存起始地址是否32倍数的问题,不多说
在碰到奇怪问题的时候,官方资料的效果不会差
博客可以快速上手,但是作者不一定踩到所有的坑,碰到这种问题就只能求助于官方资料了
如果没有自动补全,我就得去看头文件里面的函数定义,才知道有u和不带u这两种函数
在知道是地址问题前,我测试过
传入参数数组or指针
编译过程-g -O
windows linux环境。
特别是尝试过程中有次不加-O2就segfault,加了就正常,搞得我一头雾水
1 http://www.cnblogs.com/zyl910/archive/2012/10/22/simdsumfloat.html
2 http://www.cnblogs.com/zyl910/archive/2012/08/27/intrin_table_gcc.html
3 http://blog.csdn.net/zyl910/article/category/1128358
4 http://stackoverflow.com/questions/4468420/segmentation-fault-due-to-memory-alignment-in-sse
5 intel org 的视频暂时不记得在哪儿了。。。