从编译到执行,C++如何开发SIMD友好的代码?

一:名词解释


Flynn分类法

Flynn于1972年提出了计算平台的Flynn分类法,主要根据指令流和数据流来分类。按照Flynn分类法,计算平台共分为四种类型。

1.单指令流单数据流机器(SISD)

 

2.单指令流多数据流机器(SIMD)

 

3.多指令流单数据流机器(MISD)

 

4.多指令流多数据流机器(MIMD)

 

标量处理器

标量处理器是一种最简单的计算机处理器类型。这类处理器在同一时间内只处理一条数据(整数或浮点数)。标量处理器是一种单指令流单数据流(SISD)处理器。

向量处理器

当今大多数商业 CPU 都包括一些向量处理器指令,较为典型的是 SIMD。从运算上说,标量机只是一个数一个数地进行计算,而向量机则能够对一批数据同时进行加工处理。因此,向量机比标量机的运算速度快,更适合于演算数据量多的大型科学、工程计算问题。

并行

不同于并发的概念,并发偏重于多个任务交替执行,而多个任务之间有可能还是串行。真正意义上的“同时执行”。同时做很多事情。

SIMD

读做(sim-dee),单指令流多数据流(英语:Single Instruction Multiple Data,缩写:SIMD)是一种采用一个控制器来控制多个处理器,同时对一组数据(又称“数据向量”)中的每一个分别执行相同的操作从而实现空间上的并行性的技术。实现这种架构的并行处理机,每次都执行相同的指令,对不同的数据进行处理。这种计算机适合处理矩阵计算。

keypoints:

1.SIMD结构的CPU有多个执行部件,但都在同一个指令部件的控制之下,中央控制器向各个处理单元发送指令,整个系统只要求有一个中央控制器,只要求存储一份程序,所有的计算都是同步的。

2.以加法指令为例,单指令流单数据流(SISD)型CPU对加法指令译码后,执行部件先访问主存,取得第一个操作数,之后再一次访问主存,取得第二个操作数,随后才能进行求和运算;而在SIMD型CPU中,指令译码后,几个执行部件同时访问主存,一次性获得所有操作数进行运算。

3.SIMD不是一种具体的指令格式,经历了mmx->sse->avx这样的发展趋势,但它们都属于SIMD,SIMD更像是一种满足某种规范的技术架构总称。

SSE

Intel在1999年引入了SSE指令,SSE是"Streaming SIMD Extention"(流SIMD扩展)的缩写,SSE是Streaming SIMD Extensions(流SIMD扩展)的缩写。是由 Intel公司,在1999年推出Pentium III处理器时,同时推出的新指令集。SSE是一种SIMD指令集。SSE有8个128位寄存器,XMM0 ~XMM7。这些128位元的寄存器,可以用来存放四个32位的单精确度浮点数。SSE的浮点数运算指令就是使用这些寄存器。

keypoints:

-Intel的初代SIMD指令集是MMX,Multi-Media Extension, 即多媒体扩展,因为它的首要目标是为了支持MPEG视频解码。后来Intel进一步实现了SSE, SSE2~SSE4指令集,给了他们单独的寄存器,之后MMX就停掉了。

-SSE包含70条新的指令,其中大部分指令用于单精度 浮点数据。当对多个数据对象执行完全相同的操作时,SIMD指令可以大大提高性能。

-SSE随后被英特尔扩展为SSE2,SSE3,SSSE3和SSE4。-SSE功能历经几代,最新的版本为AVX(advanced vector extension)

-SSE中大部分指令要求地址是16byte对齐的。-要能够使用 Intel 的 SIMD 指令集,不仅需要当前 Intel 处理器的硬件支持,还需要编译器的支持。

-由于SIMD指令有多个版本,每个版本支持的指令集不同。所以如果你的软件要支持更多的CPU,就要在使用SIMD指令之前知道当前指令运行所在的CPU是否支持这条指令。

-SSE发展到SSE5戛然而止。这里有故事,2007年8月,AMD抢先宣布了SSE5指令集(之前从SSE到SSE4均为Intel制定),Intel火大,转而另起炉灶,2008年4月,Intel公布了AVX指令集规范,随后开始不断进行更新。

 

二:C++如何开发SIMD友好的代码?


使用SIMD技术的开发方法业内有如下几种:

1.使用著名的IPP库,IPP的全称是Intel Integrated Performance Primitives

2.方法是使用编译器的自动向量化(Auto-vectorization)支持。

3.使用编译器指示符(compiler directive),如使用英特尔的C/C++编译器(ICC)编译如下代码,那么ICC便会对#pragma simd指示符下面的for循环做向量化

4.使用Cilk技术。

5.使用编译器的内建函数(intrinsic),例如要使用要使用SSE3,#include,如果不关心使用那个版本的SSE指令,则可以包含所有#include

