谈谈中间代码

   1。中间代码的每个方法在第一次运行时必然要编译为本地代码,这一步中间代码一定比本地代码多花时间。所以问题的关键转换为这一编译过程要花多少时间,JAVA我不清楚,不过.NET的表现还是非常好的。JIT编译器将中间代码编译为本地代码,这个过程的复杂度远远小于传统的编译器,因为很多相当耗时且困难的部分已经在“源代码-中间代码”阶段做完了,当你剖析一下CLR编译器编译出来的IL中间代码,你会发现这种基于堆栈的语言其实很容易翻译为本地代码。从IL代码编译为本地代码,在算法上最重要的一步就是运行一个有限状态机体系:分析组成IL代码的字节流中的每一个Byte值,然后放到不同的分支处理结构中去处理,从而输出表示本地代码的字节流。其实以现在的硬件效能,这一步的时间倒真的不是问题。况且每个方法只会编译一次,而一个方法几乎都要被多次使用,平均算下来,则就显得微不足道了。


    2。JIT编译器会根据不同的硬件平台优化编译出不同的代码,而不需多写任何代码。这一点是本地代码所不可比的。直观的看个例子:

         static   void  Main( string [] args)
        {
            
float  f1  =   324.6f ;
            
int  i1  =  ( int )f1;

            
float  f2  =   9.0f ;
            
double  d1  =  ( double )f2;

            
double  d2  =   3445.67 ;
            
long  l1  =  ( long )d2;
        }


    
    再看两个硬件平台编译的结果:第一个是支持X64指令集的64位处理器(64位Vista),第二个是普通的支持SSE2的32位处理器(虚拟机中的)(手头上没有更差的了,谁有P3、K7之类的可以编译试试)。

        // 面向X64平台 

       
static   void  Main( string [] args)
        {
00000000   mov         qword ptr [rsp + 8 ],rcx 
00000005   sub         rsp,58h 
00000009   xorps       xmm0,xmm0 
0000000c  movss       dword ptr [rsp
+ 20h],xmm0 
00000012   mov         dword ptr [rsp + 24h], 0  
0000001a  xorps       xmm0,xmm0 
0000001d  movss       dword ptr [rsp
+ 28h],xmm0 
00000023   xorpd       xmm0,xmm0 
00000027   movsd       mmword ptr [rsp + 30h],xmm0 
0000002d  xorpd       xmm0,xmm0 
00000031   movsd       mmword ptr [rsp + 38h],xmm0 
00000037   mov         qword ptr [rsp + 40h], 0  
00000040   mov         rax,642801A2188h 
0000004a  mov         eax,dword ptr [rax] 
0000004c  test        eax,eax 
0000004e  je          
0000000000000055  
00000050   call        FFFFFFFFFF7AB2D0 
00000055   nop              
            
float  f1  =   324.6f ;
00000056   movss       xmm0,dword ptr [000000E0h] 
0000005e  movss       dword ptr [rsp
+ 20h],xmm0 
            
int  i1  =  ( int )f1;
00000064   cvttss2si   eax,dword ptr [rsp + 20h] 
0000006a  mov         dword ptr [rsp
+ 24h],eax 

            
float  f2  =   9.0f ;
0000006e  movss       xmm0,dword ptr [000000E8h] 
00000076   movss       dword ptr [rsp + 28h],xmm0 
            
double  d1  =  ( double )f2;
0000007c  cvtss2sd    xmm0,dword ptr [rsp
+ 28h] 
00000082   movsd       mmword ptr [rsp + 30h],xmm0 

            
double  d2  =   3445.67 ;
00000088   movsd       xmm0,mmword ptr [000000F0h] 
00000090   movsd       mmword ptr [rsp + 38h],xmm0 
            
long  l1  =  ( long )d2;
00000096   cvttsd2si   rax,mmword ptr [rsp + 38h] 
0000009d  mov         qword ptr [rsp
+ 40h],rax 
        }
000000a2  jmp         00000000000000A4 
000000a4  add         rsp,58h 
000000a8  rep ret          


         // IA32 with SSE2

        
