汇编代码插入c++代码及函数效率比较
时间:2010-10-22 来源:幻魇
内联调用省去了参数压入栈和保存、还原寄存器的开销,提高了程序的效率,也比较简单安全,缺点是每次都要重新插入汇编代码。我用的编译器Virtual Stdio 2008使用 __asm保留字实现内联汇编。
函数调用比较复杂,具体来说,首先在汇编器中设计好原始的汇编代码,要求是:
一、使用 .model flat,C
二、对函数具有正确的原型声明
三、汇编代码中的函数名就是c++中调用所需的函数名
之后需要用汇编器将源文件编译为目标文件(.obj),加入到Virtual Stdio 2008的项目中,如图
其中ASMlenthOfString.obj就是一个目标文件,它已经加入到了一个Virtual Stdio 2008项目中了
然后需要建立一个头文件,并在其中进行外部引用说明,然后让需要调用这段汇编代码的源文件包含这个头文件就大功告成了,具体步骤稍后说明。
下面的这个程序是调用了一个统计C风格字符串的长度的汇编过程,并将它的效率和C标准库“string.h”自带的strlen()函数的效率进行比较,程序中使用的是std::strlen(),即加入了std命名空间的strlen,它被包含在库“cstring”中。
具体测试的内容是让两个函数分别统计长度从16776216 到16777216 的1000个C风格字符串的大小,并将所用的总时间以毫秒为单位输出。
前段时间写过两个汇编版本的C风格字符串长度统计,今天发现效率都不高,又重新写了一个,总体而言,今天的版本效率如果是1的话,之前的版本效率在53%左右。
内联版本:
#include <memory.h> #include <cstring> #include <iostream> #include <ctime> #define ARRAY_SIZE 16777216 #define START_POINT 16776216 #define TEST_TIMES 1; char ap[ARRAY_SIZE]={" "}; std::clock_t start_time,end_time,total; unsigned long len,i; int main() { memset(ap,'1',START_POINT); std::cout<<"Now we test c++ function."<<std::endl; total=0; for (i=START_POINT; i!=ARRAY_SIZE-2; ++i) { ap[i]='1'; start_time=std::clock(); len=std::strlen(ap); end_time=std::clock(); total+=end_time-start_time; } std::cout<<"It cost "<<total<<"ms."<<std::endl; std::cout<<"Now we test ASM procedure."<<std::endl; total=0; memset(ap+START_POINT,0,ARRAY_SIZE-START_POINT); for (i=START_POINT; i!=ARRAY_SIZE-2; ++i) { ap[i]='1'; start_time=std::clock(); __asm { mov esi,offset ap lea edx,[esi+1] L: mov al,byte ptr [esi] inc esi test al,al jne L sub esi,edx mov len,esi } end_time=std::clock(); total+=end_time-start_time; } std::cout<<"It cost "<<total<<"ms."<<std::endl;; return 0; }
一组测试结果如下:
经过多组测试发现,本例中c++标准库和内联的汇编代码效率基本不相上下
非内联版本需要三个文件:
main.cpp:
#include "header.h" #include <memory.h> #include <cstring> #include <iostream> #include <ctime> #define ARRAY_SIZE 16777216 #define START_POINT 16776216 #define TEST_TIMES 1; char ap[ARRAY_SIZE]={" "}; std::clock_t start_time,end_time,total; unsigned long len,i; int main() { memset(ap,'1',START_POINT); std::cout<<"Now we test c++ function."<<std::endl; total=0; for (i=START_POINT; i!=ARRAY_SIZE-2; ++i) { ap[i]='1'; start_time=std::clock(); len=std::strlen(ap); end_time=std::clock(); total+=end_time-start_time; } std::cout<<"It cost "<<total<<"ms."<<std::endl; std::cout<<"Now we test ASM procedure."<<std::endl; total=0; memset(ap+START_POINT,0,ARRAY_SIZE-START_POINT); for (i=START_POINT; i!=ARRAY_SIZE-2; ++i) { ap[i]='1'; start_time=std::clock(); len=lenthOfString(ap); end_time=std::clock(); total+=end_time-start_time; } std::cout<<"It cost "<<total<<"ms."<<std::endl;; return 0; }
header.h:只有一行代码
extern "C" unsigned long lenthOfString(char string[]);
ASMLengthOfString.obj:它是由ASMLengthOfString.asm编译而成
ASMLengthOfString.asm原文
Title Length Of String
.386
.model flat,C
lenthOfString proto,
string:ptr byte
.code
lenthOfString proc uses ecx esi,;it will change flag,ecx,esi
string:ptr byte
mov esi,string
lea edx,[esi+1]
L: mov al,byte ptr [esi]
inc esi
test al,al
jne L
sub esi,edx
mov eax,esi
ret
lenthOfString endp
end
这些都弄好后就可以调用了,其中一组测试结果如下:
多组测试发现外部调用的话效率反而略高。
分析:
一、外部调用效率有时更高的原因,估计是cpu检测到这段代码被大量调用,于是将其存入cpu高速缓存中提高了速度
二、
repne scasb
的效率远低于
L: mov al,byte ptr [esi]
inc esi
test al,al
jne L
下面的代码有4条指令,上面只有1条指令,效率却是上边的代码将近两倍。原因可能是
1、repne和scasb本身效率就低,如果是这样的话,Intel公司推出这两条指令莫非只是为了减少内存占用而拖慢了CPU????
2、虽然下边的指令数较多,但考虑到我电脑的CPU有四条流水线,CPU将工作平分给四条流水线,反而让效率增长不少。
我CPU型号是Intel Core(TM)2 Duo Cpu P7450
和这个问题相似的还有,lodsb的效率不如mov+inc组合来的高
以上两个问题还望高手指点。
三、通过比较发现,C标准库中的函数与直接汇编代码效率几乎不相上下,这说明,C或C++标准库中的一些函数是经过了汇编级别的精心优化的,因此调用库函数的效率往往非常高。
以上。