【问题标题】:Will the compiler optimize for boolean assignment?编译器会针对布尔赋值进行优化吗?
【发布时间】:2012-06-01 15:01:44
【问题描述】:

这就是我想做的:

if(ABoolean || (BBoolean && CBoolean))
{
    SomeButton.Enabled = true;
    AnotherButton.Enabled = true;
}
else 
{
    SomeButton.Enabled = false;
    AnotherButton.Enabled = false;
}

我可以切换到:

SomeButton.Enabled = (ABoolean || (BBoolean && CBoolean));
AnotherButton.Enabled = (ABoolean || (BBoolean && CBoolean));

为了更简洁的代码。我的问题是,编译器是否会优化分配,使其看到布尔表达式相同并为第二个按钮分配其值,还是每次都计算该值。

注意:我知道这是一个微不足道的例子,加速/减速将微不足道到无关紧要的地步,但它有助于让我更好地理解编译器优化。

编辑:这就是我认为可能会优化第二个选项的原因:

class Program
{
    static bool ABoolean = true, BBoolean = true, CBoolean = false;
    static bool AEnable, BEnable;


    static void Main(string[] args)
    {
        Stopwatch sw = new Stopwatch();
        sw.Start();
        for (int i = 0; i < 1000000000; i++)
        {
            Operation1();
        }
        sw.Stop();
        Console.WriteLine(sw.ElapsedMilliseconds);

        Stopwatch sw1 = new Stopwatch();
        sw1.Start();
        for (int i = 0; i < 1000000000; i++)
        {
            Operation2();
        }
        sw1.Stop();
        Console.WriteLine(sw1.ElapsedMilliseconds);
        Console.Read();
    }

    static void Operation1()
    {
        if (ABoolean || (BBoolean && CBoolean))
        {
            AEnable = true;
            BEnable = true;
        }
        else
        {
            AEnable = false;
            BEnable = false;
        }
    }

    static void Operation2()
    {
        AEnable = (ABoolean || (BBoolean && CBoolean));
        BEnable = (ABoolean || (BBoolean && CBoolean));
    }
}

这在 10 亿次操作中产生了大约 8-9 秒的差异(第二个选项运行得更快)。随着我添加更多“启用”布尔值,第二个操作变得更慢。

【问题讨论】:

  • 顺便说一句:第三种方法是通过变量赋值:var enabled = (ABoolean || (BBoolean &amp;&amp; CBoolean)),然后使用该变量设置.Enabled 属性。

标签: c# compiler-optimization


【解决方案1】:

不,我不希望 编译器 优化它。 JIT 可能会优化它(因为它有更多信息),但我不希望 C# 编译器这样做。

编译器如何知道SomeButton.Enabled 是否会产生一些可能改变ABooleanBBooleanCBoolean 的值的副作用?

编辑:验证这个...让我们给 C# 编译器绝对最大的机会:

class Test
{
    static void Main()
    {
        Foo(true, false, true);
    }

    static void Foo(bool x, bool y, bool z)
    {
        A = x || (y && z);
        B = x || (y && z);
    }

    static bool A { get; set; }
    static bool B { get; set; }
}

编译:

csc /o+ /debug- Test.cs

Foo 通过 ILDASM 的代码:

.method private hidebysig static void  Foo(bool x,
                                           bool y,
                                           bool z) cil managed
{
  // Code size       37 (0x25)
  .maxstack  8
  IL_0000:  ldarg.0
  IL_0001:  brtrue.s   IL_000c
  IL_0003:  ldarg.1
  IL_0004:  brfalse.s  IL_0009
  IL_0006:  ldarg.2
  IL_0007:  br.s       IL_000d
  IL_0009:  ldc.i4.0
  IL_000a:  br.s       IL_000d
  IL_000c:  ldc.i4.1
  IL_000d:  call       void Test::set_A(bool)
  IL_0012:  ldarg.0
  IL_0013:  brtrue.s   IL_001e
  IL_0015:  ldarg.1
  IL_0016:  brfalse.s  IL_001b
  IL_0018:  ldarg.2
  IL_0019:  br.s       IL_001f
  IL_001b:  ldc.i4.0
  IL_001c:  br.s       IL_001f
  IL_001e:  ldc.i4.1
  IL_001f:  call       void Test::set_B(bool)
  IL_0024:  ret
} // end of method Test::Foo

如您所见,表达式确实在两种情况下都会被计算。

【讨论】:

  • 感谢您的回答,我很困惑,因为我实际上对这两个选项进行了计时,并且使用第二个选项获得了明显更快的结果。我假设这是由于 if/else 子句的开销。因此,在第二种情况下,布尔运算的数量应该有一个不归点,这会导致时间变慢,对吧?
  • @NominSim:我不希望任何一个版本都能显着获得更快的结果。我对您的诊断表示怀疑 :) 但实际上,这对您来说可能甚至有点重要吗?
  • 远远超过 10 亿次操作,差异约为 8 秒(~19 vs ~11)...我认为显着性是主观的,当我添加 5 个布尔赋值时,第二个选项变得更慢。绝对没有一点意义,我只是在解释我最初是如何得出它可能正在优化第二个版本的结论。
  • @NominSim:这听起来确实很有趣——但打破微优化测试真的很容易;特别是您可以很容易地发现一个版本是内联的,而另一个不是内联的,但不会对更大的方法产生影响。
  • 我在我的 OP 中编辑了我的测试代码,我不确定秒表是否是计时这类事情的最佳方式...你介意看一下它是否可行?
【解决方案2】:

我的问题是,编译器是否会优化分配,使其看到布尔表达式相同并为第二个按钮分配其值,还是每次都计算该值。

每次都会计算值。

如果这是一个多线程应用程序会怎样。其他一些线程可能会更改它。

如果它们不是常量变量,则可以更改。

要优化你可以做

SomeButton.Enabled = AnotherButton.Enabled = (ABoolean || (BBoolean && CBoolean));

在这种情况下,它将被计算一次,并首先将值分配给AnotherButton,然后再分配给SomeButton。记住它在分配中从右到左。

【讨论】:

  • 对。因为这些是涉及非常量变量的两个独立语句。所以编译器不能在这里假设任何东西。这两个语句之间的变量可能已经改变,特别是如果您的应用程序是多线程的。
  • 现在您只是从其他人的答案中获取详细信息。这很滑稽。但我不确定您发布的内容是否真的是优化。布尔求值非常快。而且我认为引用属性的get 访问器通常与调用内联方法具有相同的开销。所以这很容易比原始代码慢。虽然,我们谈论的是纳秒,但仍然如此。
  • @SteveWortham:是的,我承认我复制了它。但不是来自某人的回答,而是来自我的大脑。
  • 实际上,get 访问器不会被引用,因为赋值的返回值是被赋值的值。因此,布尔表达式将被评估一次,然后沿着链返回。如果分配链中有不同的类型,可能会产生微妙的影响,但这不是问题。
  • @SteveCzetty - 我进行了另一个测试,你说得对,get 访问器永远不会被调用。但我仍然不确定为什么它会这么慢。
【解决方案3】:

不,根据我的经验,编译器不会对其进行优化,但是,您可以这样做:

SomeButton.Enabled = AnotherButton.Enabled = (ABoolean || (BBoolean && CBoolean));

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-04-24
    • 2017-03-11
    • 1970-01-01
    • 2011-10-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-11
    相关资源
    最近更新 更多