汇编效率优化:打破依赖链

上一篇文章指令处理机制说过现代CPU处理指令的方式大多数都是out-of-order,那么为了更好地利用out-of-order这种处理机制,我们在编写程序的时候有必要规避过长的依赖链。

循环

如下面的一个C++例子,目的是计算长度为100的数组的总和:

// Example 9.3a, Loop-carried dependency chain
double list[100], sum = 0.;
for (int i = 0; i < 100; i++) sum += list[i];

上述代码中有100次加法运算,并且每次加法运算都依赖于上一次加法运算的结果。这是一个循环依赖链,一旦循环依赖链非常长,就有可能长时间使得out-of-order处理机制处于无法有效运转的状态。在上述例子中,在执行浮点运算时,只有加法运算i ++ 可以并行执行。

现假设浮点加法的latency为4,throughput为1,那么最优的实现方式就是并行执行4个浮点加法,如此一来就能充分利用浮点加法器的流水线机制。我们可以把代码修改如下:

// Example 9.3b, Multiple accumulators
double list[100], sum1 = 0., sum2 = 0., sum3 = 0., sum4 = 0.;
for (int i = 0; i < 100; i += 4) {
sum1 += list[i];
sum2 += list[i+1];
sum3 += list[i+2];
sum4 += list[i+3];
}
sum1 = (sum1 + sum2) + (sum3 + sum4);

这下循环里面有了四条依赖链,并且每条依赖链的长度为原来的四分之一,由于四条依赖链相互独立,因此可以并行执行,优化后的代码理论上的处理速度为原来的四倍。不过依赖链越多,CPU进行指令调度的难度也就越大,因此不一定能达到理论值。

现在的微处理器运行速度越来越快,可能在一个时钟周期可以同时处理4到5条指令,支持macro-op fusion的处理器甚至能处理更多,对于并行度越高的处理器,就更应该避免过长的依赖链。

普通线性运算

普通线性运算可以打破依赖链:

y = a + b + c + d;

上述式子可以写成以下形式:

y = (a + b) + (c + d);

如此一来a+b与c+d就能并行执行。不过可能有些编译器已经对这种形式的运算做了优化。

寄存器清零

前一篇文章我们讨论过:对整个寄存器进行写入时会使得寄存器重命名、打破依赖链。因此对寄存器清零是一种常用的打破依赖链的方式。

寄存器清零可以用 mov eax, 0 、 xor eax, eax 、 sub eax, eax 等方式。不过常用的是 xor eax,eax 的这种形式,原因如下面表格:

Instruction length side-effect
mov eax, 0 5 bytes no
xor eax, eax 2 bytes no
sub eax, eax 2 bytes depend on carry flag

我们之前也说过,对partial register写入是会导致falce dependence的,因此对partial register的清零是不能打破依赖链的。另外,现代处理器对清零操作的处理不用经过处理阶段,可以直接在寄存器重命名阶段对物理寄存器清零并对接上逻辑寄存器。

SIMD的相关寄存器也是有相应的xor等指令,也常用于打破依赖链。

时间: 2024-08-07 16:58:55

汇编效率优化:打破依赖链的相关文章

汇编效率优化:指令处理机制

大多数情况下,编写程序都不会使用汇编语言而是使用高级语言,原因大致有以下几点: 花费更多时间.高级语言的一行相当于汇编语言的几行.几十行甚至更多. 不够安全.比如说在进行函数调用时PUSH与POP必须成对出现,高级语言中的函数调用会自动为你执行PUSH与POP的操作,但是汇编语言中就必须由程序员自己保证PUSH与POP一致,否则会导致栈错乱,使得程序出现不可预知的错误. 难以调试.相对于高级语言来说,汇编语言除了语法之外,还要对指令有一定的了解,比如说有些指令的操作数只能是常数,有些指令只能有一

Linux之nginx优化与防盗链

Linux之nginx优化与防盗链 Nginx是俄罗斯人编写的十分轻量级的HTTP服务器,Nginx,它的发音为"engine X",是一个高性能的HTTP和反向代理服务器,同时也是一个IMAP/POP3/SMTP 代理服务器.Nginx是由俄罗斯人 Igor Sysoev为俄罗斯访问量第二的 Rambler.ru站点开发. Nginx以事件驱动(epoll)的方式编写,所以有非常好的性能,同时也是一个非常高效的反向代理.负载平衡.但是Nginx并不支持cgi方式运行,原因是可以减少因

