做不必要的大括号会降低性能

本文关键字:性能 不必要 | 更新日期: 2023-09-27 18:02:44

最近在编程时遇到这个问题后,我一直在想这个问题。以下是 2 个既合法又可编译的代码片段。具体来说,我的问题是这个..在第二种情况下,括号会使程序变慢吗?还有为什么允许这样做?

第一种情况:

if (statement)
{
 // do something
}

第二种情况:

{
    if (statement)
    {
        // do something
    }
}

另外,如果我有类似下面的代码怎么办..运行时是否与仅调用函数 X 而不调用任何大括号相同。

{
  {
    {
      // call function X
    }
  }
}

做不必要的大括号会降低性能

大多数时候,它没有任何区别 - 你绝对应该为可读性编写代码,而不是其他任何东西。

然而,花括号可以以一种令人惊讶的方式对性能产生影响,尽管它很不寻常。请考虑以下代码:

using System;
using System.Collections.Generic;
class Test
{
    static void FewerCurlies()
    {
        List<Action> actions = new List<Action>();
        for (int i = 0; i < 100; i++)
        {
            int x;
            if (i % 3 == 0)
            {
                actions.Add(() => x = 10);
            }
            int y;
            if (i % 3 == 1)
            {
                actions.Add(() => y = 10);
            }
        }
    }
    static void MoreCurlies()
    {
        List<Action> actions = new List<Action>();
        for (int i = 0; i < 100; i++)
        {
            {
                int x;
                if (i % 3 == 0)
                {
                    actions.Add(() => x = 10);
                }
            }
            {
                int y;
                if (i % 3 == 1)
                {
                    actions.Add(() => y = 10);
                }
            }
        }
    }
}

MoreCurlies中多余的大括号看起来是多余的,对吧?差一点。。。生成的代码看起来更像这样:

using System;
using System.Collections.Generic;
class Test
{
    static void FewerCurlies()
    {
        List<Action> actions = new List<Action>();
        for (int i = 0; i < 100; i++)
        {
            FewerCurliesCapture capture = new FewerCurliesCapture();
            if (i % 3 == 0)
            {
                actions.Add(capture.Method1);
            }
            if (i % 3 == 1)
            {
                actions.Add(capture.Method2);
            }
        }
    }
    static void MoreCurlies()
    {
        List<Action> actions = new List<Action>();
        for (int i = 0; i < 100; i++)
        {
            {
                MoreCurliesCapture1 capture = new MoreCurliesCapture1();
                if (i % 3 == 0)
                {
                    actions.Add(capture.Method);
                }
            }
            {
                MoreCurliesCapture1 capture = new MoreCurliesCapture2();
                if (i % 3 == 1)
                {
                    actions.Add(capture.Method);
                }
            }
        }
    }
    private class FewerCurliesCapture
    {
        public int x;
        public int y;
        public void Method1()
        {
            x = 10;
        }
        public void Method2()
        {
            y = 10;
        }
    }
    private class MoreCurliesCapture1
    {
        public int x;
        public void Method()
        {
            x = 10;
        }
    }
    private class MoreCurliesCapture2
    {
        public int y;
        public void Method()
        {
            y = 10;
        }
    }
}

这里的区别是:

  • 捕获类的实例在循环的每次迭代中都会创建 FewerCurlies ,即使它没有被使用
  • FewerCurlies中使用的捕获类的每个实例都包含这两个变量,即使每个委托实际上只使用其中一个变量,而MoreCurlies每个捕获类只捕获一个变量

这在某种程度上是特定于实现的,但它表明看起来多余的卷曲可能会产生影响。

简短的回答是"不,它们不会降低性能"。

编译器需要大括号来确定变量的范围,并知道当前语句组的结束位置。编译器完成处理后,带有和不带不必要的大括号的代码将生成相同的输出。

请注意,这与编译代码的性能有关,与编译器本身的性能无关。编译器将花费额外的时间来编译代码,这仅仅是因为输入的原始大小更大。但是,为了使这个额外的时间变得可测量,不必要的牙套的数量需要相当极端。

