C#编译器优化

杰西·卡特(Jesse Carter)

我想知道是否有人可以向我解释编译器为观察一种简单方法在性能上的极端差异而可能对我造成的影响。

 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
  }

注意事项:

  • 优化版本使用较少的本地语言。这可以使JIT更有效地使用寄存器。
  • 经过优化的版本在检查是否再次循环时使用blt.s而不是clt紧随其后的brtrue.s(这是额外的本地变量之一的原因)。
  • 未优化的版本在返回之前使用其他本地存储返回值,以简化调试。
  • 未优化的版本在返回之前就具有一个无条件分支。
  • 优化版本较短,但我怀疑它是否短到可以内联,因此我怀疑这无关紧要。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

C#编译器会优化此代码吗?

来自分类Dev

C#编译器不会优化foreach吗?

来自分类Dev

这是C#编译器完成的优化吗?

来自分类Dev

了解MSVS C ++编译器优化

来自分类Dev

C 编译器优化的基准

来自分类Dev

C#垃圾收集器,线程和编译器/抖动优化

来自分类Dev

C#编译器优化-仅捕获包含throw的Catch

来自分类Dev

C#语言编译器是否自行执行任何实际的优化?

来自分类Dev

C#编译器是否会在if块优化为空

来自分类Dev

C#编译器或JIT是否可以优化Lambda表达式中的方法调用?

来自分类Dev

当其内容具有Conditional属性时,C#编译器是否优化foreach块?

来自分类Dev

禁用针对特定功能或代码块(C#)的编译器优化

来自分类Dev

避免C#编译器优化临时变量的Noop方法

来自分类Dev

编译器如何优化C#中的异常过滤器?

来自分类Dev

为什么C#编译器无法优化简单的异步/等待方法

来自分类Dev

是否有C#编译器为“优化代码”定义,如存在DEBUG或TRACE?

来自分类Dev

C#编译器优化-仅捕获包含throw的Catch

来自分类Dev

编译器优化问题

来自分类Dev

编译器的优化指标

来自分类Dev

关于变量范围的C / C ++编译器优化

来自分类Dev

C / C ++优化编译器由于使用模板而损坏

来自分类Dev

C# 编译器标志

来自分类Dev

Microsoft C ++优化编译器已停止工作

来自分类Dev

C ++编译器优化放弃模板专业化

来自分类Dev

是否允许C编译器优化掉多余的语句?

来自分类Dev

Microsoft C ++优化编译器不断崩溃

来自分类Dev

C ++ inline关键字和编译器优化

来自分类Dev

现代C编译器能否优化位访问的组合?

来自分类Dev

我如何“告诉” C编译器不应优化代码?

Related 相关文章

热门标签

归档