jquery选择器效率优化问题

jquery选择器效率优化问题   jquery选择器固然强大,但是使用不当回导致效率问题: 1.要养成将jQuery对象缓存进变量的习惯 //不好的写法 $('#btn').bind("click",function() {}); $('#btn').css("border","1px solid red"); $("#btn").css("background-color","green&qu

(同事的原创)关于效率优化的一点工作心得

文是单位同事胡计平的一个关于效率优化的总结,内容很实用,转贴到blog里,以备自己日后查看,也希望能对更多的人有所帮助 最近写一程序,跟效率优化打上了交道,把其中的体会写下来,供大家讨论分享,我想效率优化工作可以分为如下几个步骤: (1)查找影响效率的瓶颈之处:定位的方法当然是使用时间函数,一般精确的使用GetTickCount就可以,非常精确的使用 function GetCycleCount: Int64;asm  RDTSC;    //得到当前CPU的时钟周期数.end; 想必这个知识大

打破依赖,使用模拟对象,桩对象,隔离框架

打破依赖,使用模拟对象,桩对象,隔离框架 在上节中,完成了第一个单元测试,研究了各种特性,在本节,将介绍一些更实际的例子.SUT依赖于一个不可操控的对象,最常见的例子是文件系统,线程,内存和时间等. 本系列将分成3节: 单元测试基础知识 打破依赖,使用模拟对象,桩对象,隔离框架 创建优秀的单元测试 本节索引: 伪对象.桩对象. 模拟对象 为什么需要伪对象,如何处理 手工新建伪对象 使用隔离框架创建伪对象 伪对象(fake) 桩对象(stub) 模拟对象(mock) 伪对象是一个通用术语,它即可指

Apache网页优化之防盗链和隐藏版本号

Apache网页优化之防盗链 防盗链的作用 防盗链就是防止别人的网址代码里面盗用服务器的图片,文件,视频等相关资源如果别人盗用网站的这些静态资源,明显的是会增大服务器的带宽压力所以作为网站的维护人员,要杜绝我们服务器的静态资源被其他网站盗用 防盗链的配置规则 %{}HTTP_REFERER}: 浏览header中的链接字段,存放一个链接的URL,代表是从哪个链接访问所需的网页 !^: 不以后面的字符串开头 .*$: 以任意字符结尾 NC: 不区分大写 R:强制跳转 防盗链规则匹配说明 Rewri

c++程序的效率优化初涉

能写出稳定高效的程序一直是程序员所追求的,今天就和大家一起探讨一下关于C++程序优化的几点看法. 由于C/C++语言的复杂性,致使C++编译器隐藏了层层幔布,我们不经意的一条语句都可能是编译器幕后几经周折的结果,在要求程序高效运行的环境下,每一条语句都会让我们慎之又慎,而程序优化又是个十分广泛的话题,包括程序架构设计的优化,语言本身的优化,编程技巧和策略等等,如此大的范围非我能力所及,这里谈的优化就是在实际开发中遇到的问题. 一.  举手之劳的小差别 既然说优化就一定要仔细,不放过任何微小的细节

(转)as3效率优化

1.改进算法无论对于那一种程序,好的算法总是非常重要的,而且能够极大地提高程序性能,所以任何性能的优化第一步就是从算法或者说程序逻辑的优化开始,检查自己的程序是否有多余的运算,是否在没有必要的时候做了无用功,往往从这些方面就能找到那些导致性能低下的地方. 2.优化细节代码针对细节总是好的,有一些小技巧比如:用 var obj:Object = {}; 要比 var obj:Object = new Object();要好:var arr:Array = []; 要比 var arr:Array

对Listview控件的效率优化

不管在Android平台还是IOS平台,Listview或者是类似控件,在数据显示方面都占据着相当重要的位置.而作为最重要的数据展示形式,Listview控件或者是类似的需要使用Adapter的控件的加载以及数据展示的效率和优化,就被摆在了一个很重要的位置,本篇文章主要给大家介绍,如何可以实现Listview控件的效率优化. 1.重用已经生成过的Item View 我们都知道,Listview的数据显示,少不了Adapter的设计,所以优化在重点都在如何设计Adapter中.而BaseAdapt