6.直接使用汇编语言编写汇编函数,C++调用汇编函数

 

编写高效程序需要做到以下几点:

第一,必须选择一组适当的算法和数据结构。

第二,必须编写出编译器能够有效优化以转换成高效可执行代码的源代码。

参考:http://sci.tuomastonteri.fi/programming/sse

三:从编译到执行,发生了什么事情?


我们刚才提到了C++代码,也提到了指令集。

提到了如何写SIMD友好的C++代码和SSE指令有多神奇。

可是,这一切是怎么发生的呢?

从C++源代码编译生成可执行代码,到CPU运行load到内存的这些代码。

这中间,发生了什么事情?

我们来聊聊CPU指令集和GCC编译的那点儿事儿。

先来温习一下计算机体系结构的基础知识:

指令 = 操作码 + 操作数

操作码:表示指令要完成的工作,如存数,取数。

操作数:操作对象的内容或者所在的单元格地址。

计算机工作的过程实际上是快速的执行指令的过程,当计算机在工作时,有两种信息流在流动。一种是数据流,通常是各种原始数据、中间结果等。一种是控制流,是由各种控制指令构成的。

指令的执行过程:

1.取指令,从内存储器取出指令到指令寄存器。

2.分析指令,对指令寄存器中存放的指令进行分析,由译码器对操作码进行译码。将指令的操作码转换成相应的控制电信号,并由地址码确定操作数的地址。

3.执行指令。由操作控制线路发出的完成该操作所需的一系列控制信息。用以完成该指令所需的操作。

4.为执行下一条指令做准备。形成下一条指令的地址。指令计数器指向存放下一条指令的地址,最后控制单元将执行结果写入内存。

上述完成一条指令的执行过程叫做一个“机器周期”;计算机在运行时,CPU从内存读取一条指令到CPU内执行,指令执行完,再从内存读取下一条指令到CPU执行。CPU不断的取指令、分析指令、执行指令,再取下一条指令,这就是程序的执行过程。

再看高级语言编译过程,是将源代码转换为机器可认识代码的过程。编译程序读取源程序(字符流),对之进行词法和语法的分析,将高级语言指令转换为功能等效的汇编代码,再由汇编程序转换为机器语言,并且按照操作系统对可执行文件格式的要求链接生成可执行程序。

C/C++源代码编译成相应平台下的可执行文件需要经过如下步骤:

1、预处理

2、编译

3、汇编

4、链接

接下来我们探索一下C++编译的机器码如何和SIMD挂上了钩的?

首先,需要被执行的机器码先要被OS调度到内存之中, 程序执行时, 机器码依次经过了Memory--Cache--CPU fetch, 进入CPU流水线, 接着就要对它进行译码了, 译码工作生成的是CPU内部数据格式, 微码。特别需要注意的是:CPU不需要任何形式的存储介质去存储指令集, 因为"译码"这个步骤就是在对指令集里规范的机器码做解码。具体的指令编程机器码后就会变成数字电路的开关信号。其中某几段会作为控制信号,控制其他部分的数据走不同的电路以执行运算。

关键点就在这里了,从汇编到机器码这步是汇编程序翻译的。汇编程序知道某条指令要翻译成什么样的机器码。汇编的前提是一定要有格式,支持什么指令, 指令带什么限制条件, 用什么操作数, 用什么地址, 其实都是指令集规范的内容, 如果写错了, 就无法翻译成机器码。

比如C++编译后的汇编文件中某条支持SSE的某条汇编语句:

movaps  %xmm1, %xmm4

翻译成如下的机器码:

26 66 c7 84 c8 44 33 22 11 78 56

到真正执行的层面,CPU将指令(一个由0,1构成的字符串)输入译码电路,译码电路根据指令集的描述,生成各种控制信号。

控制整体晶体管逻辑电路开始工作。这个机器码就是SIMD的机器码,在单个时钟周期里能够并行的从内存中读取和计算数据。

FAQ:

1.ClickHouse怎样用到SIMD技术?

目前Clickhouse通过编译器的内建函数(intrinsic)开发的方式来利用到SIMD技术。

2. ClickHouse为什么不用更牛逼的MIMD ?

MIMD和SIMD只是体系结构的两种概念,并不是说采用SIMD支持的指令集就无法使用MIMD操作。实现SIMD概念的SSE3(举例),就支持某些MIMD操作。另外,目前笔者也没有发现有C++能够支持MIMD机器的优化。

注:由于每个执行单元的指令流都是相同的,SIMD模式将指令的获取时间均摊到每一个执行单元。但是,当指令流出现分支,指令就会被序列化。而MIMD模式的设计主要是为了处理不同指令流,当指令流出现分支,它不需要对线程进行阻塞。然而它需要更多指令存储以及译码单元,这就意味着硬件需要更多的硅,同时,为了维持多个单独的指令序列,它对指令带宽的需求也非常的高。一般使用SIMD与MIMD的混合模式才是最好的方案。用MIMD的模式处理控制流,用SIMD的模式处理大数据,在CPU上使用SSE/MME/AVX指令扩展集时就是采用的SIMD与MIMD的混合模式

