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函数来说吧,系统怎么来 ...
随机推荐
- AS
1.修改注释模板: “File“->“Settings” File and Code Templates 然后选中Includes tab下面的File Header. 2.代码提 ...
- jquery的扩展之extend函数
1.$.extend()使用 作用:扩展全局的函数 $.extend({ sayHellow:function(pram){ alert(pram+"hellow"); } }) ...
- delphi模拟按键精灵自动控制PDF页面自动扩边的源代码
需要的环境:Adobe Acrobat 7.0 Professional 和 Quite Imposing Plus 1.5d Acrobat plugin (qi160.exe) 程序界面: ...
- avalon2学习教程13组件使用
avalon2最引以为豪的东西是,终于有一套强大的类Web Component的组件系统.这个组件系统媲美于React的JSX,并且能更好地控制子组件的传参. avalon自诞生以来,就一直探索如何优 ...
- arcgis desktop按ctrl键后地图乱移的解决办法
习惯使用快捷键的,经常会按下ctrl. 但在arcmap中,按下ctrl后,地图乱移.分析发现变为鼠标导航状态,也就是鼠标偏离地图中心,地图就会往鼠标所在方向移动. 解决办法:1. 以前按下esc键, ...
- java-web乱码问题解决
<一>乱码问题(设置tomcat uriencoding=’utf-8’); 统一设置编码过滤器 <1>get请求: request.setCharacterEncoding( ...
- springMVC + Spring + MyBatis 整合
整理下SSM(基于注解)的整合 1. web.xml 配置文件 <?xml version="1.0" encoding="UTF-8"?> < ...
- $使用dom4j可解析 返回&#x等字样的 html转义字符
如果以GET或POST请求某个系统返回,带有 $#x 那很有可能是axis服务器返回的. <?xml version="1.0" encoding="UTF-8&q ...
- zookeeper client 常用操作
#获取权限(类似于登陆) addauth digest admin-user:admin-password #查看权限 getAcl /collections/meixin_product/state ...
- 开源PLM软件Aras详解五 如何让ItemType显示在TOC上
通过上一边ItemType我们大概了解,那么如何让ItemType显示在左侧的菜单上呢,又如何设置增删查改的权限呢,接下来将为演示. 在上一篇中,我们知道了ItemType的结构图,如下图 那么如何让 ...