SIMD 编程的优势与SIMD指令:SSE/AVX 与编程demo

2023-10-14 06:58

本文主要是介绍SIMD 编程的优势与SIMD指令:SSE/AVX 与编程demo,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

资源:https://download.csdn.net/download/Rong_Toa/18745608

《Benefits of SIMD Programming | SIMD的优势》

目录

SIMD指令编程demo

正常代码

一次循环计算4次

使用SSE指令

使用AVX指令

性能对比

更多参考


SIMD指令编程demo


本文更新于 2018.10.24

本demo主要使用矩阵相乘, 演示了Intel SSE和AVX内部指令(intrinsics)的显式使用, 并对比了使用gcc和icc(Intel C/C++编译器)使用不同编译选项编译后的代码性能.

完整源码见: https://raw.githubusercontent.com/zzqcn/storage/master/code/c/simd_multiply.c

参考: https://software.intel.com/zh-cn/articles/ticker-tape-part-2

本文的软硬件环境如下:

  • CPU: Intel(R) Xeon(R) CPU E5-2620 v3 @ 2.40GHz
  • 操作系统: CentOS Linux release 7.0.1406
  • 内核: 3.10.0-123.el7.x86_64
  • gcc: 4.8.5 20150623
  • icc: 19.0.0.120 20180804

正常代码


正常代码如下, 直接使用基本算法计算2个矩阵a和b, 结果放在c中:

void multiply(void) {unsigned i;for(i=0; i<N; i++) {c[i] = a[i] * b[i];}
}

一次循环计算4次


可以将正常代码改为一次循环内计算4次乘法, 某种情况下可以提升性能:

void multiply(void) {unsigned i;for(i=0; i<(N & ((~(unsigned)0x3))); i+=4) {c[i]   = a[i]   * b[i];c[i+1] = a[i+1] * b[i+1];c[i+2] = a[i+2] * b[i+2];c[i+3] = a[i+3] * b[i+3];}for(; i<N; i++) {c[i] = a[i] * b[i];}
}

使用SSE指令


Intel SSE指令通过128bit位宽的专用寄存器, 支持一次操作128bit数据. float是单精度浮点数, 占32bit, 那么可以使用一条SSE指令一次计算4个float数:

void multiply(void) {unsigned i;__m128 A, B, C;for(i=0; i<(N & ((~(unsigned)0x3))); i+=4) {A = _mm_load_ps(&a[i]);B = _mm_load_ps(&b[i]);C = _mm_mul_ps(A, B);_mm_store_ps(&c[i], C);}for(; i<N; i++) {c[i] = a[i] * b[i];}
}

注意这些SSE指令要求参数中的内存地址必须对齐于16字节边界, 所以可以用以下函数分配内存:

a = (float*) _mm_malloc(N*sizeof(float), 16);
b = (float*) _mm_malloc(N*sizeof(float), 16);
c = (float*) _mm_malloc(N*sizeof(float), 16);

要使用这些intrinsics, 需要包含x86intrin.h头文件.

 

使用AVX指令


较新的Intel CPU都支持AVX指令集, 它可以一次操作256bit数据, 是SSE的2倍. 使用AVX的代码如下:

void multiply(void) {unsigned i;__m256 A, B, C;for(i=0; i<(N & ((~(unsigned)0x7))); i+=8) {A = _mm256_load_ps(&a[i]);B = _mm256_load_ps(&b[i]);C = _mm256_mul_ps(A, B);_mm256_store_ps(&c[i], C);}for(; i<N; i++) {c[i] = a[i] * b[i];}
}

AVX指令要求内存地址对齐于32字节边界, 所以内存分配代码改为:

a = (float*) _mm_malloc(N*sizeof(float), 32);
b = (float*) _mm_malloc(N*sizeof(float), 32);
c = (float*) _mm_malloc(N*sizeof(float), 32);

 

性能对比


我分别使用gcc和icc以默认选项和-O3选项编译了以上3种版本的代码, 其中用gcc编译AVX版代码时需要加-mavx选项.

代码执行时间如下(单位毫秒):

代码版本gccgcc -O3iccicc -O3
原始946438405404
每次计算4项780438439442
SSE680439405406
AVX545447407406

