用AVX2指令集优化浮点数组求和
用AVX2指令集优化浮点数组求和
- 一、AVX2指令集介绍
- 二、代码实现
- 0. 数据生成
- 1. 普通数组求和
- 2. AVX2指令集求和:单精度浮点(float)
- 3. AVX2指令集求和:双精度浮点(double)
- 三、性能测试
- 测试环境
- 计时方式
- 测试内容
- 进行性能测试
- 第一次测试
- 第二次测试
- 四、总结
- 个人猜测原因:
一、AVX2指令集介绍
AVX2是SIMD(单指令多数据流)指令集,支持在一个指令周期内同时对256位内存进行操作。包含乘法,加法,位运算等功能。下附Intel官网使用文档。
Intel® Intrinsics Guide
我们本次要用到的指令有 __m256i _mm256_add_pd(__m256i a, __m256i b), __m256i _mm256_add_ps等,(p代表精度precision,s代表single,d代表double)
它们可以一次取256位的内存,并按32/64位一个浮点进行加法运算。下附官网描述。
Synopsis
__m256d _mm256_add_pd (__m256d a, __m256d b)
#include <immintrin.h>
Instruction: vaddpd ymm, ymm, ymm
CPUID Flags: AVX
Description
Add packed double-precision (64-bit) floating-point elements in a and b, and store the results in dst.
Operation
FOR j := 0 to 3i := j*64dst[i+63:i] := a[i+63:i] + b[i+63:i]
ENDFOR
dst[MAX:256] := 0
Performance
Architecture | Latency | Throughput (CPI) |
---|---|---|
Icelake | 4 | 0.5 |
Skylake | 4 | 0.5 |
Broadwell | 3 | 1 |
Haswell | 3 | 1 |
Ivy Bridge | 3 | 1 |
二、代码实现
0. 数据生成
为了比较结果,我们生成从1到N的等差数列。这里利用模版兼容不同数据类型。由于AVX2指令集一次要操作多个数据,为了防止访存越界,我们将大小扩展到256的整数倍位比特,也就是32字节的整数倍。
uint64_t lowbit(uint64_t x)
{return x & (-x);
}uint64_t extTo2Power(uint64_t n, int i)//arraysize datasize
{while(lowbit(n) < i)n += lowbit(n);return n;
}
template <typename T>
T* getArray(uint64_t size)
{uint64_t ExSize = extTo2Power(size, 32/sizeof(T));T* arr = new T[ExSize];for (uint64_t i = 0; i < size; i++)arr[i] = i+1;for (uint64_t i = size; i < ExSize; i++)arr[i] = 0;return arr;
}
1. 普通数组求和
为了比较性能差异,我们先实现一份普通的数组求和。这里也使用模版。
template <typename T>
T simpleSum(T* arr, uint64_t size)
{T sum = 0;for (uint64_t i = 0; i < size; i++)sum += arr[i];return sum;
}
2. AVX2指令集求和:单精度浮点(float)
这里我们预开一个avx2的整形变量,每次从数组中取8个32位浮点,加到这个变量上,最后在对这8个32位浮点求和。
float avx2Sum(float* arr, uint64_t size)
{float sum[8] = {0};__m256 sum256 = _mm256_setzero_ps();__m256 load256 = _mm256_setzero_ps();for (uint64_t i = 0; i < size; i += 8){load256 = _mm256_loadu_ps(&arr[i]);sum256 = _mm256_add_ps(sum256, load256);}sum256 = _mm256_hadd_ps(sum256, sum256);sum256 = _mm256_hadd_ps(sum256, sum256);_mm256_storeu_ps(sum, sum256);sum[0] += sum[4];return sum[0];
}
这里的hadd是横向加法,具体实现类似下图,可以帮我们实现数组内求和:
3. AVX2指令集求和:双精度浮点(double)
double avx2Sum(double* arr, uint64_t size)
{double sum[4] = {0};__m256d sum256 = _mm256_setzero_pd();__m256d load256 = _mm256_setzero_pd();for (uint64_t i = 0; i < size; i += 4){load256 = _mm256_loadu_pd(&arr[i]);sum256 = _mm256_add_pd(sum256, load256);}sum256 = _mm256_hadd_pd(sum256, sum256);_mm256_storeu_pd(sum, sum256);sum[0] += sum[2];return sum[0];
}
三、性能测试
测试环境
Device | Description |
---|---|
CPU | Intel Core i9-9880H 8-core 2.3GHz |
Memory | DDR4-2400MHz Dual-Channel 32GB |
complier | Apple Clang-1300.0.29.30 |
计时方式
利用chrono库获取系统时钟计算运行时间,精确到毫秒级
uint64_t getTime()
{uint64_t timems = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();return timems;
}
测试内容
对1到1e9求和,答案应该为500000000500000000, 分别测试float和double。
uint64_t N = 1e9;// compare the performance of normal add and avx2 adduint64_t start, end;// test floatcout << "compare float sum: " << endl;float* arr3 = getArray<float>(N);start = getTime();float sum3 = simpleSum(arr3, N);end = getTime();cout << "float simpleSum time: " << end - start << endl;cout << "float simpleSum sum: " << sum3 << endl;start = getTime();sum3 = avx2Sum(arr3, N);end = getTime();cout << "float avx2Sum time: " << end - start << endl;cout << "float avx2Sum sum: " << sum3 << endl;delete[] arr3;cout << endl << endl;// test doublecout << "compare double sum: " << endl;double* arr4 = getArray<double>(N);start = getTime();double sum4 = simpleSum(arr4, N);end = getTime();cout << "double simpleSum time: " << end - start << endl;cout << "double simpleSum sum: " << sum4 << endl;start = getTime();sum4 = avx2Sum(arr4, N);end = getTime();cout << "double avx2Sum time: " << end - start << endl;cout << "double avx2Sum sum: " << sum4 << endl;delete[] arr4;cout << endl << endl;
进行性能测试
第一次测试
- 测试命令
g++ -mavx2 avx_big_integer.cpp
./a.out
- 测试结果
方法 | 耗时(ms) |
---|---|
AVX2加法 单精度 | 615 |
普通加法 单精度 | 2229 |
AVX2加法 双精度 | 1237 |
普通加法 双精度 | 2426 |
这里能看到单精度下已经出现了比较明显的误差,并且由于普通求和和avx2求和的加法顺序不一样,导致误差值也不一样。
第二次测试
- 测试命令
现在我们再开启O2编译优化试一试:
g++ -O2 -mavx2 avx_big_integer.cpp
./a.out
- 测试结果
方法 | 耗时(ms) |
---|---|
AVX2加法 32位 | 244 |
普通加法 32位 | 1012 |
AVX2加法 64位 | 476 |
普通加法 64位 | 1292 |
我们发现,比起上一次对整形的测试,浮点型在开启O2优化后反而是AVX2指令集加法得到了明显的提升。
四、总结
可见在进行浮点运算时,用avx2指令集做并行优化,能得到比起整形更好的效果。
个人猜测原因:
- 浮点型加法器比整形加法器复杂许多,流水线操作的效果不那么明显。
- 有可能CPU内的浮点加法器少于整形加法器,导致O2优化乱序执行时的优化效果不如整形理想。
- AVX2指令集可能针对浮点运算有专门的优化,使得浮点运算性能和整形运算更为接近。
用AVX2指令集优化浮点数组求和相关推荐
- [C] 跨平台使用Intrinsic函数范例1——使用SSE、AVX指令集 处理 单精度浮点数组求和(支持vc、gcc,兼容Windows、Linux、Mac)...
作者:zyl910. 本文面对对SSE等SIMD指令集有一定基础的读者,以单精度浮点数组求和为例演示了如何跨平台使用SSE.AVX指令集.因使用了stdint.zintrin.ccpuid这三个模块, ...
- [C] 跨平台使用Intrinsic函数范例3——使用MMX、SSE2指令集 处理 32位整数数组求和...
作者:zyl910. 本文面对对SSE等SIMD指令集有一定基础的读者,以32位整数数组求和为例演示了如何跨平台使用MMX.SSE2指令集.支持vc.gcc编译器,在Windows.Linux.Mac ...
- ARM NEON指令集优化理论与实践
ARM NEON指令集优化理论与实践 一.简介 NEON就是一种基于SIMD思想的ARM技术,相比于ARMv6或之前的架构,NEON结合了64-bit和128-bit的SIMD指令集,提供128-bi ...
- python 依据某几列累加求和_关于Python数组求和的四个问题及详解,让你更加爱Python!...
总结了四个数求和的问题及详解,如果你正在学习Python的话,可以多学习一下. | 问题一:专题概述 代码相关 本节的内容 通过第一个问题来初步了解数组求和的两种常用方法 Two Sum 给定一个整数 ...
- Intel的AVX2指令集解读
原文链接:http://blog.csdn.net/vbskj/article/details/38408213 在Intel Sandy Bridge微架构中,Intel引入了256位SIMD扩展A ...
- java 数组怎么求和,感动,我终于学会了Java对数组求和
前言 看到题目是不是有点疑问:你确定你没搞错?!数组求和???遍历一遍累加起来不就可以了吗??? 是的,你说的都对,都听你的,但是我说的就是数组求和,并且我也确实是刚刚学会.╮(╯▽╰)╭ 继续看下去 ...
- 感动,我终于学会了用Java对数组求和
前言 看到题目是不是有点疑问:你确定你没搞错?!数组求和???遍历一遍累加起来不就可以了吗??? 是的,你说的都对,都听你的,但是我说的就是数组求和,并且我也确实是刚刚学会.╮(╯▽╰)╭ 继续看下去 ...
- neon 指令 c语言,Neon指令集优化快速入门教程
Neon指令集优化快速入门教程 Neon指令集优化快速入门教程 文章目录 1. Neon是什么? 2.Neon为什么速度快 3. Neon基础概念 4. Neon的C语言接口 C语言程序中集成Neon ...
- neon浮点运算_ARM NEON指令集优化理论与实践
ARM NEON指令集优化理论与实践 一.简介 NEON就是一种基于SIMD思想的ARM技术,相比于ARMv6或之前的架构,NEON结合了64-bit和128-bit的SIMD指令集,提供128-bi ...
- 【genius_platform软件平台开发】第二十八讲:NEON指令集优化(附实例)
当在ARM芯片上进行一些例如图像处理等计算的时候,常常会因为计算量太大造成计算帧率较低的情况.因而,需要选择一种更加简单快捷的计算方式以获得处理速度上的提升.ARM NEON就是一个不错的选择. ※ ...
最新文章
- java配置中心开源项目_配置中心搭建(spring-cloud-config-server)
- 安卓天天练练(十一)用list绑数据
- recycleview 嵌套高度问题_简单解决RecyclerView嵌套的RecyclerView条目显示不全和宽度不能铺满...
- Matlab | 数字信号处理:Matlab语言的基本使用方法(matlab代码版)
- (赞助5本)谷歌官方推荐的 TensorFlow 2 “豹书”来了!
- Marketing Cloud extension field technical name
- python仿真智能驾驶_基于Python的3R机器人运动仿真
- .NET Core开发实战(定义API的最佳实践)Source Generators版
- Java 获取集合长度
- hdu1066(经典题)
- ES6中的扩展运算符
- 5、Fiddler如何捕获HTTPS会话
- loadrunner回放时IE模拟器弹出windows安全警告
- 单总线CPU微程序条件判别测试逻辑
- keil4找不到c语言头文件路径,keil4中头文件路径设置的方法汇总
- SQL如何还原数据库
- 北航MOOC系统Android客户端NABC
- 很激励人的一首歌《逍遥叹》(ZT)
- SpaceSyntax【空间句法】之DepthMapX学习:第四篇 凸多边形图分析[未完]
- JavaWeb中 pojo、entity、Dao、bo的含义