C# 不必要的花括号会降低性能吗?
最近在编程时遇到了这个问题,我一直在想这个问题。下面是两段既合法又可编译的代码片段。具体来说,我的问题是。。在第二种情况下,括号是否会使程序变慢?为什么这是允许的 第一种情况:C# 不必要的花括号会降低性能吗?,c#,performance,optimization,syntax,C#,Performance,Optimization,Syntax,最近在编程时遇到了这个问题,我一直在想这个问题。下面是两段既合法又可编译的代码片段。具体来说,我的问题是。。在第二种情况下,括号是否会使程序变慢?为什么这是允许的 第一种情况: if (statement) { // do something } 第二种情况: { if (statement) { // do something } } 另外,如果我有下面的代码。。运行时是否与不使用任何花括号调用函数X相同 { { { //
if (statement)
{
// do something
}
第二种情况:
{
if (statement)
{
// do something
}
}
另外,如果我有下面的代码。。运行时是否与不使用任何花括号调用函数X相同
{
{
{
// call function X
}
}
}
没有花括号不会降低性能 它有助于理解TNAD代码并提供良好的代码格式。但有些大括号是必须的,比如函数开始/结束、循环开始/结束、条件开始/结束,这些大括号也有助于理解变量的顺序。简短的回答是“不,它们不会降低性能”
编译器需要大括号来确定变量的范围,并知道当前语句组的结束位置。一旦编译器完成处理,带有和不带不必要的大括号的代码将产生相同的输出
请注意,这与编译代码的性能有关,而与编译器本身的性能无关。编译器将花费额外的时间来编译代码,这仅仅是因为输入的原始大小更大。但是,为了让这段额外的时间变得可测量,不必要的大括号的数量必须非常多。它不会导致任何性能下降,但使用大括号肯定会增加代码的可读性。 在真实的世界场景中,当您进行对等代码审查或结对编程时,您编写的内容将非常清晰可读 通过以下链接
与以往的问题一样,答案在于它产生的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;
}
public Func X()
{
{
var i=1;
{
i++;
{
return()=>i;
}
}
}
}
公共职能
{
var i=1;
i++;
return()=>i;
}
>P>不同于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_uu1
从第一个带括号的部分开始编码,无论它在哪里使用i
,在第二个部分使用i_u2
进行编码。在某些情况下,在多个作用域块中声明具有相同名称和用途的变量可能比在外部作用域块中声明具有相同用途的一个变量生成的代码效率更低,但这几乎不会产生有意义的效果。在大多数情况下,即时编译器将能够确定代码中的多个变量可以安全地映射到同一存储位置,即使在无法使用的情况下,为几个额外变量所需的存储也不太可能对性能产生很大影响。大多数情况下,它不会产生任何影响,而且您应该明确地为可读性编写代码
然而,花括号可能会对性能产生影响,这是一种令人惊讶的方式,尽管这很不寻常。考虑这个代码:
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);
}
}
}
}
}
这里的区别是:
- 捕获类的实例在
FewerCurlies
中循环的每次迭代中创建,即使未使用它
- 在
FewerCurlies
中使用的捕获类的每个实例都包含两个变量,即使每个委托实际上只使用其中一个,而在moreccurlies
中,每个捕获类只捕获一个变量
这在某种程度上都是特定于实现的,但它表明看起来多余的花括号可能会产生影响。使用不必要的花括号,假设您没有嵌套变量,在将代码转换为字节码或机器码的过程中,只会在标签表中添加标签。因此,在更糟糕的情况下,构建时间会更慢。如果在嵌套中有变量,如果它们是没有销毁代码的原语,那么就不应该有问题,但是如果在嵌套的大括号中创建了对象,那么就需要对GC有更深入的理解,但我强烈怀疑是否会出现任何明显的差异。在所有情况下,由于编译器在构建项目时通过在查找表中存储引用来进行额外的工作,因此在构建项目时会有轻微的延迟(尽管可能不明显)。不,它们只是用于分组。这个问题在这里也是离题的。从技术上讲,它们是作用域,但我认为编译器足够聪明,可以删除不必要的作用域。在中添加额外的花括号不太可能使程序变慢,因为它们用于指定作用域。这个问题已经讨论过了。@deathismyfriend-似乎表明这个问题的主题是:“一个实用的、可回答的问题,这是软件开发所特有的。”另外,请参阅一个有趣的案例,其中额外的大括号可能会影响性能。@deathismyfriend这将作为存根代码在代码评审中脱离主题。如果OP希望代码审查,欢迎他们发布他们希望审查的完整代码。。
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;
}
}
}