郁闷的汇编码

来源:互联网 发布:试卷扫描软件 编辑:程序博客网 时间:2024/06/05 03:31

    我是个编程业余爱好者,虽说是业余,也有近20年了,以前在DOS下编程,计算机速度很慢,内存、硬盘容量也有限,必须对程序代码精益求精,稍大点的程序还得兼顾代码长度和运行速度,否则,你的程序可能跑不起来。
    我以前主要用C编程,虽然效率较高,但为了代码长度和速度,关键的代码我喜欢用汇编,相同的算法和流程,ASM和C无论是代码长度和运行速度,都没得比,而且有些特殊的位运算,ASM比C方便多了。在当时dBASE流行的时代(除去系统程序和科技应用及少量的专用商业程序,其它应用几乎清一色dBASE),我的程序还是很受欢迎的(免费的当然受欢迎啦),为此还获得过本系统省级和部级优秀程序奖。新的世纪到来,无论是C还是ASM,几乎有出局的危险,我又喜欢上了DELPHI(DELPHI也快出局了),十年了,C都忘了,而ASM因时不时我喜欢来个BASM函数,所以还没全忘。
    前几天,有人在CSDN的汇编语言板块发了个《千分求最快的Base64编码函数》的帖,而刚好我有个Delphi的BASM函数放在BLOG中(参见《Delphi版的Base64转换函数(修改版)》,其中的代码已经是修改过的),自己测试了一下,在我的机器上87MB/s,于是抛砖引玉推荐给了楼主,楼主测试有266MB/s(我的机器是P464位 2.8G,DDR2 667 1G双通道,楼主的是AMD64x2 3600+(2.01G), DDR2 667(334.9MHz))。后来,向楼主推荐代码的越来越多,速度也是越来越快,由几十/s上升到IGM/s以上(楼主测试的),而且有个有趣的现象,相同的算法和流程,C/C++代码普遍比ASM快,在我的印象中,这应该是不可能的,只要汇编码优化到位,速度绝对高于C/C++,于是对我的Base64Encode函数在不改变基本算法的基础上进行了一些简单优化,自己测试164/s,速度比以前提高近一倍,但还是很不理想,没想到楼主的测试结果更气人,居然比我那个没优化的函数还慢,不同的CPU竟然有如此大的悬殊,郁闷!反复优化后的代码速度也不理想,自己测试470MB/s(没改变嵌套循环流程,如果改用顺序执行流程,速度可大大提高,但是我很不喜欢这样的“油条代码”)。

    不是C/C++比ASM快吗,于是用C写了2个函数试试,主要代码如下:

static const char base64table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";


unsigned 
long Base64_Encode_Byte(void *Source, unsigned long SourceSize, void *Base64_out)
{
 unsigned 
char *pIn = (unsigned char*)Source;
 unsigned 
long *pOut = (unsigned long*)Base64_out;
 unsigned 
long *pOut_end = pOut + SourceSize / 3;
 
for (; pOut < pOut_end; pOut ++, pIn += 3)
 
{
  
*pOut = *(base64table + (pIn[0>> 2))
     
| *(base64table + (((pIn[0<< 4| (pIn[1>> 4)) & 0x3f)) << 8
     
| *(base64table + (((pIn[1<< 2| (pIn[2>> 6)) & 0x3f)) << 16
     
| *(base64table+ (pIn[2& 0x3f)) << 24;
 }

 Base64_addpaing(pIn, SourceSize 
% 3, pOut);
 
return ((SourceSize + 2/ 3<< 2;
}


unsigned 
long Base64_Encode_Bit(void *Source, unsigned long SourceSize, void *Base64_out)
{
 unsigned 
char *pIn = (unsigned char*)Source;
 unsigned 
long *pOut = (unsigned long*)Base64_out;
 unsigned 
long *pOut_end = pOut + SourceSize / 3;
 unsigned 
long data;
 
for (; pOut < pOut_end; pOut ++, pIn += 3)
 
{
  data 
= *pIn << 16 | pIn[1<< 8 | pIn[2];
  
*pOut = *(base64table + (data >> 18))
     
| *(base64table + ((data >> 12& 0x3f)) << 8
     
| *(base64table + ((data >> 6& 0x3f)) << 16
     
| *(base64table + ((data) & 0x3f)) << 24;
 }

 Base64_addpaing(pIn, SourceSize 
% 3, pOut);
 
return ((SourceSize + 2/ 3<< 2;
}

    Base64_Encode_Byte是按字节拆分后移位重新组合的传统算法,而Base64_Encode_Bit则是先组合成24位二进制内存映像再拆分。在BCB上编译运行,没选择优化的Debug程序都没超过300MB/s,且Base64_Encode_Byte比Base64_Encode_Bit要快一些,打开优化的Release程序都可达550MB/s以上,而此时Base64_Encode_Bit反倒比Base64_Encode_Byte差不多快30MB/s。C代码果真比普通ASM快!我用BCC32 -S -O2 -6编译命令取得Base64_Encode_Bit优化后的ASM代码,几乎没作修改改名为Base64_Encode_ASM编译为Release程序测试,晕!只有470MB/s多。难道BCB给的不是优化ASM码?不得而知,郁闷!
    再次反省总结,决定仿效C的ASM码,采用顺序流程(虽然不喜欢,但还是试试)和传统算法重新写了个BASM函数,居然达到710MB/s!相同算法,顺序流程比循环流程快是肯定的,但编程老手们是不愿意为了一点点速度而放弃合理的流程的,确实影响代码的美观和可读性。可是现在也太离谱了,我只要一改为循环,速度就成倍下降,真是郁闷!
    好了,总算有可同《千分求最快的Base64编码函数》贴上高手们相媲美的BASM函数了(贴上虽然有1GMB/s左右的函数,但那是拼内存消耗拼出来的,常规算法我这应该算高的了,而且我的机器比他们的慢,估计楼主的测试怎么也有个800-900MB/s吧!),为了放心起见,我没发到贴上,而是发CSDN短信给楼主,让他测试一下,毕竟是他发的贴,以他的测试为准,不一会,结果出来了:“新的Base64_Encode在我的AMDx2 4200+上 427.9MB/s”,注意,是比前面的AMD64x2 3600+(2.01G), DDR2 667(334.9MHz)更快的机器上测试结果!真是郁闷到极点了!!!
    汇编码比高级语言兼容性和移植性差,是个不争的事实,但不同的CPU竟然如此大的悬殊,不能不令人悲哀,看来我崇尚的高级语言与ASM组合编程方式应该结束了。
    推而广之,高级语言原生代码程序也不可能为每种CPU都编译一个版本,这台机器上跑得很欢的程序到另一台机器上会不会其慢无比?看来说原生代码程序几年后将被JAVA、.NET等中间件托管代码程序取代应该是不假了,难怪有人宣称JAVA的运行速度将超过C/C++,看来也不是不可能,因为中间件托管代码完全可根据不同的机器特性提供最高效率的代码!
    以上只是笔者有感而发,有些观点不见得正确,毕竟我只是个业余爱好者,而且年纪很大(多大?你可能猜不着),文化水平也很低(多低,你可能也想不到),同高手们是有相当大的差距的,还希望不吝赐教!

 

原创粉丝点击