代码执行时间是连续运行10次取的平均值. 某些时候执行时间起伏时间较大. 下图是根据上表生成的对比图:

由上图可知:

  • 现代编译器在-O3编译时会对代码进行充分优化, 使得本文中的代码无论使不使用SIMD指令性能差距不大
  • gcc编译器默认编译时未对代码进行充分优化, 使得不同算法的代码性能差距较大
  • intel编译器对-O3编译选项不敏感, 总是会自动优化代码

更多参考

  • Intel 64 and IA-32 Architectures Software Developer’s Manual
  • Intel Intrinsics Guide

 

这篇关于SIMD 编程的优势与SIMD指令:SSE/AVX 与编程demo的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/208875

相关文章

揭秘Python Socket网络编程的7种硬核用法

《揭秘PythonSocket网络编程的7种硬核用法》Socket不仅能做聊天室,还能干一大堆硬核操作,这篇文章就带大家看看Python网络编程的7种超实用玩法,感兴趣的小伙伴可以跟随小编一起... 目录1.端口扫描器:探测开放端口2.简易 HTTP 服务器:10 秒搭个网页3.局域网游戏:多人联机对战4.

Java并发编程必备之Synchronized关键字深入解析

《Java并发编程必备之Synchronized关键字深入解析》本文我们深入探索了Java中的Synchronized关键字,包括其互斥性和可重入性的特性,文章详细介绍了Synchronized的三种... 目录一、前言二、Synchronized关键字2.1 Synchronized的特性1. 互斥2.

Springboot处理跨域的实现方式(附Demo)

《Springboot处理跨域的实现方式(附Demo)》:本文主要介绍Springboot处理跨域的实现方式(附Demo),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不... 目录Springboot处理跨域的方式1. 基本知识2. @CrossOrigin3. 全局跨域设置4.

Spring Boot 3.4.3 基于 Spring WebFlux 实现 SSE 功能(代码示例)

《SpringBoot3.4.3基于SpringWebFlux实现SSE功能(代码示例)》SpringBoot3.4.3结合SpringWebFlux实现SSE功能,为实时数据推送提供... 目录1. SSE 简介1.1 什么是 SSE?1.2 SSE 的优点1.3 适用场景2. Spring WebFlu

Nginx指令add_header和proxy_set_header的区别及说明

《Nginx指令add_header和proxy_set_header的区别及说明》:本文主要介绍Nginx指令add_header和proxy_set_header的区别及说明,具有很好的参考价... 目录Nginx指令add_header和proxy_set_header区别如何理解反向代理?proxy

Python异步编程中asyncio.gather的并发控制详解

《Python异步编程中asyncio.gather的并发控制详解》在Python异步编程生态中,asyncio.gather是并发任务调度的核心工具,本文将通过实际场景和代码示例,展示如何结合信号量... 目录一、asyncio.gather的原始行为解析二、信号量控制法:给并发装上"节流阀"三、进阶控制

C#多线程编程中导致死锁的常见陷阱和避免方法

《C#多线程编程中导致死锁的常见陷阱和避免方法》在C#多线程编程中,死锁(Deadlock)是一种常见的、令人头疼的错误,死锁通常发生在多个线程试图获取多个资源的锁时,导致相互等待对方释放资源,最终形... 目录引言1. 什么是死锁?死锁的典型条件:2. 导致死锁的常见原因2.1 锁的顺序问题错误示例:不同

PyCharm接入DeepSeek实现AI编程的操作流程

《PyCharm接入DeepSeek实现AI编程的操作流程》DeepSeek是一家专注于人工智能技术研发的公司,致力于开发高性能、低成本的AI模型,接下来,我们把DeepSeek接入到PyCharm中... 目录引言效果演示创建API key在PyCharm中下载Continue插件配置Continue引言

C#反射编程之GetConstructor()方法解读

《C#反射编程之GetConstructor()方法解读》C#中Type类的GetConstructor()方法用于获取指定类型的构造函数,该方法有多个重载版本,可以根据不同的参数获取不同特性的构造函... 目录C# GetConstructor()方法有4个重载以GetConstructor(Type[]

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template