【发布时间】:2011-01-28 21:03:31
【问题描述】:
今天我打开了一个 Microsoft 的 .Net 正则表达式实现,我突然想到这可能是在 C# 之类的语言中存在 goto 语句的唯一原因(它主要强调“开发人员的概念,其中 goto 似乎根本没有位置),并且可能也是 Microsoft 使用它的 CLR 库实现的唯一原因 - 以提高性能。我还记得在 System.Web.UI.dll 中的 Microsoft 的 ASP.NET 页面/控件呈现中也看到了类似的优化。这会是一个有效的(或记录在案的?)假设吗?除了提高代码执行时间之外,您是否见过 Microsoft 在任何其他情况下使用它?谢谢。
以下是微软自己的正则表达式实现的摘录(不打算详细检查,只是扫描一下),他们再次广泛使用 goto 似乎只是为了提高性能:
public override void Go()
{
int num4;
int num5;
string runtext = base.runtext;
int runtextstart = base.runtextstart;
int runtextbeg = base.runtextbeg;
int runtextend = base.runtextend;
int runtextpos = base.runtextpos;
int[] runtrack = base.runtrack;
int runtrackpos = base.runtrackpos;
int[] runstack = base.runstack;
int runstackpos = base.runstackpos;
runtrack[--runtrackpos] = runtextpos;
runtrack[--runtrackpos] = 0;
runstack[--runstackpos] = runtextpos;
runtrack[--runtrackpos] = 1;
if ((((runtextpos != base.runtextstart) ||
(4 > (runtextend - runtextpos))) ||
((runtext[runtextpos] != '<') ||
(runtext[runtextpos + 1] != '%'))) ||
((runtext[runtextpos + 2] != '-') ||
(runtext[runtextpos + 3] != '-')))
{
goto Label_02F8;
}
runtextpos += 4;
runstack[--runstackpos] = -1;
runtrack[--runtrackpos] = 1;
goto Label_0213;
Label_0161:
if (num5 > num4)
{
runtrack[--runtrackpos] = (num5 - num4) - 1;
runtrack[--runtrackpos] = runtextpos - 1;
runtrack[--runtrackpos] = 2;
}
Label_0194:
num4 = runstack[runstackpos++];
this.Capture(2, num4, runtextpos);
runtrack[--runtrackpos] = num4;
runtrack[--runtrackpos] = 3;
if (runtextpos >= runtextend)
{
goto Label_02F8;
}
runtextpos++;
if (runtext[runtextpos] != '-')
{
goto Label_02F8;
}
num4 = runstack[runstackpos++];
this.Capture(1, num4, runtextpos);
runtrack[--runtrackpos] = num4;
runtrack[--runtrackpos] = 3;
Label_0213:
if (num4 != -1)
{
runtrack[--runtrackpos] = num4;
}
else
{
runtrack[--runtrackpos] = runtextpos;
}
if ((num4 = runstack[runstackpos++]) != runtextpos)
{
runtrack[--runtrackpos] = runtextpos;
runtrack[--runtrackpos] = 4;
}
else
{
runstack[--runstackpos] = num4;
runtrack[--runtrackpos] = 5;
}
if (((3 > (runtextend - runtextpos)) ||
(runtext[runtextpos] != '-')) ||
((runtext[runtextpos + 1] != '%') ||
(runtext[runtextpos + 2] != '>')))
{
goto Label_02F8;
}
runtextpos += 3;
num4 = runstack[runstackpos++];
this.Capture(0, num4, runtextpos);
runtrack[--runtrackpos] = num4;
runtrack[--runtrackpos] = 3;
Label_02EF:
base.runtextpos = runtextpos;
return;
Label_02F8:
base.runtrackpos = runtrackpos;
base.runstackpos = runstackpos;
this.EnsureStorage();
runtrackpos = base.runtrackpos;
runstackpos = base.runstackpos;
runtrack = base.runtrack;
runstack = base.runstack;
switch (runtrack[runtrackpos++])
{
case 1:
runstackpos++;
goto Label_02F8;
case 2:
runtextpos = runtrack[runtrackpos++];
num4 = runtrack[runtrackpos++];
if (num4 > 0)
{
runtrack[--runtrackpos] = num4 - 1;
runtrack[--runtrackpos] = runtextpos - 1;
runtrack[--runtrackpos] = 2;
}
goto Label_0194;
case 3:
runstack[--runstackpos] = runtrack[runtrackpos++];
this.Uncapture();
goto Label_02F8;
case 4:
runtextpos = runtrack[runtrackpos++];
runstack[--runstackpos] = runtextpos;
runtrack[--runtrackpos] = 5;
if ((runtrackpos > 40) && (runstackpos > 30))
{
runstack[--runstackpos] = runtextpos;
runtrack[--runtrackpos] = 1;
runstack[--runstackpos] = runtextpos;
runtrack[--runtrackpos] = 1;
num4 = (num5 = runtextend - runtextpos) + 1;
do
{
if (--num4 <= 0)
{
goto Label_0161;
}
runtextpos++;
}
while (runtext[runtextpos] != '-');
runtextpos--;
goto Label_0161;
}
runtrack[--runtrackpos] = 6;
goto Label_02F8;
case 5:
runstack[runstackpos] = runtrack[runtrackpos++];
goto Label_02F8;
}
runtextpos = runtrack[runtrackpos++];
goto Label_02EF;
}
【问题讨论】:
-
根据标签,这绝对是反编译的IL。反编译器可能很难从 IL 重构高级控制流块,并且经常使用 goto 来表示 IL 级分支指令(这通常是控制流块的结果)。所以你的答案是原始代码和这个反编译代码的性能将是相同的,因为它们具有相同的 IL 表示。
-
我认为大多数 .net 库的源代码都可以下载(使用相当严格的 MS 参考许可证)。您可以在那里查看原始构造的外观。
-
此外,为了清晰起见,goto 的存在已得到充分验证。例如,考虑到您不能将
break排除在多层嵌套循环之外。goto可能是你唯一的选择,除非你重写你的循环,让它读起来完全是废话,以换取避免使用goto。性能参数实际上很弱。 -
明确地说,这个是生成的代码,对吧?不是 RegEx 类的一部分,而是来自相关类生成的“已编译”RegEx。
-
感谢 cdhowie 的解释。但是在 imo 中,如果你不能打破嵌套循环,那么你的设计就有问题,超过 2-3 层的嵌套循环在所有情况下都非常糟糕,在这种情况下,人们应该明确地重新评估“大局” ' 然后回来重写代码。
标签: c# .net performance goto