POPPUR爱换

 找回密码
 注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

搜索
查看: 7852|回复: 8
打印 上一主题 下一主题

[转帖] 【技术探讨】关于那个x87和SSE指令集

[复制链接]
跳转到指定楼层
1#
发表于 2010-7-9 21:37 | 只看该作者 回帖奖励 |正序浏览 |阅读模式
在其他论坛上看来的,觉得比还不错;大家看看吧。
原帖由tpy显卡论坛的:开普勒,完成。赞~~~

(本帖欢迎技术讨论,谢绝漫骂)

今天看到不少人在讨论x87和SSE指令,各种观点满天飞。例如有认为x87是古老技术,现在已经是SSE大行其道,x87已经绝迹,也有人说某物理引擎用x87是故意为之,其实用SSE开发更简单。也有认为用x87也很正常,不过是没有针对SSE做优化。本人根据自己的一些编程经验来谈谈x87和SSE。

开发环境就选VS2008吧,这个应该是x86平台C++开发环境中使用最广泛的,有普遍意义。

1、x87

物理引擎是计算物体运动的,相信一定少不了矢量计算。举个简单例子,例如有两个4D矢量A和B,我们需要把它们相加,放到矢量C中。C语言中是这样写的:

C.x = A.x + B.x;
C.y = A.y + B.y;
C.z = A.z + B.z;
C.w = A.w + B.w;

(变量定义我就不写了)

也就是说各元素单独相加。看看用VS2008在Release模式下用默认选项编译出来的汇编代码:



嗯,一点SSE的影子都没有,编译器笨得可以啊,全用x87。

2、SSE/SSE2

既然编译器那么笨,我只好亲自动刀子了。xyzw四个浮点数一起算加法,可以打包在一个XMM寄存器里用SSE2指令计算嘛,代码改成:

__m128 result = _mm_add_ps(_mm_load_ps((float *)&A), _mm_load_ps((float *)&B));
_mm_store_ps((float *)&C, result);

看看编译后的汇编:



嗯,不错,一个addps指令(属于SSE2指令集)搞掂4个浮点数加法,比起之前调用4次fadd要好多了(理论上吞吐量可以达到4倍)。

不过,貌似很多计算不是那么简单,能直接打包成SSE2的,例如以下两个很常用的计算:

矢量点乘(求功、求夹角等):
r = a.x * b.x + a.y * b.y + a.z * b.z;

矢量叉乘(求法线):
r.x = a.y * b.z - a.z * b.y;
r.y = a.z * b.x - a.x * b.z;
r.z = a.x * b.y - a.y * b.x;

交叉相乘或相加,无法直接打包,如果硬要打包计算,宽度又不够,而且要浪费很多打包指令,效率低下。

不过聪明的网友应该会想到,很多情况下需要计算的矢量是很多个的,如果放在数组里的话,完全可以每4个矢量打包在一起计算(把循环向量化)。例如计算叉乘,完全可以把a[0].x到a[3].x放到一个XMM寄存器,a[0].y到a[3].y放到一个XMM寄存器……那样a[0].y*b[0].z、a[1].y*b[1].z、a[2].y*b[2].z、a[3].y*b[3].z就可以用一条SSE2指令计算,以此类推。

不过,这样做的话属于带stride访存,而SSE访存模式不支持Gather/Scatter,需要借助中间操作把需要打包的数据排列成连续128位,访存效率较低。而为了优化访存效率,可以在数组存储形式上做优化,这就是 Array of Structures (AoS) 和 Structure of Arrays (SoA) 的问题了,呵呵,再引申下去就扯远了。

不管怎样,使用SSE、SSE2指令集总的来说肯定比x87性能要高一些。例如本人之前写过一个SHA-1编码的纯SSE版本,比非SSE版本快1倍左右。不过效率是以牺牲可读性为代价的,而且这种直接写成SSE Intrinsics的方式,需要花大量时间去做人工打包的工作。还有就是某些程序员的优化水平实在厉害,优化出来完全变了个样,在榨干性能的同时,代码也非常让人费解。

当然如果有人直接把一些常用的计算过程写成SSE优化库,直接提供给开发者使用,那就最好。

3、VS2008对SSE/SSE2的优化

刚才用默认编译选项编译C代码,没有发现编译出SSE指令。不过其实VS2008有SSE指令集选项,只是默认是关闭的:



