CLR筆記 二 函數調用
關於函數調用以及函數調用代碼性能優化
函數分為3類:
1.非虛實例方法-普通方法
2.虛方法
3.靜態方法
方法構成:
方法名+簽名+返回值;
方法的記錄:
每一個方法在程序集的方法定義表中都有一個記錄項,每個記錄項用一個標識flag指明方法的類型:實例方法,虛方法,靜態方法;
編譯器根據每個方法的標識生成IL代碼指令,即call和callvirt;
call與callvirt區別:
call:
可調用靜態方法(必須指定方法定義的類型),實例方法和虛方法(必須指定引用對象的變量,假定變量不為空)
callvirt:
即驗證變量是否為null,若為空則拋出NullReferenceException,然後以多態方式調用;
共同點:
接收一個隱藏的this實參作為第一個參數,this實參引用要操作的對象;
調用call性能比callvirt高,call不會進行調用變量的非空判斷,JIT編譯器不能內嵌(inline)虛方法;
如何盡量讓方法編譯為IL後,JIT調用時,使用call而不是callvirt:
1.盡量使用靜態類中的靜態方法 - 靜態方法IL代碼為call;
3.調用值類型中的方法,一般使用call;
4.盡量使用sealed 密封類,JIT使用非虛方式(call)調用該類中的虛方法(C#編譯器生成callvirt指令,JIT會優化這個調用);
實驗驗證
定義三種代碼->反編譯查看IL代碼:
1.定義一個抽象基類,普通子類,密封子類,一個結構體(值類型):
1 public class TestClass 2 {View Code3 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 }
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筆記 二 函數調用