C/C++ 调用avx/sse函数(Intrinsics函数)

最近,实验室同学要写一个计算异或校验的代码,用在raid6里,他说kernel里面用的avx,于是我参考网上一些教程和Intel.org的资料,花了4,5天,踏平了一个大坑之后完成一个简单的对比测试。IDE 用的qt creator,gcc 需要加 -mavx2
代码在我的github上 avx2_c

网上的博客很多,就不介绍基础了,讲些最重要的。

1. 环境

OS: 本人是 win10 和 centos7,其他的linux应该都差不多
gcc: 低版本的gcc 不支持 -mavx2 参数,具体是在哪个版本加进来的不知道,试试就知道了。
CPU: 其实处理器也需要支持这个指令集,比如avx512需要Intel四代之后才有,可以去对应厂家的官网上查询,或者直接跑代码,看下耗时。

2. immintrin.h

mingw里面有这个头文件,包含了各种位数(128bit, 256bit, etc)的类型定义、函数封装。下面我用到的是256bit的,类型定义在avxintrin.h,函数在avx2intrin.h

3. 关键数据结构

3.1 类型

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为例,其他几个类似

3.2 函数

3.2.1 load

把连续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的。

3.2.2 运算

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,两个加数,返回和,没有碰到什么坑

3.2.3 store

这是最后一步,整个流程看起来确实很像汇编。

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倍数的问题,不多说

4. 启发

4.1 参考官方资料

在碰到奇怪问题的时候,官方资料的效果不会差
博客可以快速上手,但是作者不一定踩到所有的坑,碰到这种问题就只能求助于官方资料了

4.2 自动补全有奇效

如果没有自动补全,我就得去看头文件里面的函数定义,才知道有u和不带u这两种函数

4.3 调试过程要脑洞

在知道是地址问题前,我测试过
传入参数数组or指针
编译过程-g -O
windows linux环境。
特别是尝试过程中有次不加-O2就segfault,加了就正常,搞得我一头雾水

5 参考

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 的视频暂时不记得在哪儿了。。。

  • 5
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值