C/C++ 调用avx/sse函数(Intrinsics函数)
2017-04-06 15:01
1296 查看
最近,实验室同学要写一个计算异或校验的代码,用在raid6里,他说kernel里面用的avx,于是我参考网上一些教程和Intel.org的资料,花了4,5天,踏平了一个大坑之后完成一个简单的对比测试。IDE 用的qt creator,gcc 需要加 -mavx2
代码在我的github上 avx2_c
网上的博客很多,就不介绍基础了,讲些最重要的。
gcc: 低版本的gcc 不支持 -mavx2 参数,具体是在哪个版本加进来的不知道,试试就知道了。
CPU: 其实处理器也需要支持这个指令集,比如avx512需要Intel四代之后才有,可以去对应厂家的官网上查询,或者直接跑代码,看下耗时。
float, long long, double 指的是构成256bit(32Byte)的unit 类型,add,sub这类算数运算需要注意,and,or,xor这些位运算应该就无所谓了(没有测试过)。
看avxintrin.h里面,这三个类型的上面其实还有从char 到double的定义,我还没有使用过,不清楚情况。
下面的代码都以double为例,其他几个类似
两个函数的区别在: 前者没有u,后者有u
带u的表示即使需要复制的内存起始地址不是32的倍数也没事,应该是汇编那层给处理了,至少不会出错;
不带u的,如果起始地址不是32的倍数,运行的时候就会segfault,这个错误卡了我三天,根本不知道错在哪儿,在stackoverflow上搜到一个类似的,外加 Intel org 的视频,看到 _mm_malloc(申请的内存起始地址是32的倍数) 函数才想到,网上的博客都没有提过这个。
带u和不带u这个两个函数是此前 qt creator 的自动补全显示出来的,当时以为没什么区别,就先用了不带u的。
目前我就使用过xor,两个加数,返回和,没有碰到什么坑
和前面load一样也是内存起始地址是否32倍数的问题,不多说
博客可以快速上手,但是作者不一定踩到所有的坑,碰到这种问题就只能求助于官方资料了
传入参数数组or指针
编译过程-g -O
windows linux环境。
特别是尝试过程中有次不加-O2就segfault,加了就正常,搞得我一头雾水
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 的视频暂时不记得在哪儿了。。。
代码在我的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.h3. 关键数据结构
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.html2 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 的视频暂时不记得在哪儿了。。。
相关文章推荐
- C#中 对标准C++动态库的调用
- JAVA如何调用C/C++方法
- 一步一步教你用VC和VB调用C++ DLL
- C++编写“异步调用代理组件”的一点想法
- 托管C++中函数调用的双重转换(Double Thunking)
- 如何在C/C++中调用Java的方法
- 一步一步教你用VC和VB调用C++ DLL
- C++指针直接调用类成员函数探讨(转载)
- C/C++中调用SQLITE3的基本步骤
- C++一种有意思的构造析构函数调用现象
- 为什么c++程序调用c编译器编译的函数需要在调用前加上extern“C”
- 编程经验点滴(二)——《C、C++中函数调用时参数压栈的顺序问题》
- C++ 函数调用约定和名称修饰
- C++虚函数调用的反汇编解析
- InstallShield6.x调用由C++创建的DLL函数
- 混沌 In C++::是类型?还是函数调用?
- c#调用API 和c++dll的参数问题
- C++ 第二篇 函数的调用
- 如何在C/C++中调用Java的方法
- C/C++中如何调用Python方法