浮点峰值那些事儿

本文作者高洋,目前在商汤科技高性能计算组担任总监,对于并行计算颇有心得。本篇为高洋写给高性能并行计算的初学者,核心内容是教初学者用科学手段测量硬件的理论指标。有了这个指标,就能对硬件的能力上限有更深入了解,知道优化工作做到了什么程度,是否还有上升的空间。 此篇干货满满,如果你对高性能计算感兴趣,本篇绝对不可错过。

这个系列的第一篇文章,先谈点轻松的,常用CPU架构浮点峰值的理论计算和实测。做性能优化,先要知己知彼,了解自己优化的CPU的能力上限。这样优化做到什么程度,心里会有数。

本文只介绍Intel x86-64架构,并且只针对单精度float类型。浮点峰值的计算,一般是计算单位时间内,乘法和加法的最大总吞吐量,单位是GFLOPS或者TFLOPS,表示每秒钟计算乘法和加法的总次数。

乘法和加法可能涉及到的指令包括:单独的乘法指令,如vmulps;单独的加法指令,如vaddps;融合乘加指令,如vfmadd231ps等。后者将乘法和加法融合为一条指令,在多数处理器中,三种指令都可以在一个发射端口每周期发射一条,所以乘加指令往往比单独使用乘法或者加法带来翻倍的吞吐量。

先来看x86-64,Intel在2010年推出Sandy Bridge架构(下面简称SNB),首次引入了256位宽的向量指令集AVX,即一条指令可以同时操作8组32位宽的数据类型。SNB架构示意图如下:

六个dispatch ports,其中port0和port1各有一条向量乘法(256-FP MUL)和向量加法(256-FP Add),即一个周期内,SNB架构可以吞吐一条浮点向量乘法和浮点向量加法。由于AVX指令集还不支持融合乘加FMA,浮点峰值计算只能使用这两条指令的总和吞吐量。

综上所述,SNB架构的理论浮点峰值就等于(8Mul + 8Add) * 核心频率 * 核心数。例如SNB桌面高端的i7 2600k,有四个核心,关闭睿频后每核心频率3.4GHz,所以这款CPU的理论浮点峰值(关闭睿频)就是(8+8)*3.4*4=217.6GFLOPS。

如果想写一个小程序实测的话,可以这样设计:设置一个简单的循环,次数足够多,保证每次循环执行一个时钟周期,发射两条无依赖的vmulps和vaddps指令:

.loop:
    vmulps %ymm2, %ymm2, %ymm1
    vaddps %ymm4, %ymm4, %ymm3
    sub $0x1, %rax
    jne .loop

.loop是循环体;前两条vmulps和vaddps指令,输入和结果使用不同的寄存器,这样前后两个相邻循环的同一条指令产生WAW型寄存器依赖,通过寄存器renamer部件可以解决;然后用sub指令每次给rax寄存器里的循环计数减一,如果减到0,会修改状态寄存器的值,jne指令不会跳转,循环结束。sub指令和jne指令在SNB架构下和可以做到宏融合,形成单一的微指令,不会产生延迟,在port5和前面的乘法加法同周期分派。

由于本人手头没有SNB架构的处理器,暂时不能测试。SNB下一代微架构Ivy Bridge只是工艺升级版的SNB,各种特性几乎和SNB一样。我们再看下一代,即2013年推出的Haswell架构:

Haswell架构增加了AVX2和FMA指令集,其中FMA指令集就是256位的融合乘加指令,根据架构图中所示,port0和port1端口各有一个FMA单元,所以Haswell架构的理论峰值就等于2port * 8 * 2(mul+add) * 频率 * 核心数。例如i7系列的最高版本i7 4790k,四核心,关闭睿频固定4GHz频率,那么浮点峰值就是2 * 8 * 2 * 4 * 4 = 512 GFLOPS,每个核心的峰值就是128 GFLOPS。

如果要实测Haswell的理论峰值,与SNB大部分思路是相同的。除了用到不同的指令以外,与SNB还有如下一些区别:fma指令的某个参数寄存器,既做输入也做输出,这样前后两个相邻循环间的同一条fma,就形成了RAW型寄存器依赖。所以如果每个循环还要求一个周期执行,并发射两条fma指令,相邻循环间肯定就会有数据依赖发生,要等待前一个循环的两条fma执行完毕才能进行第二次循环的两条fma,这就会导致流水线停顿,浪费时钟周期。假设fma指令的执行周期是k,那么解决办法就是一次循环内安排2k个数据无依赖的fma指令。经过试验,我们发现放10条fma指令到一个周期,浮点吞吐刚好达到最大值:

.loop:
    vfmadd132ps %ymm0, %ymm0, %ymm0
    vfmadd132ps %ymm1, %ymm1, %ymm1
    vfmadd132ps %ymm2, %ymm2, %ymm2
    vfmadd132ps %ymm3, %ymm3, %ymm3
    vfmadd132ps %ymm4, %ymm4, %ymm4
    vfmadd132ps %ymm5, %ymm5, %ymm5
    vfmadd132ps %ymm6, %ymm6, %ymm6
    vfmadd132ps %ymm7, %ymm7, %ymm7
    vfmadd132ps %ymm8, %ymm8, %ymm8
    vfmadd132ps %ymm9, %ymm9, %ymm9
    sub $0x1, %rax
    jne .loop

