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

原文地址:https://www.cnblogs.com/forevertime/p/9152520.html