深入探讨用位掩码代替分支(2):汇编代码分析
来源:互联网 发布:vb 表格控件 编辑:程序博客网 时间:2024/04/30 08:57
查看编译器生成的汇编代码,有助于我们分析程序的性能。
1 让VC6输出编译的汇编代码
用VC6打开前一篇文章(http://blog.csdn.net/zyl910/article/details/7345655)的工程“noifCheck.dsw”。
首先需要配置项目设置——
1.点击菜单栏 “工程”->“Project Settings”打开“Project Settings”对话框。
2.将“Settings For:”设为“Win32 Release”。
3.将右侧的选项卡换到“C/C++”面板。
4.点击“Category:”组合框,选择“Listing Files”(列表文件)。
5.点击“Listing file type:”组合框,选择“Assembly with Source Code”(汇编与源码)。
6.点击“OK”保存设置。
然后点击菜单栏 “编译”->“Batch Build...” 进行批生成——
编译完成后,可在“Release”文件夹中找到“noifCheck.asm”,它就是编译器为“noifCheck.c”生成的汇编源码。
2 分析“<0”处理
打开“noifVC6.asm”,找到“<0”处理的相关汇编代码——
; 45 : // 检查 “<0”处理; 46 : printf("[Test: less0]\n");pushOFFSET FLAT:??_C@_0P@GACN@?$FLTest?3?5less0?$FN?6?$AA@ ; `string'call_printfaddesp, 4movesi, OFFSET FLAT:_bufmovedi, 255; 000000ffH$L53259:; 47 : for(i=0; i<0x8100; ++i)// [-32768, 255]; 48 : //for(i=0x7FFE; i<=0x8002; ++i)// [-2, 2]; 49 : {; 50 : // 加载数值; 51 : n = buf[i];movbx, WORD PTR [esi]; 52 : ; 53 : // 用if分支做饱和处理; 54 : m = n;moveax, ebx; 55 : if (m < 0) m = 0;cmpbx, bpmovDWORD PTR _m$[esp+28], eaxjgeSHORT $L53324movDWORD PTR _m$[esp+28], ebpmoveax, ebp$L53324:; 56 : by0 = (BYTE)m;; 57 : ; 58 : // 用位掩码做饱和处理.用求负生成掩码; 59 : by1 = (BYTE)(n & -(n >= 0));setgeclnegclandcl, bl; 60 : if (by1 != by0)printf("[Error] 1.1 neg: [%d] %d!=%d\n", n, by0, by1);// 验证cmpcl, almovBYTE PTR _by1$[esp+28], cljeSHORT $L53265movedx, DWORD PTR _by1$[esp+28]andeax, ediandedx, edipushedxpusheaxmovsxeax, bxpusheaxpushOFFSET FLAT:??_C@_0BO@OGNG@?$FLError?$FN?51?41?5neg?3?5?$FL?$CFd?$FN?5?$CFd?$CB?$DN?$CFd?6?$AA@ ; `string'call_printfmoveax, DWORD PTR _m$[esp+44]addesp, 16; 00000010H$L53265:; 61 : ; 62 : // 用位掩码做饱和处理.用带符号右移生成掩码; 63 : by2 = (BYTE)(n & ~((signed short)n >> 15));movcx, bxsarcx, 15; 0000000fHnotclandcl, bl; 64 : if (by2 != by0)printf("[Error] 1.2 sar: [%d] %d!=%d\n", n, by0, by2);// 验证cmpcl, almovBYTE PTR _by2$[esp+28], cljeSHORT $L53260movecx, DWORD PTR _by2$[esp+28]andeax, ediandecx, edimovsxedx, bxpushecxpusheaxpushedxpushOFFSET FLAT:??_C@_0BO@EGHC@?$FLError?$FN?51?42?5sar?3?5?$FL?$CFd?$FN?5?$CFd?$CB?$DN?$CFd?6?$AA@ ; `string'call_printfaddesp, 16; 00000010H$L53260:addesi, 2cmpesi, OFFSET FLAT:_buf+66048jlSHORT $L53259; 65 : }
下面对其进行整理和分析。
2.1 用if分支做饱和处理
C语言源码——
// 用if分支做饱和处理m = n;if (m < 0) m = 0;by0 = (BYTE)m;
汇编代码——
xorebp, ebp; 在循环外将ebp设为0。不列入统计。...movesi, OFFSET FLAT:_buf; 在循环外将esi指向buf。不列入统计。...movbx, WORD PTR [esi]; 加载数值。不列入统计。moveax, ebx; *复制数据到eax。cmpbx, bp; *与0进行比较。movDWORD PTR _m$[esp+28], eax; 存储数值。不列入统计。jgeSHORT $L53324; *若大于等于就跳转movDWORD PTR _m$[esp+28], ebp; 存储数值。不列入统计。moveax, ebp; *否则将eax设为0。$L53324:
核心指令共4条。
2.2 用位掩码做饱和处理.用求负生成掩码
C语言源码——
// 用位掩码做饱和处理.用求负生成掩码by1 = (BYTE)(n & -(n >= 0));
汇编代码——
cmpbx, bp; *与0进行比较。...setgecl; *将大于等于标志赋给clnegcl; *将cl求负andcl, bl; *与原数值进行与运算
核心指令共4条。
2.3 用位掩码做饱和处理.用带符号右移生成掩码
C语言源码——
// 用位掩码做饱和处理.用带符号右移生成掩码by2 = (BYTE)(n & ~((signed short)n >> 15));
汇编代码——
movcx, bx; 复制当前数据。不列入统计。sarcx, 15; *右移15位notcl; *对cl逐位取反andcl, bl; *与原数值进行与运算
核心指令共3条。
3 分析“>255”处理
找到“>255”处理的相关汇编代码——
; 67 : // 检查 “>255”处理; 68 : printf("[Test: great255]\n");pushOFFSET FLAT:??_C@_0BC@LFG@?$FLTest?3?5great255?$FN?6?$AA@ ; `string'call_printfaddesp, 4movesi, OFFSET FLAT:_buf+65536$L53272:; 69 : for(i=0x8000; i<0x10000; ++i)// [0, 32767]; 70 : //for(i=0x80FE; i<=0x8102; ++i)// [254, 258]; 71 : {; 72 : // 加载数值; 73 : n = buf[i];movbx, WORD PTR [esi]; 74 : ; 75 : // 用if分支做饱和处理; 76 : m = n;movecx, ebx; 77 : if (m > 255) m = 255;cmpbx, dimovDWORD PTR _m$[esp+28], ecxjleSHORT $L53275movDWORD PTR _m$[esp+28], edimovecx, edi$L53275:; 78 : by0 = (BYTE)m;; 79 : ; 80 : // 用位掩码做饱和处理.用求负生成掩码; 81 : by1 = (BYTE)(n | -(n >= 256) );cmpbx, 256; 00000100Hsetgealnegaloral, bl; 82 : if (by1 != by0)printf("[Error] 2.1 neg: [%d] %d!=%d\n", n, by0, by1);// 验证cmpal, clmovBYTE PTR _by1$[esp+28], aljeSHORT $L53278moveax, DWORD PTR _by1$[esp+28]andecx, ediandeax, edipusheaxpushecxmovsxecx, bxpushecxpushOFFSET FLAT:??_C@_0BO@FGBC@?$FLError?$FN?52?41?5neg?3?5?$FL?$CFd?$FN?5?$CFd?$CB?$DN?$CFd?6?$AA@ ; `string'call_printfmovecx, DWORD PTR _m$[esp+44]addesp, 16; 00000010H$L53278:; 83 : ; 84 : // 用位掩码做饱和处理.用带符号右移生成掩码; 85 : by2 = (BYTE)(n | ((signed short)(255-n) >> 15));movax, disubax, bxsarax, 15; 0000000fHoral, bl; 86 : if (by2 != by0)printf("[Error] 2.2 sar: [%d] %d!=%d\n", n, by0, by2);// 验证cmpal, clmovBYTE PTR _by2$[esp+28], aljeSHORT $L53273movedx, DWORD PTR _by2$[esp+28]andecx, ediandedx, edimovsxeax, bxpushedxpushecxpusheaxpushOFFSET FLAT:??_C@_0BO@PGLG@?$FLError?$FN?52?42?5sar?3?5?$FL?$CFd?$FN?5?$CFd?$CB?$DN?$CFd?6?$AA@ ; `string'call_printfaddesp, 16; 00000010H$L53273:addesi, 2cmpesi, OFFSET FLAT:_buf+131072jl$L53272; 87 : }
下面对其进行整理和分析。
3.1 用if分支做饱和处理
C语言源码——
// 用if分支做饱和处理m = n;if (m > 255) m = 255;by0 = (BYTE)m;
汇编代码——
movedi, 255; 在循环外将edi设为255。不列入统计。...movesi, OFFSET FLAT:_buf; 在循环外将esi指向buf。不列入统计。...movbx, WORD PTR [esi]; 加载数值。不列入统计。movecx, ebx; *复制数据到ecx。cmpbx, di; *与255进行比较。movDWORD PTR _m$[esp+28], ecx; 存储数值。不列入统计。jleSHORT $L53275; *若大于等于就跳转movDWORD PTR _m$[esp+28], edi; 存储数值。不列入统计。movecx, edi; *否则将ecx设为0。$L53275:
核心指令共4条。
3.2 用位掩码做饱和处理.用求负生成掩码
C语言源码——
// 用位掩码做饱和处理.用求负生成掩码by1 = (BYTE)(n | -(n >= 256) );
汇编代码——
cmpbx, 256; *与256进行比较。setgeal; *将大于等于标志赋给alnegal; *将al求负oral, bl; *与原数值进行或运算
核心指令共4条。
3.3 用位掩码做饱和处理.用带符号右移生成掩码
C语言源码——
// 用位掩码做饱和处理.用带符号右移生成掩码by2 = (BYTE)(n | ((signed short)(255-n) >> 15));
汇编代码——
movax, di; *复制255subax, bx; * ax = ax - bx = 255 - 当前数据sarax, 15; *右移15位oral, bl; *与原数值进行或运算
核心指令共4条。
4 分析饱和处理
找到饱和处理的相关汇编代码——
; 89 : // 检查 饱和处理; 90 : printf("[Test: saturation]\n");pushOFFSET FLAT:??_C@_0BE@BNPN@?$FLTest?3?5saturation?$FN?6?$AA@ ; `string'call_printfaddesp, 4movesi, OFFSET FLAT:_buf$L53285:; 91 : for(i=0; i<0x10000; ++i)// [-32768, 32767]; 92 : //for(i=0x7FFE; i<=0x8102; ++i)// [-2, 258]; 93 : {; 94 : // 加载数值; 95 : n = buf[i];movbx, WORD PTR [esi]; 96 : ; 97 : // 用if分支做饱和处理; 98 : m = n;movecx, ebx; 99 : if (m < 0) m = 0;cmpbx, bpmovDWORD PTR _m$[esp+28], ecxjgeSHORT $L53288movDWORD PTR _m$[esp+28], ebp; 100 : else if (m > 255) m = 255;jmpSHORT $L53325$L53288:cmpbx, dijleSHORT $L53290movDWORD PTR _m$[esp+28], edi$L53325:movecx, DWORD PTR _m$[esp+28]$L53290:; 101 : by0 = (BYTE)m;; 102 : ; 103 : // 用位掩码做饱和处理.用求负生成掩码; 104 : by1 = LIMITSU_BYTE(n);cmpbx, bpsetgealnegalandal, blcmpbx, 256; 00000100Hsetgedlnegdloral, dl; 105 : if (by1 != by0)printf("[Error] 3.1 neg: [%d] %d!=%d\n", n, by0, by1);// 验证cmpal, clmovBYTE PTR _by1$[esp+28], aljeSHORT $L53293moveax, DWORD PTR _by1$[esp+28]andecx, ediandeax, edipusheaxpushecxmovsxecx, bxpushecxpushOFFSET FLAT:??_C@_0BO@MGFB@?$FLError?$FN?53?41?5neg?3?5?$FL?$CFd?$FN?5?$CFd?$CB?$DN?$CFd?6?$AA@ ; `string'call_printfmovecx, DWORD PTR _m$[esp+44]addesp, 16; 00000010H$L53293:; 106 : ; 107 : // 用位掩码做饱和处理.用带符号右移生成掩码; 108 : by2 = LIMITSW_BYTE(n);movax, dimovdx, bxsubax, bxsarax, 15; 0000000fHsardx, 15; 0000000fHoral, blnotdlandal, dl; 109 : if (by2 != by0)printf("[Error] 3.2 sar: [%d] %d!=%d\n", n, by0, by2);// 验证cmpal, clmovBYTE PTR _by2$[esp+28], aljeSHORT $L53286moveax, DWORD PTR _by2$[esp+28]andecx, ediandeax, edipusheaxpushecxmovsxecx, bxpushecxpushOFFSET FLAT:??_C@_0BO@GGPF@?$FLError?$FN?53?42?5sar?3?5?$FL?$CFd?$FN?5?$CFd?$CB?$DN?$CFd?6?$AA@ ; `string'call_printfaddesp, 16; 00000010H$L53286:addesi, 2cmpesi, OFFSET FLAT:_buf+131072jl$L53285popedipopesipopebp; 110 : }
下面对其进行整理和分析。
4.1 用if分支做饱和处理
C语言源码——
// 用if分支做饱和处理m = n;if (m < 0) m = 0;else if (m > 255) m = 255;by0 = (BYTE)m;
汇编代码——
xorebp, ebp; 在循环外将ebp设为0。不列入统计。movedi, 255; 在循环外将edi设为255。不列入统计。...movesi, OFFSET FLAT:_buf; 在循环外将esi指向buf。不列入统计。...movbx, WORD PTR [esi]; 加载数值。不列入统计。movecx, ebx; *复制数据到ecx。cmpbx, bp; *与0进行比较。movDWORD PTR _m$[esp+28], ecx; *存储数值。jgeSHORT $L53288; *若大于等于就跳转movDWORD PTR _m$[esp+28], ebp; *存储数值。jmpSHORT $L53325; *强制跳转$L53288:cmpbx, di; *与255进行比较。jleSHORT $L53290; *若小于等于就跳转movDWORD PTR _m$[esp+28], edi; *存储数值。$L53325:movecx, DWORD PTR _m$[esp+28]; *加载数值$L53290:
核心指令共10条。
4.2 用位掩码做饱和处理.用求负生成掩码
C语言源码——
// 用位掩码做饱和处理.用求负生成掩码by1 = LIMITSU_BYTE(n);// #define LIMITSU_FAST(n, bits) ( (n) & -((n) >= 0) | -((n) >= (1<<(bits))) )
汇编代码——
cmpbx, bp; *与0进行比较。setgeal; *将大于等于标志赋给alnegal; *将al求负andal, bl; *与原数值进行与运算cmpbx, 256; *与256进行比较。setgedl; *将大于等于标志赋给dlnegdl; *将dl求负oral, dl; *与原数值进行或运算
核心指令共8条。
4.3 用位掩码做饱和处理.用带符号右移生成掩码
C语言源码——
// 用位掩码做饱和处理.用带符号右移生成掩码by2 = LIMITSW_BYTE(n);// #define LIMITSW_FAST(n, bits) ( ( (n) | ((signed short)((1<<(bits)) - 1 - (n)) >> 15) ) & ~((signed short)(n) >> 15) )
汇编代码——
movax, di; *复制255。movdx, bx; 复制当前数据。不列入统计。subax, bx; * ax = ax - bx = 255 - 当前数据sarax, 15; *右移15位sardx, 15; *右移15位oral, bl; *与原数值进行或运算notdl; *对dl逐位取反andal, dl; *与原数值进行与运算
核心指令共7条。
5 小结
在做饱和处理时——
1、if分支法:10条指令。不仅用到了多条跳转指令,还需要将变量暂存在比寄存器慢很多的内存中。
2、求负生成掩码法:8条指令。无分支,但仍需访问状态寄存器。
3、移位生成掩码法:7条指令。无分支,避免了状态寄存器访问。
- 深入探讨用位掩码代替分支(2):汇编代码分析
- 深入探讨用位掩码代替分支(1):利用带符号移位生成掩码
- 深入探讨用位掩码代替分支(3):VC6速度测试
- 深入探讨用位掩码代替分支(4):VC2010速度测试
- 深入探讨用位掩码代替分支(5):C#2010速度测试
- 深入探讨用位掩码代替分支(6):VB6速度测试
- 深入探讨用位掩码代替分支(7):MMX指令集速度测试
- 深入探讨用位掩码代替分支(8):SSE指令集速度测试
- 深入探讨用位掩码代替分支(9):测试成绩总结
- 位掩码(BitMask)
- 位掩码(BitMask)
- 位掩码
- 位掩码
- 位掩码
- (32):用EnumSet代替位域
- 深入探讨ROP 载荷分析
- 【16位汇编】neg+sbb算术运算代替逻辑跳转
- 32位汇编代码结构
- java 之 线程池学习
- 如何调试跟踪Android源代码
- Java ClassLoader
- struts的ActionRedirect类
- linux c inline
- 深入探讨用位掩码代替分支(2):汇编代码分析
- typedef定义函数类型分析
- 通过IE句柄 获得 IE Document对象和IWebBrowser2对象
- DNS Bind9 配置小记
- sturts2上传文件,后台获取不到文件!
- C++ delegate
- 动态规划——最长公共子序列(LCS)
- Oracle10g 问题总结及对策
- 编程语言API性能大比拼