gromacs, quake III和vrsqrtps
看标题大家可能觉得三个词汇风马牛不相及,第一个是解蛋白质分子动力学的软件,第二个是上三代宅男最爱雷神之锤,第三个则是一个存在于IntelSSE及AVX中的一个指令,他的作用是快速求平方根的倒数。
起因是这样子的。某天闲着没事,跑去benchmarksgame.alioth.debian.org上看到了万年被压在fortran身体下蹂躏的c++居然翻身了。最不可思议的是,在fortran长项上的多体运算nbody居然被c++拉了一大截性能下来(2倍,5千万步,fortran用时19秒,C++用时9.59秒)。本着fortran居然能被c++搞倒根本是不可能的事情的信念,我开始出发了。
好在他们提供了nbody的f90代码。装好ifort就开始优化,初一眼看过去就好多问题,“怪不得那么慢”,看来现在fortran培训太少了以至于部分不合格码农直接拿java代码改改就过来用了。内存不对齐,行列优先搞混,没有写成向量化,这类错误比比皆是。改了改,差不多跑快了10秒,感觉差不多了,把c++的代码拉下来一编译,瞬间崩溃,在本机上只跑了6秒!
接下去是苦逼的两天,我绞尽脑汁改代码,向量化,发现部分代码无法向量化,然后手工指定,变更慢了……终于,在现实面前,终于低下头,想想是不是c++里用了什么新技术导致比fortran快呢?或许人家也有新的算法呢?(可喜可贺)
跑去看c++的代码部分。就发现他将多体问题中常见的求出距离求倒数r^(-1)=(1/sqrt(x*x+y*y+z*z))的那一步用了很简明的方法写了一下
distance=_mm_cvtps_pd(_mm_rsqrt_ps(_mm_cvtpd_ps(dsquared)));
这里给不是很了解这段代码的人解释一下,dsquared是一个双精度浮点数,_mm_cvtpd_ps是把一个双精度浮点数变成了单精度浮点数。_mm_cvtps_pd是把一个单精度浮点数变成双精度浮点数。_mm_rsqrt_ps就难倒我了,不懂c++的人表示很不解:这是什么东西啊?拿去搜了一把发现这其实是调用SSE指令集vrsqrtps的一个函数,好高端!居然能在C++里直接实现SSE/AVX指令集的调用。再一搜索,差点笑喷出来,搞了半天敢情这个算法就是QuakeIII中开用的快速求平方根倒数的那个算法。也就是传说中的超快算法,利用泰勒展开来求解。所以在intel官网上,关于这一指令的介绍是这么写的“(V)RSQRT[P/S]SComputeapproximatereciprocalofsquarerootofpacked/scalarsingleprecision”
关于这个fastinversesquareroot的具体步骤可以看wiki:http://en.wikipedia.org/wiki/Fast_inverse_square_root
但很遗憾的是,这只是一种近似算法。在大多数情况下一次迭代能收敛到单精度准确值。但二次迭代就变慢了,所以很多人不使用二次迭代。雷神之锤里用的也是一次迭代,而对于一个3d游戏来说这个精度足够了。在浏览之余,我看到了某个小论坛上表明GROMACS其实也是使用这个算法的,于是就去搜了一下,在最新版本4.6.3中也发现了类似代码(以下只是专门为avx指令集编写的头文件中的一个,有兴趣的人可以自己在gromacs源代码目录中用grep-IRn_mm_rsqrt_ps*看一下)
include/gmx_math_x86_avx_128_fma_double.h: /* 1.0/sqrt(x) */
static gmx_inline __m128d
gmx_mm_invsqrt_pd(__m128d x)
{
const __m128d half = _mm_set1_pd(0.5);
const __m128d three = _mm_set1_pd(3.0); /* Lookup instruction only exists in single precision, convert back and forth... */
__m128d lu = _mm_cvtps_pd(_mm_rsqrt_ps( _mm_cvtpd_ps(x))); lu = _mm_mul_pd(_mm_mul_pd(half, lu), _mm_nmacc_pd(_mm_mul_pd(lu, lu), x, three));
return _mm_mul_pd(_mm_mul_pd(half, lu), _mm_nmacc_pd(_mm_mul_pd(lu, lu), x, three));
}
不同之处在于,这个算法是完整的fastinversesquareroot算法(做了一步迭代以保证收敛到单精度),而nbody中的那个fastinverseSquareroot算法则没有做这一步迭代。这里要提醒诸位注意。这一函数对于双精度程序也会降低精度到单精度。由于_mm_cvtpd_ps已经把一个双精度数值降低到单精度了,所以_mm_cvtps_pd拿到的是一个单精度值,而对于单精度数值变成双精度数值,计算机的做法就是剩余位数用随机数填充。所以无论是gromacs编译时如何指定单双精度。拿到距离矩阵已经是单精度了。
接下去做一个比较
双精度1/sqrt
~$./nbody2f50000000
初始能量-0.1690751638285244717874178377
终态能量-0.1690599067877010530658310472
单精度1/sqrt
~$./nbodyc50000000
初始能量-0.1690751638285244717874178377
终态能量-0.1690599067881611849983869433
加黑部分就是误差了。而且这个体系非常小,只有5体运动,而对于一般性蛋白质模拟,可能牵涉到几百万个原子,这个误差积累就相当大了
然后,这里测试了5亿个数,从0开始步长0.0001的1/sqrt测试(也就是0.0001,0.0002,0.0003.....到50000)。我发现在不迭代时,最大误差能达过8.5e-3。当计算3e-4的1/sqrt时,误差能达到8.5e-3,平均误差则在3.57e-7,IEEE允许的单精度误差是1.17e-7,而双精度允许误差则是2.22e-16。所以说不包含一次迭代的误差已经超过单精度允许误差了。
而包含一次迭代的算法,最大误差在2.7e-13,高于双精度允许误差但低于单精度允许误差,有问题的是2.1e-3的1/sqrt(2.1e-3)。平均误差8.67e-19。
从误差分布来,1/sqrt(x)的快速算法中,x与误差是成反比的,x越靠近0,误差越大,x越大误差则越小。
到这里。谜底揭晓了,alioth上那个速度超快的c++程序和gromacs都是通过降低精度来达到高速的,fortran的王者地位依然无法动摇(偷笑)。
Reference
http://software.intel.com/en-us/articles/introduction-to-intel-advanced-vector-extensions
ftp://ftp.gromacs.org/pub/manual/manual-4.5.6.pdf AppendixB.3,我猜没人会看到附录B.3的……
http://scicomp.stackexchange.com/questions/2168/what-is-the-computational-cost-of-sqrtx-in-standard-libraries
gromacs, quake III和vrsqrtps的更多相关文章
- 如何在树莓派上运行雷神之锤III
昨天在树莓派上尝试运行了一下雷神之锤III,流畅运行,效果不错~~~ RPI这个小身板的东东总是可以给你带来惊喜,这里记录一下步骤以备后续再用 先确保RPI的程序和固件都已经更新 sudo apt-g ...
- 速算1/Sqrt(x)背后的数学原理
概述 平方根倒数速算法,是用于快速计算1/Sqrt(x)的值的一种算法,在这里x需取符合IEEE 754标准格式的32位正浮点数.让我们先来看这段代码: float Q_rsqrt( float nu ...
- zz Must read
http://www.opengpu.org/forum.php?mod=viewthread&tid=965&extra=page%3D1 游戏引擎剖析(Game Engine An ...
- TCP/UDP端口列表
http://zh.wikipedia.org/wiki/TCP/UDP%E7%AB%AF%E5%8F%A3%E5%88%97%E8%A1%A8 TCP/UDP端口列表 本条目可通过翻译外语维 ...
- 转:一个Sqrt函数引发的血案
转自:http://www.cnblogs.com/pkuoliver/archive/2010/10/06/1844725.html 源码下载地址:http://diducoder.com/sotr ...
- 第1部分: 游戏引擎介绍, 渲染和构造3D世界
原文作者:Jake Simpson译者: 向海Email:GameWorldChina@myway.com ---------------------------------------------- ...
- [转载]求平方根sqrt()函数的底层算法效率问题
我们平时经常会有一些数据运算的操作,需要调用sqrt,exp,abs等函数,那么时候你有没有想过:这个些函数系统是如何实现的?就拿最常用的sqrt函数来说吧,系统怎么来实现这个经常调用的函数呢? 虽然 ...
- 神秘常量复出!用0x077CB531计算末尾0的个数 -- De Bruijn 序列
http://www.matrix67.com/blog/archives/3985 神秘常量复出!用0x077CB531计算末尾0的个数 大家或许还记得 Quake III 里面的一段有如天书般的代 ...
- Sqrt函数高效实现
转自一个Sqrt函数引发的血案 我们平时经常会有一些数据运算的操作,需要调用sqrt,exp,abs等函数,那么时候你有没有想过:这个些函数系统是如何实现的?就拿最常用的sqrt函数来说吧,系统怎么来 ...
随机推荐
- 相同的问题又出现了,struts2取不出数值
debug里面是有数值的,不知道是不是又是表示错了.全部改成了小写也无济于事.正在想法解决中... 问题解决了,因为自己的不仔细,问题还是出在了action的set,get方法里,不是大小写没注意,改 ...
- windows下gethostbyname 调用失败
gethostbyname()函数属于WinSock API库,而在使用WinSock API之前,必须调用WSAStartup函数,只有该函数成功返回(表示应用程序与WinSock库成功地建立起连接 ...
- java 常见关键字的使用
Super 关键字:指向父类对象的引用空间. 作用:1.当子类和父类存在同名的成员变量时,可以通过super来调用父类的成员变量. 2.super可以用来调用父类的构造方法. Instanceof 关 ...
- TestLink安装全攻略
TestLink安装全攻略 此文章转自该链接--http://www.cnblogs.com/Tcorner/archive/2011/07/26/2117296.html 安装前准备 需要下载xam ...
- 深入理解js——原型的灵活性
在java中,class是一个模子,对象就是按照这个模子刻出来的:但是在JavaScript中对象可以刻成任意的样子. 首先,对象属性可以随时改动.对象或者函数,刚开始new出来之后,可能啥属性都没有 ...
- JAVA课程实验报告 实验三 敏捷开发与XP实践
北京电子科技学院(BESTI) 实 验 报 告 课程:Java程序设计 班级:1353 姓名:韩玉琪 学号:20135317 成绩: 指导教师:娄嘉 ...
- 先进先出集合queue
先进先出集合queue Enqueue添加到集合最后 Dequeue移除集合第一个对象并返回
- Android菜鸟成长记6 -- 网络连接的检查
在android开发中我们要经常考虑到各种问题.在开发android应用时,涉及到要进行网络访问,时常需要进行网络状态的检查,以提供给用户必要的提醒.一般可以通过ConnectivityManager ...
- JavaScript实例
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xht ...
- error in opening zip file 1 错误
项目部署服务启动时会出现: error in opening zip file 1 错误 原来是不同服务器编译过的jar包直接下载后发布有问题,重新上传本地编译好的lib下面的jar包后,启动服务,正 ...