我想知道是否有人可以向我解释编译器为观察一种简单方法在性能上的极端差异而可能对我造成的影响。
public static uint CalculateCheckSum(string str) {
char[] charArray = str.ToCharArray();
uint checkSum = 0;
foreach (char c in charArray) {
checkSum += c;
}
return checkSum % 256;
}
我正在与一位同事一起为消息处理应用程序进行一些基准测试/优化。在Visual Studio 2012中,使用相同的输入字符串执行此功能的1000万次迭代大约需要25秒,但是,使用“ Optimize Code”选项构建项目时,将在7秒内执行相同的代码,进行同样的1000万次迭代。
我非常想了解编译器在后台进行的操作,以便我们能够看到像这样的无辜代码块的性能提高了3倍以上。
根据要求,这是一个完整的控制台应用程序,用于演示我所看到的内容。
class Program
{
public static uint CalculateCheckSum(string str)
{
char[] charArray = str.ToCharArray();
uint checkSum = 0;
foreach (char c in charArray)
{
checkSum += c;
}
return checkSum % 256;
}
static void Main(string[] args)
{
string stringToCount = "8=FIX.4.29=15135=D49=SFS56=TOMW34=11752=20101201-03:03:03.2321=DEMO=DG00121=155=IBM54=138=10040=160=20101201-03:03:03.23244=10.059=0100=ARCA10=246";
Stopwatch stopwatch = Stopwatch.StartNew();
for (int i = 0; i < 10000000; i++)
{
CalculateCheckSum(stringToCount);
}
stopwatch.Stop();
Console.WriteLine(stopwatch.Elapsed);
}
}
在关闭优化的情况下在调试中运行,我看到了13秒,而我得到了2秒。
在3.1版和2.3秒的优化版本中运行。
要查看C#编译器为您执行的操作,您需要查看IL。如果您想了解它如何影响JITted代码,则需要查看Scott Chamberlain所描述的本机代码。请注意,JITted代码会根据处理器体系结构,CLR版本,启动过程的方式以及其他因素而有所不同。
我通常从IL开始,然后潜在地看一下JITted代码。
比较IL的使用ildasm
可能会有些棘手,因为它包括每个指令的标签。这是您的方法的两个版本,分别经过优化和不优化(使用C#5编译器)编译,并nop
删除了多余的标签(和说明),以使它们尽可能容易地进行比较:
优化
.method public hidebysig static uint32
CalculateCheckSum(string str) cil managed
{
// Code size 46 (0x2e)
.maxstack 2
.locals init (char[] V_0,
uint32 V_1,
char V_2,
char[] V_3,
int32 V_4)
ldarg.0
callvirt instance char[] [mscorlib]System.String::ToCharArray()
stloc.0
ldc.i4.0
stloc.1
ldloc.0
stloc.3
ldc.i4.0
stloc.s V_4
br.s loopcheck
loopstart:
ldloc.3
ldloc.s V_4
ldelem.u2
stloc.2
ldloc.1
ldloc.2
add
stloc.1
ldloc.s V_4
ldc.i4.1
add
stloc.s V_4
loopcheck:
ldloc.s V_4
ldloc.3
ldlen
conv.i4
blt.s loopstart
ldloc.1
ldc.i4 0x100
rem.un
ret
} // end of method Program::CalculateCheckSum
未优化
.method public hidebysig static uint32
CalculateCheckSum(string str) cil managed
{
// Code size 63 (0x3f)
.maxstack 2
.locals init (char[] V_0,
uint32 V_1,
char V_2,
uint32 V_3,
char[] V_4,
int32 V_5,
bool V_6)
ldarg.0
callvirt instance char[] [mscorlib]System.String::ToCharArray()
stloc.0
ldc.i4.0
stloc.1
ldloc.0
stloc.s V_4
ldc.i4.0
stloc.s V_5
br.s loopcheck
loopstart:
ldloc.s V_4
ldloc.s V_5
ldelem.u2
stloc.2
ldloc.1
ldloc.2
add
stloc.1
ldloc.s V_5
ldc.i4.1
add
stloc.s V_5
loopcheck:
ldloc.s V_5
ldloc.s V_4
ldlen
conv.i4
clt
stloc.s V_6
ldloc.s V_6
brtrue.s loopstart
ldloc.1
ldc.i4 0x100
rem.un
stloc.3
br.s methodend
methodend:
ldloc.3
ret
}
注意事项:
blt.s
而不是clt
紧随其后的brtrue.s
(这是额外的本地变量之一的原因)。本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句