我最近在学习MSIL,并且对数组有些困惑:以下2种方法:
private static void FormatTest3()
{
string s = string.Format("{0}{1}{2}", 1, 2,3);
}
private static void FormatTest4()
{
string s = string.Format("{0}{1}{2}{3}", 1, 2,3,4);
/*
equal to
object[] obj = new object[4];
obj[0] = 1;
obj[1] = 2;
obj[2] = 3;
obj[3] = 4;
string text = string.Format("{0}{1}{2}{3}", obj);
*/
}
Run Code Online (Sandbox Code Playgroud)
这是IL:
.class private auto ansi '<Module>'
{
} // end of class <Module>
.class private auto ansi beforefieldinit Program
extends [System.Private.CoreLib]System.Object
{
// Methods
.method private hidebysig static
void FormatTest3 () cil managed
{
// Method begins at RVA 0x2050
// Code size 31 (0x1f)
.maxstack 4
.locals init (
[0] string
)
IL_0000: nop
IL_0001: ldstr "{0}{1}{2}"
IL_0006: ldc.i4.1
IL_0007: box [System.Private.CoreLib]System.Int32
IL_000c: ldc.i4.2
IL_000d: box [System.Private.CoreLib]System.Int32
IL_0012: ldc.i4.3
IL_0013: box [System.Private.CoreLib]System.Int32
IL_0018: call string [System.Private.CoreLib]System.String::Format(string, object, object, object)
IL_001d: stloc.0
IL_001e: ret
} // end of method Program::FormatTest3
.method private hidebysig static
void FormatTest4 () cil managed
{
// Method begins at RVA 0x207c
// Code size 55 (0x37)
.maxstack 5
.locals init (
[0] string
)
IL_0000: nop
IL_0001: ldstr "{0}{1}{2}{3}"
IL_0006: ldc.i4.4
IL_0007: newarr [System.Private.CoreLib]System.Object
IL_000c: dup
IL_000d: ldc.i4.0
IL_000e: ldc.i4.1
IL_000f: box [System.Private.CoreLib]System.Int32
IL_0014: stelem.ref
IL_0015: dup
IL_0016: ldc.i4.1
IL_0017: ldc.i4.2
IL_0018: box [System.Private.CoreLib]System.Int32
IL_001d: stelem.ref
IL_001e: dup
IL_001f: ldc.i4.2
IL_0020: ldc.i4.3
IL_0021: box [System.Private.CoreLib]System.Int32
IL_0026: stelem.ref
IL_0027: dup
IL_0028: ldc.i4.3
IL_0029: ldc.i4.4
IL_002a: box [System.Private.CoreLib]System.Int32
IL_002f: stelem.ref
IL_0030: call string [System.Private.CoreLib]System.String::Format(string, object[])
IL_0035: stloc.0
IL_0036: ret
} // end of method Program::FormatTest4
.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
// Method begins at RVA 0x20bf
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [System.Private.CoreLib]System.Object::.ctor()
IL_0006: nop
IL_0007: ret
} // end of method Program::.ctor
} // end of class Program
Run Code Online (Sandbox Code Playgroud)
我的问题是:
这是最常见情况下的性能优化。通过对通用数量的参数使用单独的重载,它们不必创建params数组参数,从而节省了分配空间(尽管可能仍需要装箱,但这比数组便宜)。从理论上讲,0、1、2和3参数的重载不需要存在,因为采用a的方法也params object[]可以处理所有重载。它只是更昂贵。
dup复制堆栈上的当前项目。stelem.ref从堆栈中获取三个项目,数组,索引和该数组索引的值,并将值存储在数组中的索引处。这意味着之后数组引用不再在堆栈上。因此dup。我们想要将该数组引用保留在堆栈的顶部,因为我们需要将其传递给被调用的方法,因此我们创建一个数组,对其进行复制,将索引和第一个项目压入,用于stelem.ref将该项目存储在数组中,并仍然有该数组引用,否则该引用将不复存在。
有其他方法可以做到这一点。如果采用从反编译的C#复制的代码,则最终会得到不同的IL,每次都从局部变量获取数组引用:
IL_0036: ldc.i4.4
IL_0037: newarr [System.Private.CoreLib]System.Object
IL_003c: stloc.1
IL_003d: ldloc.1
IL_003e: ldc.i4.0
IL_003f: ldc.i4.1
IL_0040: box [System.Private.CoreLib]System.Int32
IL_0045: stelem.ref
IL_0046: ldloc.1
IL_0047: ldc.i4.1
IL_0048: ldc.i4.2
IL_0049: box [System.Private.CoreLib]System.Int32
IL_004e: stelem.ref
Run Code Online (Sandbox Code Playgroud)
我认为这样做的效率不如dup,但JIT可能并不在乎这两种方式。真正反编译的C#代码实际上是这样的:
string text = string.Format("{0}{1}{2}{3}", new object[] { 1, 2, 3, 4 });
Run Code Online (Sandbox Code Playgroud)
这导致与IL相同的IL
string text = string.Format("{0}{1}{2}{3}", 1, 2, 3, 4);
Run Code Online (Sandbox Code Playgroud)| 归档时间: |
|
| 查看次数: |
76 次 |
| 最近记录: |