3.如何编写可以自动矢量化的代码?

参考:http://www.jianshu.com/p/186339c16e8c

这个例子说明,现在处理器具备相当的计算能力,但是我们需要按照特定程式化的方式来编写程序,就可以将这些能力诱发出来。

题外话:

C++语言之父Bjarne在“2016 C++及系统软件技术大会”上。做了题为《What C++ is and what it will become》的演讲。

what is C++ --

他用两行话描述了C++是什么:

1.“Direct map to hardware” -- 直接映射到硬件

2.“Zero overhead abstraction” -- 0负担抽象

对于1,C++的字节对齐以及多指令集的支持,使之能够直接翻译对应到CPU的指令。C++的数据类型也可以直接对应到CPU支持的数据类型。无需进行转换。可谓效率奇高。

对于2,今天的JAVA、C#都有抽象,但有些语言为了抽象付出的代价很大,资源消耗多,运行速度慢,C++基本0负担,无额外开销。

what it will become --

提到了10项内容,第8项提到了在语言层面支持SIMD向量和并行化算法。

期待语言层面的SIMD支持和并行算法封装。这样,在编写能够支持SIMD优化代码时,可能就不用关注太多的细节了。



作者:_金科
链接:https://www.jianshu.com/p/6b1bbbefbb70
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/438212.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Nacos介绍

Nacos 英文全称为 Dynamic Naming and Configuration Service,是一个由阿里巴巴团队使用 Java 语言开发的开源项目。 参考:home (nacos.io) Nacos 是一个更易于帮助构建云原生应用的动态服务发现、配置和服务管理平台,可以将 Nacos 理解成服务注册中心…

在部署 C#项目时转换 App.config 配置文件

问题 部署项目时,常常需要根据不同的环境使用不同的配置文件。例如,在部署网站时可能希望禁用调试选项,并更改连接字符串以使其指向不同的数据库。在创建 Web 项目时,Visual Studio 自动生成了 Web.config、Web.Debug.config、We…

设计模式之Factory

设计模式之Factory 2016-08-04 11:57 设计模式总共有23种模式这种都仅仅是为了一个目的:解耦解耦解耦...(高内聚低耦合满足开闭原则) 介绍: Factory Pattern有3种当然是全部是creational pattern。 1.Simple Factory Pattern 2.Factory…

C#多线程之旅(七)——终止线程

阅读目录 一、什么时候用Thread.Abort();二、Thread.Abort的用法三、无法终止线程的情形四、Catch块中抛出异常五、Finally块中抛出异常六、Abort调用的时间先交代下背景,写《C#多线程之旅》这个系列文章主要是因为以下几个原因:1.多线程在C/S和B/S架构中…

ASP.NET MVC 入门7、Hellper与数据的提交与绑定

本系列文章基于ASP.NET MVC Preview5. ASP.NET MVC提供了很多Hellper的方法,Hellper就是一些生成HTML代码的方法,方便我们书写HTML代码(有一部分的朋友更喜欢直接写HTML代码)。我们也可以利用.NET 3.5的扩展方法来书写我们自己的Hellper。 例如&#x…

ASP.NET MVC 入门8、ModelState与数据验证

ViewData有一个ModelState的属性,这是一个类型为ModelStateDictionary的ModelState类型的字典集合。在进行数据验证的时候这个属性是比较有用的。在使用Html.ValidationMessage()的时候,就是从ViewData.ModelState中检测是否有指定的KEY,如果…

ASP.NET MVC 入门9、Action Filter 与 内置的Filter实现(介绍)

本系列文章基于ASP.NET MVC Preview5. 有时候你想在调用action方法之前或者action方法之后处理一些逻辑,为了支持这个,ASP.NET MVC允许你创建action过滤器。Action过滤器是自定义的Attributes,用来标记添加Action方法之前或者Action方法之后…

ASP.NET MVC 入门10、Action Filter 与 内置的Filter实现(实例-防盗链)

本系列文章基于ASP.NET MVC Preview5. 前一篇中我们已经了解了Action Filter 与 内置的Filter实现&#xff0c;现在我们就来写一个实例。就写一个防盗链的Filter吧。 首先继承自FilterAttribute类同时实现IActionFilter接口&#xff0c;代码如下&#xff1a; /// <summary…

base64原理及其编解码的python实现

base64原理及其编解码的python实现base64base64简介base64编码表base64编码原理base64编解码的python实现其他base编码base16base32base36、base58、 base62、 base85、base91、 base92base64 base64简介 base64是一种基于64个可打印字符来表示二进制数据的表示方法。2664&am…

