拆分一班轮会导致更多的开销吗?

时间:2018-09-22 11:19:08

标签: c#

与将语句分为多行相比,我正在尝试弄清人们对单行的感觉。

例如,此C#代码:

var output = Regex.Match(key, @"\[(.*?)\]").Groups[1].Value;

可以很容易地这样写:

var regex = @"\[(.*?)\]";
var matches = Regex.Match(input, regex);
var output = matches.Groups[1].Value;

第二个代码段是否有额外的开销?还是编译器最终将两个代码片段最终转换为相同的代码?

很显然,第二个片段更容易逐步调试,但也让我感到烦恼的是,对于一些相对简单的代码,它占用了3行。

2 个答案:

答案 0 :(得分:4)

这取决于您是否启用了优化功能(即调试或发布版本)。

在未优化的版本中,编译器将尝试保留尽可能多的局部变量(通常是方法中的变量,例如regexmatches,但如果需要,编译器可以添加更多的临时存储空间),以简化操作调试。优化器(用于发布版本)将尽可能地优化那些优化器。这就是为什么在调试优化的程序集时无法检查某些变量的原因。

让我们使用online C# compiler将代码转换为IL。

单线调试:

.locals init (
    [0] string
)

IL_0000: nop
IL_0001: ldarg.1
IL_0002: ldstr "\\[(.*?)\\]"
IL_0007: call class [System]System.Text.RegularExpressions.Match [System]System.Text.RegularExpressions.Regex::Match(string, string)
IL_000c: callvirt instance class [System]System.Text.RegularExpressions.GroupCollection [System]System.Text.RegularExpressions.Match::get_Groups()
IL_0011: ldc.i4.1
IL_0012: callvirt instance class [System]System.Text.RegularExpressions.Group [System]System.Text.RegularExpressions.GroupCollection::get_Item(int32)
IL_0017: callvirt instance string [System]System.Text.RegularExpressions.Capture::get_Value()
IL_001c: stloc.0
IL_001d: ret

单线发布:

IL_0000: ldarg.1
IL_0001: ldstr "\\[(.*?)\\]"
IL_0006: call class [System]System.Text.RegularExpressions.Match [System]System.Text.RegularExpressions.Regex::Match(string, string)
IL_000b: callvirt instance class [System]System.Text.RegularExpressions.GroupCollection [System]System.Text.RegularExpressions.Match::get_Groups()
IL_0010: ldc.i4.1
IL_0011: callvirt instance class [System]System.Text.RegularExpressions.Group [System]System.Text.RegularExpressions.GroupCollection::get_Item(int32)
IL_0016: callvirt instance string [System]System.Text.RegularExpressions.Capture::get_Value()
IL_001b: pop
IL_001c: ret

长方法,调试:

.locals init (
    [0] string,
    [1] class [System]System.Text.RegularExpressions.Match,
    [2] string
)

IL_0000: nop
IL_0001: ldstr "\\[(.*?)\\]"
IL_0006: stloc.0
IL_0007: ldarg.1
IL_0008: ldloc.0
IL_0009: call class [System]System.Text.RegularExpressions.Match [System]System.Text.RegularExpressions.Regex::Match(string, string)
IL_000e: stloc.1
IL_000f: ldloc.1
IL_0010: callvirt instance class [System]System.Text.RegularExpressions.GroupCollection [System]System.Text.RegularExpressions.Match::get_Groups()
IL_0015: ldc.i4.1
IL_0016: callvirt instance class [System]System.Text.RegularExpressions.Group [System]System.Text.RegularExpressions.GroupCollection::get_Item(int32)
IL_001b: callvirt instance string [System]System.Text.RegularExpressions.Capture::get_Value()
IL_0020: stloc.2
IL_0021: ret

长方法,发布:

.locals init (
    [0] string
)

IL_0000: ldstr "\\[(.*?)\\]"
IL_0005: stloc.0
IL_0006: ldarg.1
IL_0007: ldloc.0
IL_0008: call class [System]System.Text.RegularExpressions.Match [System]System.Text.RegularExpressions.Regex::Match(string, string)
IL_000d: callvirt instance class [System]System.Text.RegularExpressions.GroupCollection [System]System.Text.RegularExpressions.Match::get_Groups()
IL_0012: ldc.i4.1
IL_0013: callvirt instance class [System]System.Text.RegularExpressions.Group [System]System.Text.RegularExpressions.GroupCollection::get_Item(int32)
IL_0018: callvirt instance string [System]System.Text.RegularExpressions.Capture::get_Value()
IL_001d: pop
IL_001e: ret

在Release版本中,代码几乎相同。编译器仅为较长的方法引入一个本地string,其中包含正则表达式。单线没有本地人。 编译器没有为任何一个函数的返回值生成本地。

在Debug版本中,单行只有一个本地来保存返回值。较长的方法对所有三个变量都具有局部变量。

不过,这只是生成的IL。运行时/ JIT编译器将执行更多优化。您很可能通过手动排队本地人而不会获得任何速度优势。 而且在这种情况下,大多数Regex操作可能比在堆栈中存储两个本地变量要贵几个数量级,因此请选择任何您喜欢的样式。

答案 1 :(得分:0)

就个人而言,分开陈述是个人选择的问题。这样的语句:

public static void main()
{
    Console.WriteLine(“hello world”);
}

由编译器解释为与(白线,空格忽略)

public static void main()
{ Console.WriteLine(“hello world”); }

显然,第一个更具可读性。

无论对象是一行还是不同,对象仍将存储在堆中。

随着机器越来越快,可读,可测试,可维护的代码比轻微的性能成本更为重要。

这更具可读性;

if (i == j ||
    j == k ||
    l == m)
{

}

类似于:

 var result = list
               .Where(x => x == some Variable)
               .Skip(5)
               .ToList();

有些准则指出方法不应超过7行。

简而言之,我认为可调试代码更为重要。