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 }
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