例如我在这里指定使用SSE2指令集,再重新编译刚才那个C代码:



嗯,VS2008编译出SSE2指令了。不过仔细看看,杯具啊,用是用了,但全是带ss的,即它用128位的XMM寄存器来放32位的单精度数据,而每条SSE2指令仅仅计算1个数而非4个数,打包失败。不过XMM寄存器的访问怎么也应该比x87的浮点寄存器快一点,效率应该还是有一丁点提升的。

再来测试一下下面这个代码:



看看会不会展开循环到SSE2指令:



同样杯具啊,看来VS的编译器比较笨,那么明显的循环也不会展开并向量化,仅仅是拿XMM寄存器当x87浮点寄存器来用了。我记得好像ICC的编译器是有这能力的,看来还是Intel牛一些,不过现在ICC限制在AU上SSE的性能也不太厚道。

再用2个我以前写的其它程序来测试一下VS2008对SSE指令的优化能力,结果是:1、我的一个以计算sin/log函数为主的程序,打开SSE2选项,效率没有提升,看代码,只有少数语句被编译成SSE2指令。2、我以前写的一个DCT2D 8x8计算程序(用于JPEG编码),打开SSE2选项,效率提升18%。

嗯,看来使用比较广泛的VS2008对SSE指令集的支持还是比较一般。

个人对SSE等SIMD指令集的看法:其实我们惯常的C语句的写法与SIMD打包的计算方式还是有多少出入的,如果想既使用常规C语句去写,保证可读性,又要求能高效编译出SIMD指令集,我觉得还是得摒弃现有的程序写法,采用新的编程模型,一种能支持细粒度并行的编程模型。这方面CUDA编程模型和CT编程模型都不错。


结论:
1) 调用指令集由编译器决定,用VS的编译器使用:X87指令集很正常;用Intel的ICC,会尽可能调用SEE指令集,但也有可能没有啥优化,同时这个由Intel完成的编译器,直接会限制在AU上SSE的性能。
2) 优化程度 和 代码的可读性 是一对矛盾,相互制约。我也觉得这PhysX估计也是用高级语言写的,C,C++; 总不会NV为了降低CPU上的表现,用汇编写这个物理引擎吧~~~?!?!
9#
发表于 2011-8-2 22:11 | 只看该作者
前几天看到那个用sleep命令的天才排序法,简单易懂,但是的确天才!
回复 支持 反对

使用道具 举报

8#
发表于 2011-8-2 21:41 | 只看该作者
多看老外写的程序,往往让人看不懂的程序要么是因为高明,要么是程序员求都不懂乱在整。
回复 支持 反对

使用道具 举报

7#
发表于 2010-7-10 17:21 | 只看该作者
完全看不懂嘛

回复 支持 反对

使用道具 举报

6#
发表于 2010-7-10 00:13 | 只看该作者
http://www.pcinlife.com/article/ ... 4158113d191_26.html

破解 ICC QxP 。

按理说,只要 QxW 就能应用 SSE2。
回复 支持 反对

使用道具 举报

5#
发表于 2010-7-10 00:04 | 只看该作者
我倒很想知道除了intel自己以外还有几个商业软件公司会为了优化SSE去专门写汇编?
回复 支持 反对

使用道具 举报

4#
发表于 2010-7-9 23:44 | 只看该作者
嗯,要么Intel给NV的程序员发工资,要么intel给NV派遣外派工程师或者接了NV的外包软件开发,要么intel给MS塞钱,要求MS把SSE在VS的支持做得更好。。。

不过微软确实责任不小,不是据说SSE已经普及很多年了吗,微软最流行的开发工具VS竟然这么难做SSE的开发。希望现在VS开始支持的GPU编程不那么杯具。
回复 支持 反对

使用道具 举报

3#
发表于 2010-7-9 23:35 | 只看该作者
楼主这个是技术转贴....不过这样的贴子是会淹没在X87指令是恶化的声讨中的......哎.
kvip 发表于 2010-7-9 23:29



哎,看不懂~~~~你能看得懂?
回复 支持 反对

使用道具 举报

2#
发表于 2010-7-9 22:13 | 只看该作者
。。。。看不懂。。。。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

广告投放或合作|网站地图|处罚通告|

GMT+8, 2024-11-28 00:36

Powered by Discuz! X3.4

© 2001-2017 POPPUR.

快速回复 返回顶部 返回列表