REVERSE-PRACTICE-JarvisOJ-1

REVERSE-PRACTICE-JarvisOJ-1[61dctf]androideasy[61dctf]stheasyDD - Android NormalDD - Android Easy[61dctf]androideasy apk文件&#xff0c;用jadx-gui打开 主要的逻辑为&#xff0c;获取输入&#xff0c;检验输入的长度&#xff0c;输入异或23后与已知数组比较&#xf…

NuGet学习笔记(1) 初识NuGet及快速安装使用

关于NuGet园子里已经有不少介绍及使用经验&#xff0c;本文仅作为自己研究学习NuGet一个记录。 初次认识NuGet是在去年把项目升级为MVC3的时候&#xff0c;当时看到工具菜单多一项Library Package Manager&#xff0c;右键项目文件多了一项Manage Nuget Packages...&#xff0c…

NuGet学习笔记(2) 使用图形化界面打包自己的类库

上文NuGet学习笔记(1) 初识NuGet及快速安装使用说到NuGet相对于我们最重要的功能是能够搭建自己的NuGet服务器&#xff0c;实现公司内部类库的轻松共享更新。在安装好NuGet扩展后&#xff0c;我们已经能够通过NuGet轻松下载自己需要的类库&#xff0c;下面来说一说如何将自己的…

REVERSE-PRACTICE-JarvisOJ-2

REVERSE-PRACTICE-JarvisOJ-2DD - HelloAPK_500DebugMeFindPassDD - Hello macos文件&#xff0c;无壳&#xff0c;ida分析 start函数和sub_100000C90函数没什么作用 主要的逻辑在sub_100000CE0函数&#xff0c;反调试检测和byte_100001040数组的循环变换&#xff0c;最后打印…

NuGet学习笔记(3) 搭建属于自己的NuGet服务器

文章导读 创建NuGetServer Web站点 发布站点到IIS 添加本地站点到包包数据源 在上一篇NuGet学习笔记(2) 使用图形化界面打包自己的类库 中讲解了如何打包自己的类库&#xff0c;接下来进行最重要的一步&#xff0c;从零开始搭建属于自己的NuGet服务器&#xff0c;诚然园子里…

REVERSE-PRACTICE-JarvisOJ-3

REVERSE-PRACTICE-JarvisOJ-3爬楼梯软件密码破解-1Classical CrackMe2Smali爬楼梯 apk文件&#xff0c;放到模拟器里运行一下 “爬一层楼”按钮可按&#xff0c;每按一下&#xff0c;“已爬的楼层”加1层 “爬到了&#xff0c;看FLAG”按钮不可按&#xff0c;应该是“已爬的楼…

管理全局包、缓存和临时文件夹

每当安装、更新或还原包时&#xff0c;NuGet 将管理项目结构多个文件夹之外的包和包信息&#xff1a; name说明和位置&#xff08;每个用户&#xff09;global‑packagesglobal-packages 文件夹是 NuGet 安装任何下载包的位置。 每个包完全展开到匹配包标识符和版本号的子文件…

REVERSE-PRACTICE-JarvisOJ-4

REVERSE-PRACTICE-JarvisOJ-4Classical CrackmeFindKeyClassical Crackme exe程序&#xff0c;输入注册码&#xff0c;输入错误弹窗 查壳发现是.Net程序&#xff0c;dnSpy打开&#xff08;要不要用de4dot unpack无所谓&#xff0c;这里打开的是解包过的程序&#xff09; 按下“…

在解决方案中所使用 NuGet 管理软件包依赖

使用程序包恢复功能可以在提交源代码时, 不需要将 packages 中的程序集文件提交到源代码管理中&#xff0c;大幅减少项目的尺寸。所有NuGet程序包都存储在解决方案的Packages文件夹中。 要启用程序包恢复功能&#xff0c;可右键单击解决方案&#xff08;注意&#xff0c;不是右…

REVERSE-PRACTICE-BUUCTF-1

REVERSE-PRACTICE-BUUCTF-1easyrereverse1reverse2内涵的软件easyre exe程序&#xff0c;无壳&#xff0c;ida分析 左侧函数窗找到main函数&#xff0c;flag提交即可 reverse1 exe程序&#xff0c;运行后提示输入flag&#xff0c;ida分析 左侧函数窗无明显的main函数&#…

REVERSE-PRACTICE-BUUCTF-2

REVERSE-PRACTICE-BUUCTF-2新年快乐[BJDCTF 2nd]guessgamehellowordxor新年快乐 exe程序&#xff0c;运行后提示输入正确的flag&#xff0c;有upx壳&#xff0c;常规工具脱壳即可 ida分析&#xff0c;左侧函数窗找到main函数&#xff0c;F5反汇编&#xff0c;简单的输入比较逻…