static   void  Main( string [] args)
        {
00000000   push        ebp  
00000001   mov         ebp,esp 
00000003   push        edi  
00000004   push        esi  
00000005   push        ebx  
00000006   sub         esp,5Ch 
00000009   xor         eax,eax 
0000000b  mov         dword ptr [ebp
- 10h],eax 
0000000e  xor         eax,eax 
00000010   mov         dword ptr [ebp - 1Ch],eax 
00000013   mov         dword ptr [ebp - 3Ch],ecx 
00000016   cmp         dword ptr ds:[001F9150h], 0  
0000001d  je          
00000024  
0000001f  call        79C86E7F 
00000024   fldz             
00000026   fstp        dword ptr [ebp - 40h] 
00000029   xor         ebx,ebx 
0000002b  fldz             
0000002d  fstp        dword ptr [ebp
- 48h] 
00000030   fldz             
00000032   fstp        qword ptr [ebp - 50h] 
00000035   fldz             
00000037   fstp        qword ptr [ebp - 58h] 
0000003a  xor         esi,esi 
0000003c  xor         edi,edi 
0000003e  nop              
            
float  f1  =   324.6f ;
0000003f  mov         dword ptr [ebp
- 40h],43A24CCDh 
            
int  i1  =  ( int )f1;
00000046   fld         dword ptr [ebp - 40h] 
00000049   fstp        qword ptr [ebp - 68h] 
0000004c  movsd       xmm0,mmword ptr [ebp
- 68h] 
00000051   cvttsd2si   eax,xmm0 
00000055   mov         ebx,eax 

            
float  f2  =   9.0f ;
00000057   mov         dword ptr [ebp - 48h],41100000h 
            
double  d1  =  ( double )f2;
0000005e  fld         dword ptr [ebp
- 48h] 
00000061   fstp        qword ptr [ebp - 50h] 

            
double  d2  =   3445.67 ;
00000064   fld         qword ptr ds:[004A1558h] 
0000006a  fstp        qword ptr [ebp
- 58h] 
            
long  l1  =  ( long )d2;
0000006d  fld         qword ptr [ebp
- 58h] 
00000070   sub         esp, 8  
00000073   fstp        qword ptr [esp] 
00000076   call        79A52B3F 
0000007b  mov         esi,eax 
0000007d  mov         edi,edx 
        }
0000007f  nop              
00000080   lea         esp,[ebp - 0Ch] 
00000083   pop         ebx  
00000084   pop         esi  
00000085   pop         edi  
00000086   pop         ebp  
00000087   ret              

    显然,JIT编译器会针对不同的处理器做不同的处理,如果是64位处理器并且是64位操作系统,JIT会毫不犹豫地使用64位指令集、毫不犹豫地使用多出来的那8个64位通用寄存器,而不需在源代码中修改任何东西(不懂汇编也没关系,就数数两种环境所编译出来的汇编代码量就知道哪个好了(本人其实也不大懂,主要是用不上,没认真研究过)),再看最后一句: long l1 = (long)d2; 一个编译出的结果是直接使用SSE2指令cvttsd2si,另一个则调用了一个方法来完成,效能明显不一样。

    而本地代码却只能按照最低的平台标准编译。(当然本地代码也可以针对不同的平台发布不同的程序集,只是我觉得这样做所带来的麻烦远比收益大)


    3。不仅跨平台而且跨语言。跨平台是JAVA给我们带来的震撼,而跨语言则是.NET给我们带来震撼(.NET的跨平台体现在Silverlight上)。很多人至今对性能的认知都存在误区,其实面对越来越复杂多变的需求,面对速度差异越来越大的CPU和磁盘IO,面对大量的数据库操作,CPU执行上的那点差异还算什么?跨平台的好处大家都知道,只是很少人能总结到这本质上是一种对软件工程中变化点的封装,使得项目完全不必考虑目标硬件平台,所有变化点都封装在JIT编译器中,性能的优化则是额外附加的好处。而跨语言这样的设计出发点则在于软件复用这个层次,复用的是什么?复用的是各种语言所编写的类库,复用的是精通不同语言的程序员(人力资源)。

转载于:https://www.cnblogs.com/WYB/archive/2008/06/24/1229188.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值