快速排序的汇编级优化(windowds平台VS下dubug release模式对比 linux平台gcc的有无优化对比)
来源:互联网 发布:jenkins 构建php 编辑:程序博客网 时间:2024/06/07 06:17
bySA**406鲁可
前言
如果读者对基本的反汇编,压栈、出栈、参数传递不熟,由于此类文章网上汗牛充栋,本着不向互联网堆砌垃圾的态度(再让我写必定是垃圾,呵呵,因为注定没他们写得出彩),本篇博文不做介绍,可以参考
http://blog.csdn.net/zsy2020314/article/details/9429707
此篇博文只讨论快速排序汇编级的优化技术,对于代码级优化,诸如:单向扫描,双向扫描,选取中间值做划分,选取随机值做划分,不在本文讨论之列,可以参考下面两篇博文
http://wenku.baidu.com/view/4f1f6c360b4c2e3f57276304.html
http://blog.csdn.net/zuiaituantuan/article/details/5978009#t16,该篇博文最后已提到尾递归的代码级优化
程序选自编程珠玑源代码Column 11里最简单的快速排序
<span style="font-size:14px">/*Simplest version, Lomuto partitioning */void qsort1(int l, int u){ int i, m; if (l >= u) return; m = l; for (i = l+1; i <= u; i++) if (x[i] < x[l]) swap(++m, i); swap(l, m); qsort1(l, m-1); qsort1(m+1, u);}</span>
main函数中调用
qsort1(0,n-1);
VS下debug模式
把参数0 和n-1压栈
debug模式下,qsort1调用过程:
debug模式下,先开辟存放局部变量区域:sub esp,0D8h,开辟了216B的栈空间,它还将寄存器ebx、esi、edi压栈,总空间216B+3*4B=228B
接下来 lea edi,[ebp-0D8h]
mov ecx,36H(36H十进制54,54*4B=216B,D8十进制216B)
mov eax,0CCCCCCCCh
rep stos dword ptr es:[edi]
这四句汇编将ebp之下开辟的216B的栈空间初始化为CC字符,特殊调试字符,中断指令,防止栈被攻击,我们平时所见到的“烫烫”便由此而来。
其实,此举微软有吃力不讨好之嫌,因为黑客可以利用这点,发现自己是处于调试状态。
m存放在栈内局部变量区域
i也存放在栈内局部变量内存
以上调用qsort1(l,m-1),qsort1(m+1,u)都有将参数压栈过程,也发现swap开辟了新的栈帧,没有直接展开,都是正常的栈帧机制。
VS下release模式
release模式下调用qsort1之前的压栈:
两者都把参数0 和n-1压栈,不同之处:debug用的是sub指令,release用的是寄存器自减指令dec,更快
没有开辟局部变量空间,edx放l,ebx放u,ecx放i,eax放m
调用qsort1(l,m-1)有将参数压栈过程,release模式下主要省掉了局部变量的空间,用寄存器做了替代,但有将寄存器压栈的代价,编译器用了寄存器做暂存好处有二:一、速度快,二、省空间。
这里栈空间开销为两个参数8B+返回地址ret+压栈寄存器(旧的ebp、 ebx、 esi、edi)=28B,总的来说节省了不少空间。
上面也可以看出调用swap没有开辟新的栈帧,内联直接展开。
按如上分析,即使是release模式下调用一次qsort1最少也需要28B左右,但微软做了鬼斧神工的优化,release模式下调用后半段qsort1(m+1,u)时,根本没有压栈和call的过程,直接是设置新的参数,恢复堆栈平衡,提前作比较(指令预取技术),就跳到了 qsort1+10,微软这么一优化,实际上是沿用了父qsort1()的栈空间,减少了一半的栈空间消耗,并且还做到了调用qsort1(),第二个qsort1()实际上已经是尾递归了,这么分析理论上栈空间平均每次消耗就14B。考虑调用两个qsort的次数不同,实际栈消耗会在14B左右。
GCC下未优化版本
自己动手敲了个最简单的选取首元素作为基准的快速排序
#include<stdio.h>int Partition(int A[],int left,int right);int QuickSort(int A[],int left,int right);int test();int main(){ test(); return 0;}int test(){ int a5]={3,5,4,1,2}; int i; QuickSort(a,0,4); for(i =0;i<5;i++) printf("%d ",a[i]); printf(" ");}/*Partition步骤中哨兵选取的是第一个元素作为哨兵*/int Partition(int A[],int left,int q){ int pivot = A[left]; while(left<right) { while(left<right&&A[right]>=pivoit) –right; A[left]=A[right]; while(left<right&&A[left]<=pivoit) ++left; A[right]=A[left]; } A[left]=pivot; return left;}/*递归调用的QuickSort程序*/int QuickSort(int A[],int left,int right){ if(left<right) { int q = Partition(A,left,right); QuickSort(A,left,q-1); QuickSort(A,q+1,right); }}
GCC下未优化的结果如下:
能看出明显的压栈、调用过程,并且调用QuickSort都是从函数头开始调用
GCC下优化版本
优化后的反汇编结果如下:
优化后反汇编已经看不到调用partition的过程,即使是调用两段QuickSort也不是从头调用,而是从中间某段就开始调用,但用gdb disas命令调试时结果如下:
发现优化后的目标代码反汇编出来的结果和无优化时基本一致,但这段优化过的反汇编代码我暂时还未能读懂,如果读者能看懂,望不吝赐教,如果不方便评论,请发邮件look390125133@126.com,我也只是在VS下偶然一次快排溢出时研究了下,经过计算每次快排居然只有14B的开销,所以才有此心得,入门都算不上。
后记
看优化后的反汇编功力,非一朝一夕能成,需长年不断的积累,而在这方面网上能给出具体代码分析的资料似乎不多,因为大部分开发者并没有关心编译器背后做的事。
参考
GCC编译优化指南
http://lamp.linux.gov.cn/Linux/optimize_guide.html
此篇博文有GCC编译优化技术的一些介绍,但没有具体代码可分析,倘若要化为自己的功力,尚需时日,并且优化应当适可而止为好,将精力留出来做一些其它事情会更有意义。
- 快速排序的汇编级优化(windowds平台VS下dubug release模式对比 linux平台gcc的有无优化对比)
- VS中Dubug和Release版本下的一些区别
- 新浪与腾讯的开放平台对比(下)
- unity3d移动平台性能优化(13):对比法优化
- 实例分析: 如何对比JIT优化前后的汇编代码
- .net平台和java平台的对比
- gcc 连接与汇编,连接的对比
- .Net平台下的第三方Excel类库对比
- 多核平台下的JAVA优化
- 多核平台下的JAVA优化
- 多核平台下的JAVA优化
- 多核平台下的JAVA优化
- 多核平台下的Java优化
- 多核平台下的JAVA优化
- 多核平台下的JAVA优化
- 多核平台下的JAVA优化
- 多核平台下的JAVA优化
- 多核平台下的JAVA优化
- poj 2239 Selecting Courses
- web.xml 中的listener、 filter、servlet 加载顺序及其详解
- 黑马程序员_内部类和包
- RTP与RTCP协议介绍
- android 项目实训—贪吃蛇Snake(一)
- 快速排序的汇编级优化(windowds平台VS下dubug release模式对比 linux平台gcc的有无优化对比)
- iBatis SqlMap的配置总结
- C# 窗体间传值方法大汇总
- 如何做APP界面设计
- Android将应用log信息保存文件
- 4492
- poj 2446(二分匹配) Chessboard
- iPhone开发内存管理
- C++中虚函数工作原理和(虚)继承类的内存占用大小计算