与此类问题一样,答案在于它生成的 IL。对于以下代码示例:

public int X()
{
    {
        {
            {
                return 0;
            }
        }
    }
}
public int Y()
{
    return 0;
}

我们最终得到以下编译的 IL:

.method public hidebysig instance int32  X() cil managed
{
  // Code size       2 (0x2)
  .maxstack  8
  IL_0000:  ldc.i4.0
  IL_0001:  ret
} // end of method SomeType::X
.method public hidebysig instance int32  Y() cil managed
{
  // Code size       2 (0x2)
  .maxstack  8
  IL_0000:  ldc.i4.0
  IL_0001:  ret
} // end of method SomeType::Y

它们是相同的。所以不,它对性能没有影响。 X读起来很可怕,但这是另一个问题。

更新 {}会影响变量的范围,所以也许这可能会产生影响。 再次,让我们检查一下:

public int X()
{
    var i = 1;
    {
        {
            i++;
            {
                return i;
            }
        }
    }
}
public int Y()
{
    var i = 1;
    i++;
    return i;
}

再一次,生成的 IL 是相同的:

// Code size       8 (0x8)
.maxstack  2
.locals init ([0] int32 i)
IL_0000:  ldc.i4.1
IL_0001:  stloc.0
IL_0002:  ldloc.0
IL_0003:  ldc.i4.1
IL_0004:  add
IL_0005:  stloc.0
IL_0006:  ldloc.0
IL_0007:  ret

但是,如果在闭包中捕获变量,它确实会影响事情。 在以下情况下,X确实会创建更多的 IL,这会对性能产生影响:

public Func<int> X()
{
    {
        var i = 1;
        {
            i++;
            {
                return () => i;
            }
        }
    }
}
public Func<int> Y()
{
    var i = 1;
    i++;
    return () => i;
}

与C++不同,在中,编译器可能需要在变量进入或超出范围时生成代码,C# 中的大多数变量都有效地提升到封闭函数级范围。 代码:

void foo()
{
  {
    int i;
    ... stuff using i as int
  }
  {
    char i;
    ... stuff using i as char
  }
}

将有效地转化为:

void foo()
{
  int i__1;
  char i__2;
  ... stuff using i__1 as int
  ... stuff using i__2 as char
}

使用第一个变量的第一个支撑部分中的代码i__1它使用i的任何地方,以及第二个使用i__2的代码。 在某些情况下,在多个作用域块中声明具有相同名称和用途的变量可能比在外部作用域块中声明具有该共同用途的变量效率低,但它很少会产生有意义的效果。 在大多数情况下,实时编译器将能够确定代码中的多个变量可以安全地映射到同一存储位置,即使在无法映射到一些额外变量所需的存储位置的情况下,也不太可能对性能产生太大影响。

没有大括号不会降低性能。

它有助于理解代码并提供良好的代码格式。但是一些大括号是强制性的,例如函数开始/结束,循环开始/结束,条件开始/结束,这个barces也有助于理解变量的裁剪。

它不会导致任何性能下降,但使用大括号肯定会增加代码的可读性。在真实的单词场景中,当你有同行代码审查或结对编程时,你写的东西会更加清晰和可读。

通过以下链接

http://www.c-sharpcorner.com/UploadFile/d0e913/lame-question-of-the-day-role-of-curly-braces-in-our-cod/

使用不必要的大括号,假设您没有嵌套变量,只会在将代码转换为字节码或机器代码时向标签表添加标签。因此,在最糟糕的情况下,构建时间会变慢。如果你在嵌套中有变量,如果它们是没有破坏代码的原语,你仍然应该没有问题,但如果你在嵌套大括号内创建了对象,那么将需要对 GC 有更深入的理解,但我强烈怀疑是否存在任何明显的差异。在所有情况下,由于编译器在生成项目时通过在查找表中存储引用来执行额外的工作,因此在生成项目时会有轻微的延迟,尽管可能不明显。