可能有人认为相比于 ForTest1,ForTest2 存储了数组的 Length,少了对于数组属性的频繁调用,会有更好的性能表现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| using System;
namespace JITPropertyAccessInFor { internal static class Program { public static void Main(string[] args) { }
private static void Test1() { var a = new int[5]; }
private static void ForTest1() { var a = new int[5]; for (var i = 0; i < a.Length; i++) { Console.WriteLine(a[i]); } }
private static void ForTest2() { var a = new int[5]; int len = a.Length; for (var i = 0; i < len; i++) { Console.WriteLine(a[i]); } } } }
|
以下 是 上段代码编译出的 IL code:(以下所述栈均为操作数栈 (Operand stack))
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| .method private hidebysig static void ForTest1() cil managed { .maxstack 2 .locals init ([0] int32[] a, // 变量声明 (局部变量表) [1] int32 i) IL_0000: ldc.i4.5 IL_0001: newarr [mscorlib]System.Int32 IL_0006: stloc.0 IL_0007: ldc.i4.0 IL_0008: stloc.1 IL_0009: br.s IL_0017 IL_000b: ldloc.0 IL_000c: ldloc.1 IL_000d: ldelem.i4 IL_000e: call void [mscorlib]System.Console::WriteLine(int32) IL_0013: ldloc.1 IL_0014: ldc.i4.1 IL_0015: add IL_0016: stloc.1 IL_0017: ldloc.1 IL_0018: ldloc.0 IL_0019: ldlen IL_001a: conv.i4 IL_001b: blt.s IL_000b IL_001d: ret }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| .method private hidebysig static void ForTest2() cil managed { .maxstack 2 .locals init ([0] int32[] a, // 变量声明 (局部变量表) [1] int32 len, [2] int32 i) IL_0000: ldc.i4.5 IL_0001: newarr [mscorlib]System.Int32 IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: ldlen IL_0009: conv.i4 IL_000a: stloc.1 IL_000b: ldc.i4.0 IL_000c: stloc.2 IL_000d: br.s IL_001b IL_000f: ldloc.0 IL_0010: ldloc.2 IL_0011: ldelem.i4 IL_0012: call void [mscorlib]System.Console::WriteLine(int32) IL_0017: ldloc.2 IL_0018: ldc.i4.1 IL_0019: add IL_001a: stloc.2 IL_001b: ldloc.2 IL_001c: ldloc.1 IL_001d: blt.s IL_000f IL_001f: ret }
|
对比上述的 IL code,确实临时存储数组长,能够少在 for 的比较进行中少进行一定的操作,无需将数组从局部变量表(Local Variable Table)入操作数栈 (Operand stack),并执行 ldlen 获取数组长。 但要注意, JIT 编译器知道 Length 是 Array 类的属性,生成的代码中只会调用该属性一次,结果会存储到临时变量中,此后的检查中调用的都是此临时变量。不需要自己用局部变量做缓存,这样既没有性能提升,还可能造成可读性下降。
参阅
CLR via C# (第四版) 16.7 数组的内部工作原理
注释
Ildasm.exe(IL 反汇编程序)
一般,该工具位于 NETFX 4.7.2 Tools 中
C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.7.2 Tools\x64\ildasm.exe