所以haswell架构下fma指令的执行延迟就是5个周期。感兴趣的同学可以试试改变循环内fma指令数量,看看测出来的GFLOPS有何变化。

我在自己的i7 4790k上测试了这个例子,结果如下:

测试同时包含了单精度和双精度两种,分如下四种情况:单线程,双线程,四线程和八线程。i7 4790k是四核八线程的处理器,所以根据前三组测试可以发现,实测峰值和理论峰值极为接近,与使用核心数量严格成比例。最后一组八线程使用了i7处理器的八个超线程,发现测出来的值和四线程几乎一样。这是由于超线程只是为每个核心提供两组线程上下文单元,两个线程其实是共享各种核内运算部件的。超线程的好处是线程之间往往没有各种数据依赖关系,两个线程的指令流可以尽量填充流水线并充分利用乱序多发射能力。互相掩盖对方的各种延迟,提高每个核心的利用效率。我们这个测试程序已经完整地利用了浮点乘加的吞吐能力,所以超线程并不带来好处。

同样的方法也可以测试ARM架构的CPU浮点峰值,但是需要注意ARM NEON指令包含两种乘加方式:向量乘向量,以及向量乘标量。注意这两种方式的指令延迟可能是不一样的,差距甚至会比较大,需要分别测出两者的延迟。Intel也已经推出了基于Skylake-X架构的AVX512指令集的Xeon服务器CPU,在FMA指令的基础上又提升了一倍的浮点峰值性能。新的架构很有意思,高端版本支持一个周期发射两条AVX512版本的乘加指令,其中一条来自port0和port1的256位FMA的端口融合,另外一条来自port5。中低端版本的Skylake-X就去掉了port5的乘加单元,只与Haswell的浮点能力相当。大家在选购服务器的时候一定要考察清楚。

有了峰值性能的数据,我们在写矩阵乘法和卷积运算这些计算密集型算法的时候,就有了一个理论上限。通过测试结果与理论上限的差距,评估算法的可能优化空间。除了浮点峰值性能,还有很多处理器的指标对我们优化和评估程序性能都有重要意义,例如内存带宽和延迟,各级cache的带宽和延迟,分支预测失败的惩罚周期...... 本文就不一一展开详述了。

上面测试汇编代码的完整版本我放在了github上:pigirons/cpufp

https://github.com/pigirons/cpufp

参考资料:

[1] Intel® 64 and IA-32 Architectures Software Developer’s Manual [2] Intel® 64 and IA-32 Architectures Optimization Reference Manual [3] 《探求计算性能的极限》,王琤 [4] Intrinsics Guide https://software.intel.com/sites/landingpage/IntrinsicsGuide/

原文地址

https://zhuanlan.zhihu.com/p/28226956

原文发布于微信公众号 - AI科技大本营(rgznai100)

原文发表时间:2017-08-02

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏前端工程

浅谈前端/软件工程师的代码素养

“程序是写给人读的,只是偶尔让计算机执行一下。”

24660
来自专栏PPV课数据科学社区

数据结构与算法–关键路径

关键路径与无环加权有向图的最长路径 现在考虑一个这样的问题:你今天事情比较多,要洗衣服、做作业还要烧水洗澡,之后出去找朋友玩。假设洗衣服要20分钟,烧水要30分...

39570
来自专栏ThoughtWorks

改善单元测试的新方法|洞见

鄢倩 ThoughtWorks 我们为什么要写单元测试? "满足需求"是所有软件存在的必要条件,单元测试一定是为它服务的。从这一点出发,我们可以总结出写单元测试...

29950
来自专栏哲学驱动设计

090609 T 领域建模

领域建模的重要性     以数据为中心的应用程序开发,面向过程分析方法的核心在于对数据库的设计。     而现在以面向对象的方式进行分析(OOA,OOD)时,领...

20180
来自专栏做全栈攻城狮

电脑小白学习软件开发-C#语言基础之循环重点讲解,习题

本教程是基础教程,适合任何有志于学习软件开发的人。当然因为技术的连贯性,推荐按照顺序查看。

8620
来自专栏恰童鞋骚年

自己动手写游戏:飞机大战

  要说微信中最火爆的小游戏是哪款,可能既不是精心打造的3D大作,也不是《植物大战僵尸2》,而是微信5.0刚开启时的《飞机大战》。

49710
来自专栏用户2442861的专栏

C/C++ 笔试面试(2)——二分查找

转载于 http://blog.csdn.net/yangtrees/article/details/8898997

8810
来自专栏红色石头的机器学习之路

多元函数的泰勒(Taylor)展开式

多元函数的泰勒展开式 实际优化问题的目标函数往往比较复杂。为了使问题简化,通常将目标函数在某点附近展开为泰勒(Taylor)多项式来逼近原函数。 一元函数在点x...

38990
来自专栏Java编程技术

UML建模(用例图)

在软件生命周期的整个过程中,用例图是软件需求分析到软件交付的第一步,用例图的主要目的是说明这个软件的使用者是谁,使用者要使用那些功能,以及使用者需要向软件提供什...

11620
来自专栏LET

glTF简介

499100

扫码关注云+社区

领取腾讯云代金券