1. 程式人生 > >CLR筆記 二 函數調用

CLR筆記 二 函數調用

IT 實例方法 virtual RR lda jit seq ini 執行

關於函數調用以及函數調用代碼性能優化

函數分為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

CLR筆記 二 函數調用