已编译 MSIL 分析(第 2 部分)





2.00/5 (7投票s)
2006 年 10 月 8 日
2分钟阅读

25623
分析 .NET 编译器为 4 种语言生成的 MSIL,在 VS 2005 中
编译后的 MSIL 分析 (第 2 部分)
这篇文章是 (编译后的 MSIL 分析 (第 1 部分)) 的后续文章。我打算探讨编译器的工作原理以及不同语言生成的内容。我还想探讨如何使用 .NET 框架提供的通用对象来优化代码。
概念
前一篇文章中的应用程序已更新,以使用 .NET 框架的 StringBuilder
类。主要是为了说明该类在执行字符串连接时的效率。经常有人建议使用字符串生成器类来处理字符串连接工作,但我一直在想,是否有人真正见过该类提供的效率证明。
本文中遵循的一些额外的优化技术是处理 VB 应用程序。这些技术/测试包括将 VS.NET 提供的默认模块更改为类,只是为了查看编译器是否能看到这两者之间的区别。我还更改了几个项目设置。
- 虽然不是一种优化技术,但更多的是一项测试,以查看 VB 编译器中类和模块之间是否真的存在任何差异,我决定将 Module1 的默认声明从 Module 更改为 Class。
- 在项目属性编译选项卡中删除了整数溢出检查(这可以在高级设置下找到)
- 开启了 Option Strict
为了真正测试这些优化,我想让这两种方法同时运行,并将每种方法处理所需的时间长度打印到控制台,以便比较每种方法所需的时间。我还想更改每个旧应用程序中的一个项目,并将 "strSomeString
" 变量的声明从提供空引号(即 "")更改为使用 .NET 框架的 string.empty
。主要是为了查看编译器生成的内容。
代码
VB
<code>Imports System.Diagnostics
Class Module1
Shared Sub Main()
Dim sbSomeString As New System.Text.StringBuilder
Dim sw As New Stopwatch
sw.Start()
For i As Integer = 0 To 1000
sbSomeString.Append("Test ")
Next
sw.Stop()
Console.WriteLine(sbSomeString.ToString)
Console.WriteLine("Time Taken :" & sw.ElapsedMilliseconds)
OldCode()
End Sub
Shared Sub OldCode()
Dim strSomeString As String = String.Empty
Dim sw As New Stopwatch
sw.Start()
For i As Integer = 0 To 1000
strSomeString += "Test "
Next
sw.Stop()
Console.WriteLine(strSomeString)
Console.WriteLine("Time Taken :" & sw.ElapsedMilliseconds)
Console.ReadLine()
End Sub
End Class
C#
<code>using System;
using System.Diagnostics;
namespace ConsoleCSharp
{
class Program
{
static void Main(string[] args)
{
System.Text.StringBuilder sbSomeString = new System.Text.StringBuilder();
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < 1000; i++)
{
sbSomeString.Append("Test ");
}
sw.Stop();
Console.WriteLine(sbSomeString.ToString());
Console.WriteLine("Time Taken :" + sw.ElapsedMilliseconds);
OldCode();
}
static void OldCode()
{
string strSomeString = string.Empty;
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < 1000; i++)
{
strSomeString += "Test ";
}
sw.Stop();
Console.WriteLine(strSomeString);
Console.WriteLine("Time Taken :" + sw.ElapsedMilliseconds);
Console.ReadLine();
}
}
}
J#
<code>package ConsoleJSharp;
import System.*;
import System.Diagnostics.*;
public class Program
{
public static void main(String[] args)
{
System.Text.StringBuilder sbSomeString = new System.Text.StringBuilder();
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < 1000; i++)
{
sbSomeString.Append("Test ");
}
sw.Stop();
Console.WriteLine(sbSomeString.ToString());
Console.WriteLine("Time Taken :" + sw.get_ElapsedMilliseconds());
OldCode();
}
public static void OldCode()
{
String strSomeString = String.Empty;
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < 1000; i++)
{
strSomeString += "Test ";
}
sw.Stop();
Console.WriteLine(strSomeString);
Console.WriteLine("Time Taken :" + sw.get_ElapsedMilliseconds());
Console.ReadLine();
}
}
C++
<code>
#include "stdafx.h"
#using <mscorlib.dll>
using namespace System;
using namespace System::Diagnostics;
void OldCode();
int main(array<System::String ^> ^args)
{
System::Text::StringBuilder^ sbSomeString = gcnew System::Text::StringBuilder();
Stopwatch^ sw = gcnew Stopwatch;
sw->Start();
for (int i = 0;i< 1000 ;i++)
{
sbSomeString->Append("Test ");
}
sw->Stop();
Console::WriteLine(sbSomeString->ToString());
Console::WriteLine("Time Taken :" + sw->ElapsedMilliseconds);
OldCode();
return 0;
}
void OldCode()
{
String^ strSomeString = String::Empty;
Stopwatch^ sw = gcnew Stopwatch;
sw->Start();
for (int i = 0;i< 1000 ;i++)
{
strSomeString += "Test ";
}
sw->Stop();
Console::WriteLine(strSomeString);
Console::WriteLine("Time Taken :" + sw->ElapsedMilliseconds);
Console::ReadLine();
}
MSIL
VB
<code>
.method public static void Main() cil managed
{
.entrypoint
.custom instance
void [mscorlib]System.STAThreadAttribute::.ctor() = ( 01 00 00 00 )
// Code size 103 (0x67)
.maxstack 2
.locals init ([0] class [mscorlib]System.Text.StringBuilder sbSomeString,
[1] class [System]System.Diagnostics.Stopwatch sw,
[2] int32 i,
[3] int32 VB$CG$t_i4$S0)
IL_0000: nop
IL_0001: newobj instance void [mscorlib]System.Text.StringBuilder::.ctor()
IL_0006: stloc.0
IL_0007: newobj instance void [System]System.Diagnostics.Stopwatch::.ctor()
IL_000c: stloc.1
IL_000d: ldloc.1
IL_000e: callvirt instance void [System]System.Diagnostics.Stopwatch::Start()
IL_0013: nop
IL_0014: ldc.i4.0
IL_0015: stloc.2
IL_0016: ldloc.0
IL_0017: ldstr "Test "
IL_001c: callvirt instance class [mscorlib]System.Text.StringBuilder
[mscorlib]System.Text.StringBuilder::Append(string)
IL_0021: pop
IL_0022: nop
IL_0023: ldloc.2
IL_0024: ldc.i4.1
IL_0025: add.ovf
IL_0026: stloc.2
IL_0027: ldloc.2
IL_0028: ldc.i4 0x3e8
IL_002d: stloc.3
IL_002e: ldloc.3
IL_002f: ble.s IL_0016
IL_0031: ldloc.1
IL_0032: callvirt
instance void [System]System.Diagnostics.Stopwatch::Stop()
IL_0037: nop
IL_0038: ldloc.0
IL_0039: callvirt
instance string [mscorlib]System.Text.StringBuilder::ToString()
IL_003e: call void [mscorlib]System.Console::WriteLine(string)
IL_0043: nop
IL_0044: ldstr "Time Taken :"
IL_0049: ldloc.1
IL_004a: callvirt instance int64
[System]System.Diagnostics.Stopwatch::get_ElapsedMilliseconds()
IL_004f: call string
[Microsoft.VisualBasic]
Microsoft.VisualBasic.CompilerServices.Conversions::ToString(int64)
IL_0054: call string [mscorlib]System.String::Concat(string,
string)
IL_0059: call void [mscorlib]System.Console::WriteLine(string)
IL_005e: nop
IL_005f: call void VBConsole.Module1::OldCode()
IL_0064: nop
IL_0065: nop
IL_0066: ret
} // end of method Module1::Main
.method public static void OldCode() cil managed
{
// Code size 98 (0x62)
.maxstack 2
.locals init ([0] string strSomeString,
[1] class [System]System.Diagnostics.Stopwatch sw,
[2] int32 i,
[3] int32 VB$CG$t_i4$S0)
IL_0000: nop
IL_0001: ldsfld string [mscorlib]System.String::Empty
IL_0006: stloc.0
IL_0007: newobj instance void
[System]System.Diagnostics.Stopwatch::.ctor()
IL_000c: stloc.1
IL_000d: ldloc.1
IL_000e: callvirt instance void
[System]System.Diagnostics.Stopwatch::Start()
IL_0013: nop
IL_0014: ldc.i4.0
IL_0015: stloc.2
IL_0016: ldloc.0
IL_0017: ldstr "Test "
IL_001c: call string [mscorlib]System.String::Concat(string,
string)
IL_0021: stloc.0
IL_0022: nop
IL_0023: ldloc.2
IL_0024: ldc.i4.1
IL_0025: add.ovf
IL_0026: stloc.2
IL_0027: ldloc.2
IL_0028: ldc.i4 0x3e8
IL_002d: stloc.3
IL_002e: ldloc.3
IL_002f: ble.s IL_0016
IL_0031: ldloc.1
IL_0032: callvirt instance void [System]System.Diagnostics.Stopwatch::Stop()
IL_0037: nop
IL_0038: ldloc.0
IL_0039: call void [mscorlib]System.Console::WriteLine(string)
IL_003e: nop
IL_003f: ldstr "Time Taken :"
IL_0044: ldloc.1
IL_0045: callvirt instance int64
[System]System.Diagnostics.Stopwatch::get_ElapsedMilliseconds()
IL_004a: call string [Microsoft.VisualBasic]
Microsoft.VisualBasic.CompilerServices.Conversions::ToString(int64)
IL_004f: call string [mscorlib]System.String::Concat(string,
string)
IL_0054: call void [mscorlib]System.Console::WriteLine(string)
IL_0059: nop
IL_005a: call string [mscorlib]System.Console::ReadLine()
IL_005f: pop
IL_0060: nop
IL_0061: ret
} // end of method Module1::OldCode
C#
<code>
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 107 (0x6b)
.maxstack 2
.locals init ([0] class [mscorlib]System.Text.StringBuilder sbSomeString,
[1] class [System]System.Diagnostics.Stopwatch sw,
[2] int32 i,
[3] bool CS$4$0000)
IL_0000: nop
IL_0001: newobj instance void [mscorlib]System.Text.StringBuilder::.ctor()
IL_0006: stloc.0
IL_0007: newobj instance void [System]System.Diagnostics.Stopwatch::.ctor()
IL_000c: stloc.1
IL_000d: ldloc.1
IL_000e: callvirt instance void [System]System.Diagnostics.Stopwatch::Start()
IL_0013: nop
IL_0014: ldc.i4.0
IL_0015: stloc.2
IL_0016: br.s IL_002a
IL_0018: nop
IL_0019: ldloc.0
IL_001a: ldstr "Test "
IL_001f: callvirt instance class
[mscorlib]System.Text.StringBuilder
[mscorlib]System.Text.StringBuilder::Append(string)
IL_0024: pop
IL_0025: nop
IL_0026: ldloc.2
IL_0027: ldc.i4.1
IL_0028: add
IL_0029: stloc.2
IL_002a: ldloc.2
IL_002b: ldc.i4 0x3e8
IL_0030: clt
IL_0032: stloc.3
IL_0033: ldloc.3
IL_0034: brtrue.s IL_0018
IL_0036: ldloc.1
IL_0037: callvirt instance void [System]System.Diagnostics.Stopwatch::Stop()
IL_003c: nop
IL_003d: ldloc.0
IL_003e: callvirt instance string [mscorlib]System.Object::ToString()
IL_0043: call void [mscorlib]System.Console::WriteLine(string)
IL_0048: nop
IL_0049: ldstr "Time Taken :"
IL_004e: ldloc.1
IL_004f: callvirt instance int64
[System]System.Diagnostics.Stopwatch::get_ElapsedMilliseconds()
IL_0054: box [mscorlib]System.Int64
IL_0059: call string [mscorlib]System.String::Concat(object,
object)
IL_005e: call void [mscorlib]System.Console::WriteLine(string)
IL_0063: nop
IL_0064: call void ConsoleCSharp.Program::OldCode()
IL_0069: nop
IL_006a: ret
} // end of method Program::Main
.method private hidebysig static void OldCode() cil managed
{
// Code size 102 (0x66)
.maxstack 2
.locals init ([0] string strSomeString,
[1] class [System]System.Diagnostics.Stopwatch sw,
[2] int32 i,
[3] bool CS$4$0000)
IL_0000: nop
IL_0001: ldsfld string [mscorlib]System.String::Empty
IL_0006: stloc.0
IL_0007: newobj instance void [System]System.Diagnostics.Stopwatch::.ctor()
IL_000c: stloc.1
IL_000d: ldloc.1
IL_000e: callvirt instance void [System]System.Diagnostics.Stopwatch::Start()
IL_0013: nop
IL_0014: ldc.i4.0
IL_0015: stloc.2
IL_0016: br.s IL_002a
IL_0018: nop
IL_0019: ldloc.0
IL_001a: ldstr "Test "
IL_001f: call string [mscorlib]System.String::Concat(string,
string)
IL_0024: stloc.0
IL_0025: nop
IL_0026: ldloc.2
IL_0027: ldc.i4.1
IL_0028: add
IL_0029: stloc.2
IL_002a: ldloc.2
IL_002b: ldc.i4 0x3e8
IL_0030: clt
IL_0032: stloc.3
IL_0033: ldloc.3
IL_0034: brtrue.s IL_0018
IL_0036: ldloc.1
IL_0037: callvirt instance void [System]System.Diagnostics.Stopwatch::Stop()
IL_003c: nop
IL_003d: ldloc.0
IL_003e: call void [mscorlib]System.Console::WriteLine(string)
IL_0043: nop
IL_0044: ldstr "Time Taken :"
IL_0049: ldloc.1
IL_004a: callvirt instance int64
[System]System.Diagnostics.Stopwatch::get_ElapsedMilliseconds()
IL_004f: box [mscorlib]System.Int64
IL_0054: call string [mscorlib]System.String::Concat(object,
object)
IL_0059: call void [mscorlib]System.Console::WriteLine(string)
IL_005e: nop
IL_005f: call string [mscorlib]System.Console::ReadLine()
IL_0064: pop
IL_0065: ret
} // end of method Program::OldCode
J#
<code> .method public hidebysig static void main(string[] args) cil managed { .entrypoint // Code size 122 (0x7a) .maxstack 2 .locals init ([0] class [mscorlib]System.Text.StringBuilder sbSomeString, [1] class [System]System.Diagnostics.Stopwatch sw, [2] int32 i) IL_0000: ldtoken [vjslib]com.ms.vjsharp.lang.ObjectImpl IL_0005: call void [mscorlib] System.Runtime.CompilerServices. RuntimeHelpers::RunClassConstructor (valuetype [mscorlib]System.RuntimeTypeHandle) IL_000a: newobj instance void [mscorlib]System.Text.StringBuilder::.ctor() IL_000f: stloc.0 IL_0010: newobj instance void [System]System.Diagnostics.Stopwatch::.ctor() IL_0015: stloc.1 IL_0016: ldloc.1 IL_0017: callvirt instance void [System]System.Diagnostics.Stopwatch::Start() IL_001c: ldc.i4.0 IL_001d: stloc.2 IL_001e: br.s IL_0030 IL_0020: ldloc.0 IL_0021: ldstr "Test " IL_0026: callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string) IL_002b: pop IL_002c: ldloc.2 IL_002d: ldc.i4.1 IL_002e: add IL_002f: stloc.2 IL_0030: ldloc.2 IL_0031: ldc.i4 0x3e8 IL_0036: blt.s IL_0020 IL_0038: ldloc.1 IL_0039: callvirt instance void [System]System.Diagnostics.Stopwatch::Stop() IL_003e: ldloc.0 IL_003f: callvirt instance string [mscorlib]System.Text.StringBuilder::ToString() IL_0044: call void [mscorlib]System.Console::WriteLine(string) IL_0049: newobj instance void [vjslib]java.lang.StringBuffer::.ctor() IL_004e: ldstr "Time Taken :" IL_0053: callvirt instance class [vjslib]java.lang.StringBuffer [vjslib]java.lang.StringBuffer::append(string) IL_0058: ldloc.1 IL_0059: callvirt instance int64 [System]System.Diagnostics.Stopwatch::get_ElapsedMilliseconds() IL_005e: callvirt instance class [vjslib]java.lang.StringBuffer [vjslib]java.lang.StringBuffer::append(int64) IL_0063: callvirt instance string [vjslib]java.lang.StringBuffer::ToString() IL_0068: call void [mscorlib]System.Console::WriteLine(string) IL_006d: call void ConsoleJSharp.Program::OldCode() IL_0072: call void [vjslib]com.ms.vjsharp.util.Utilities::cleanupAfterMainReturns() IL_0077: br.s IL_0079 IL_0079: ret } // end of method Program::main .method public hidebysig static void OldCode() cil managed { // Code size 118 (0x76) .maxstack 2 .locals init ([0] string strSomeString, [1] class [System]System.Diagnostics.Stopwatch sw, [2] int32 i) IL_0000: ldsfld string [mscorlib]System.String::Empty IL_0005: stloc.0 IL_0006: newobj instance void [System]System.Diagnostics.Stopwatch::.ctor() IL_000b: stloc.1 IL_000c: ldloc.1 IL_000d: callvirt instance void [System]System.Diagnostics.Stopwatch::Start() IL_0012: ldc.i4.0 IL_0013: stloc.2 IL_0014: br.s IL_0035 IL_0016: newobj instance void [vjslib]java.lang.StringBuffer::.ctor() IL_001b: ldloc.0 IL_001c: callvirt instance class [vjslib] java.lang.StringBuffer [vjslib]java.lang.StringBuffer::append(string) IL_0021: ldstr "Test " IL_0026: callvirt instance class [vjslib]java.lang.StringBuffer [vjslib]java.lang.StringBuffer::append(string) IL_002b: callvirt instance string [vjslib]java.lang.StringBuffer::ToString() IL_0030: stloc.0 IL_0031: ldloc.2 IL_0032: ldc.i4.1 IL_0033: add IL_0034: stloc.2 IL_0035: ldloc.2 IL_0036: ldc.i4 0x3e8 IL_003b: blt.s IL_0016 IL_003d: ldloc.1 IL_003e: callvirt instance void [System]System.Diagnostics.Stopwatch::Stop() IL_0043: ldloc.0 IL_0044: call void [mscorlib]System.Console::WriteLine(string) IL_0049: newobj instance void [vjslib]java.lang.StringBuffer::.ctor() IL_004e: ldstr "Time Taken :" IL_0053: callvirt instance class [vjslib]java.lang.StringBuffer [vjslib]java.lang.StringBuffer::append(string) IL_0058: ldloc.1 IL_0059: callvirt instance int64 [System]System.Diagnostics.Stopwatch::get_ElapsedMilliseconds() IL_005e: callvirt instance class [vjslib]java.lang.StringBuffer [vjslib]java.lang.StringBuffer::append(int64) IL_0063: callvirt instance string [vjslib]java.lang.StringBuffer::ToString() IL_0068: call void [mscorlib]System.Console::WriteLine(string) IL_006d: call string [mscorlib]System.Console::ReadLine() IL_0072: pop IL_0073: br.s IL_0075 IL_0075: ret } // end of method Program::OldCode
C++
<code>
.method assembly static int32 main(string[] args) cil managed
{
// Code size 106 (0x6a)
.maxstack 2
.locals ([0] class [System]System.Diagnostics.Stopwatch sw,
[1] int32 i,
[2] class [mscorlib]System.Text.StringBuilder sbSomeString,
[3] int32 V_3)
IL_0000: ldc.i4.0
IL_0001: stloc.3
IL_0002: ldnull
IL_0003: stloc.2
IL_0004: ldnull
IL_0005: stloc.0
IL_0006: newobj instance void [mscorlib]System.Text.StringBuilder::.ctor()
IL_000b: stloc.2
IL_000c: newobj instance void [System]System.Diagnostics.Stopwatch::.ctor()
IL_0011: stloc.0
IL_0012: ldloc.0
IL_0013: call instance void [System]System.Diagnostics.Stopwatch::Start()
IL_0018: ldc.i4.0
IL_0019: stloc.1
IL_001a: br.s IL_0020
IL_001c: ldloc.1
IL_001d: ldc.i4.1
IL_001e: add
IL_001f: stloc.1
IL_0020: ldloc.1
IL_0021: ldc.i4 0x3e8
IL_0026: bge.s IL_0036
IL_0028: ldloc.2
IL_0029: ldstr "Test "
IL_002e: call instance class
[mscorlib]System.Text.StringBuilder
[mscorlib]System.Text.StringBuilder::Append(string)
IL_0033: pop
IL_0034: br.s IL_001c
IL_0036: ldloc.0
IL_0037: call instance void [System]System.Diagnostics.Stopwatch::Stop()
IL_003c: ldloc.2
IL_003d: callvirt instance string [mscorlib]System.Text.StringBuilder::ToString()
IL_0042: call void [mscorlib]System.Console::WriteLine(string)
IL_0047: ldstr "Time Taken :"
IL_004c: ldloc.0
IL_004d: call instance int64
[System]System.Diagnostics.Stopwatch::get_ElapsedMilliseconds()
IL_0052: box [mscorlib]System.Int64
IL_0057: call string [mscorlib]System.String::Concat(object,
object)
IL_005c: call void [mscorlib]System.Console::WriteLine(string)
IL_0061: call void modopt( [mscorlib]System.Runtime.CompilerServices.CallConvCdecl)
OldCode()
IL_0066: ldc.i4.0
IL_0067: stloc.3
IL_0068: ldloc.3
IL_0069: ret
} // end of method 'Global Functions'::main
.method assembly static void modopt ([mscorlib]System.Runtime.CompilerServices.CallConvCdecl)
OldCode() cil managed
{
.vtentry 1 : 1
// Code size 97 (0x61)
.maxstack 2
.locals ([0] class [System]System.Diagnostics.Stopwatch sw,
[1] string strSomeString,
[2] int32 i)
IL_0000: ldnull
IL_0001: stloc.1
IL_0002: ldnull
IL_0003: stloc.0
IL_0004: ldsfld string [mscorlib]System.String::Empty
IL_0009: stloc.1
IL_000a: newobj instance void [System]System.Diagnostics.Stopwatch::.ctor()
IL_000f: stloc.0
IL_0010: ldloc.0
IL_0011: call instance void [System]System.Diagnostics.Stopwatch::Start()
IL_0016: ldc.i4.0
IL_0017: stloc.2
IL_0018: br.s IL_001e
IL_001a: ldloc.2
IL_001b: ldc.i4.1
IL_001c: add
IL_001d: stloc.2
IL_001e: ldloc.2
IL_001f: ldc.i4 0x3e8
IL_0024: bge.s IL_0034
IL_0026: ldloc.1
IL_0027: ldstr "Test "
IL_002c: call string [mscorlib]System.String::Concat(string,
string)
IL_0031: stloc.1
IL_0032: br.s IL_001a
IL_0034: ldloc.0
IL_0035: call instance void [System]System.Diagnostics.Stopwatch::Stop()
IL_003a: ldloc.1
IL_003b: call void [mscorlib]System.Console::WriteLine(string)
IL_0040: ldstr "Time Taken :"
IL_0045: ldloc.0
IL_0046: call instance int64
[System]System.Diagnostics.Stopwatch::get_ElapsedMilliseconds()
IL_004b: box [mscorlib]System.Int64
IL_0050: call string [mscorlib]System.String::Concat(object,
object)
IL_0055: call void [mscorlib]System.Console::WriteLine(string)
IL_005a: call string [mscorlib]System.Console::ReadLine()
IL_005f: pop
IL_0060: ret
} // end of method 'Global Functions'::OldCode
结论
我没有包含编译器生成的所有程序集代码。我只包含我认为值得注意的内容,即我只包含函数的代码。
有趣的是,StringBuilder
类能够在 0 毫秒内执行字符串连接,而字符串连接在传统方法中需要更长的时间,平均为 17 毫秒。
历史
- 06-10-06:上传了第 2 部分草稿版本 1
- 09-10-06:更新了内容、格式并更改了文章标题