• CLR笔记 二 函数调用


     关于函数调用以及函数调用代码性能优化

    函数分为3类:

    1.非虚实例方法-普通方法
    2.虚方法
    3.静态方法

    方法构成:

    方法名+签名+返回值;

    方法的记录:

    每一个方法在程序集的方法定义表中都有一个记录项,每个记录项用一个标识flag指明方法的类型:实例方法,虚方法,静态方法;

    编译器根据每个方法的标识生成IL代码指令,即call和callvirt

    call与callvirt区别:

    call:
    可调用静态方法(必须指定方法定义的类型),实例方法和虚方法(必须指定引用对象的变量,假定变量不为空)
    callvirt:
    可调用实例方法和虚方法,JIT会调查发出调用的对象的类型是否为null(即使调用非虚实例方法也要执行null检查),
    即验证变量是否为null,若为空则抛出NullReferenceException,然后以多态方式调用;

    共同点:
    接收一个隐藏的this实参作为第一个参数,this实参引用要操作的对象;

    调用call性能比callvirt高,call不会进行调用变量的非空判断,JIT编译器不能内嵌(inline)虚方法

    如何尽量让方法编译为IL后,JIT调用时,使用call而不是callvirt:

    1.尽量使用静态类中的静态方法 - 静态方法IL代码为call;
    2.尽量使用非虚方法,某些编译器会使用call调用非虚方法,C#编译器会使用callvirt调用所有的实例方法,特殊情况例外:子类调用基类的方法(虚方法和实例方法)会使用call;
    3.调用值类型中的方法,一般使用call;
    4.尽量使用sealed 密封类,JIT使用非虚方式(call)调用该类中的虚方法(C#编译器生成callvirt指令,JIT会优化这个调用);

    实验验证

    定义三种代码->反编译查看IL代码:

    1.定义一个抽象基类,普通子类,密封子类,一个结构体(值类型):

     1     public class TestClass
     2     {
     3         public static void Test()
     4         {
     5             ClassA.StaticFunc(); //call
     6             ClassB.StaticFunc(); //call
     7             var objA = new ClassA();
     8             objA.BaseNormalFunc();   //callvirt
     9             objA.BaseVirtualFunc();  //callvirt
    10             objA.SubNormalFunc();    //callvirt
    11             var objB = new ClassB();
    12             objB.BaseNormalFunc();   //callvirt
    13             objB.BaseVirtualFunc();  //callvirt
    14             objB.SubNormalFunc();    //callvirt
    15             objB.ToString();
    16 
    17 
    18             ValueType.StaticFunc();
    19             ValueType vt;
    20             vt.NormalFunc();
    21             ValueType vt2 = new ValueType();
    22             vt2.NormalFunc();
    23         }
    24     }
    25     public abstract class BaseClass
    26     {
    27         /// <summary>
    28         /// 基类虚方法
    29         /// </summary>
    30         public virtual void BaseVirtualFunc()
    31         {
    32         }
    33         /// <summary>
    34         /// 基类普通方法
    35         /// </summary>
    36         public void BaseNormalFunc()
    37         {
    38         }
    39     }
    40     public class ClassA: BaseClass
    41     {
    42         public static void StaticFunc()
    43         {
    44         }
    45 
    46         public void SubNormalFunc()
    47         {
    48         }
    49         public override void BaseVirtualFunc()
    50         {
    51             base.BaseVirtualFunc();
    52         }
    53 
    54         public new void BaseNormalFunc()
    55         {
    56             base.BaseNormalFunc();
    57         }
    58     }
    59     public sealed class ClassB : BaseClass
    60     {
    61         public static void StaticFunc()
    62         {
    63         }
    64 
    65         public void SubNormalFunc()
    66         {
    67         }
    68         public override void BaseVirtualFunc()
    69         {
    70         }
    71 
    72         public new void BaseNormalFunc()
    73         {
    74         }
    75     }
    76     public struct ValueType
    77     {
    78         public void NormalFunc()
    79         {
    80         }
    81         public static void StaticFunc()
    82         {
    83         }
    84     }
    View Code

    2.编译完后查看IL代码:

    .method public hidebysig static 
        void Test () cil managed 
    {
        // Method begins at RVA 0x7820
        // Code size 98 (0x62)
        .maxstack 1
        .locals init (
            [0] class HelloWorld.CLR.Performance.ClassA objA,
            [1] class HelloWorld.CLR.Performance.ClassB objB,
            [2] valuetype HelloWorld.CLR.Performance.ValueType vt,
            [3] valuetype HelloWorld.CLR.Performance.ValueType vt2
        )
    
        IL_0000: nop
        IL_0001: call void HelloWorld.CLR.Performance.ClassA::StaticFunc()//call调用ClassA静态方法
        IL_0006: nop
        IL_0007: call void HelloWorld.CLR.Performance.ClassB::StaticFunc()//call调用ClassB静态方法
        IL_000c: nop
        IL_000d: newobj instance void HelloWorld.CLR.Performance.ClassA::.ctor()//new实例
        IL_0012: stloc.0
        IL_0013: ldloc.0
        IL_0014: callvirt instance void HelloWorld.CLR.Performance.ClassA::BaseNormalFunc()
        IL_0019: nop
        IL_001a: ldloc.0
        IL_001b: callvirt instance void HelloWorld.CLR.Performance.BaseClass::BaseVirtualFunc()
        IL_0020: nop
        IL_0021: ldloc.0
        IL_0022: callvirt instance void HelloWorld.CLR.Performance.ClassA::SubNormalFunc()
        IL_0027: nop
        IL_0028: newobj instance void HelloWorld.CLR.Performance.ClassB::.ctor()
        IL_002d: stloc.1
        IL_002e: ldloc.1
        IL_002f: callvirt instance void HelloWorld.CLR.Performance.ClassB::BaseNormalFunc()
        IL_0034: nop
        IL_0035: ldloc.1
        IL_0036: callvirt instance void HelloWorld.CLR.Performance.BaseClass::BaseVirtualFunc()
        IL_003b: nop
        IL_003c: ldloc.1
        IL_003d: callvirt instance void HelloWorld.CLR.Performance.ClassB::SubNormalFunc()
        IL_0042: nop
        IL_0043: call void HelloWorld.CLR.Performance.ValueType::StaticFunc()
        IL_0048: nop
        IL_0049: ldloca.s vt
        IL_004b: call instance void HelloWorld.CLR.Performance.ValueType::NormalFunc()
        IL_0050: nop
        IL_0051: ldloca.s vt2
        IL_0053: initobj HelloWorld.CLR.Performance.ValueType
        IL_0059: ldloca.s vt2
        IL_005b: call instance void HelloWorld.CLR.Performance.ValueType::NormalFunc()
        IL_0060: nop
        IL_0061: ret
    } // end of method TestClass::Test

    可以看出:

    对于引用类型:

    普通子类和密封子类的非静态函数(实例函数以及虚函数)调用都是callvirt;

    但是在子类方法中调用基类方法使用call;

    ClassA中的覆盖基类的方法BaseNormalFunc

     1 .method public hidebysig 
     2     instance void BaseNormalFunc () cil managed 
     3 {
     4     // Method begins at RVA 0x78ba
     5     // Code size 9 (0x9)
     6     .maxstack 8
     7 
     8     IL_0000: nop
     9     IL_0001: ldarg.0
    10     IL_0002: call instance void HelloWorld.CLR.Performance.BaseClass::BaseNormalFunc()
    11     IL_0007: nop
    12     IL_0008: ret
    13 } // end of method ClassA::BaseNormalFunc

    ClassA中的重写基类的方法BaseVirtualFunc

     1 .method public hidebysig virtual 
     2     instance void BaseVirtualFunc () cil managed 
     3 {
     4     // Method begins at RVA 0x78b0
     5     // Code size 9 (0x9)
     6     .maxstack 8
     7 
     8     IL_0000: nop
     9     IL_0001: ldarg.0
    10     IL_0002: call instance void HelloWorld.CLR.Performance.BaseClass::BaseVirtualFunc()
    11     IL_0007: nop
    12     IL_0008: ret
    13 } // end of method ClassA::BaseVirtualFunc

    对于引用类型:

    静态函数和普通函数都是call调用;

    但是两者有所不同:

    结构体的IL代码:

     1 .class public sequential ansi sealed beforefieldinit HelloWorld.CLR.Performance.ValueType
     2     extends [mscorlib]System.ValueType
     3 {
     4     .pack 0
     5     .size 1
     6 
     7     // Methods
     8     .method public hidebysig 
     9         instance void NormalFunc () cil managed 
    10     {
    11         // Method begins at RVA 0x2a1b
    12         // Code size 2 (0x2)
    13         .maxstack 8
    14 
    15         IL_0000: nop
    16         IL_0001: ret
    17     } // end of method ValueType::NormalFunc
    18 
    19     .method public hidebysig static 
    20         void StaticFunc () cil managed 
    21     {
    22         // Method begins at RVA 0x2a1b
    23         // Code size 2 (0x2)
    24         .maxstack 8
    25 
    26         IL_0000: nop
    27         IL_0001: ret
    28     } // end of method ValueType::StaticFunc
    29 
    30 } // end of class HelloWorld.CLR.Performance.ValueType

    可以看出结构体都是密封类而且都继承自基类:[mscorlib]System.ValueType

     demon GIT地址:https://github.com/seainchina/GitHelloWorld/blob/master/HelloWorld/CLR/Performance.cs

    2018年6月7日19:55:57

  • 相关阅读:
    mysql 游标使用
    springboot 集成swagger2
    springboot + thymeleaf静态资源访问404
    Win10系列:VC++绘制几何图形1
    Win10系列:VC++媒体播放控制4
    Win10系列:VC++媒体播放控制3
    Win10系列:VC++媒体播放控制2
    Win10系列:VC++媒体播放控制1
    Win10系列:VC++媒体播放
    Win10系列:VC++文件选取
  • 原文地址:https://www.cnblogs.com/forevertime/p/9152520.html
Copyright © 2020-2023  润新知