我有一个性能问题.
我正在解析大型文本文件(票据),并根据帐单上是否显示某些文本,将服务提供商的名称分配给变量.
这只是我正在做的一小部分(不要笑,我知道那很乱).总共大约有250个if,否则为if.
if (txtvar.BillText.IndexOf("SWGAS.COM") > -1)
{
txtvar.Provider = "Southwest Gas";
}
else if (txtvar.BillText.IndexOf("georgiapower.com") > -1)
{
txtvar.Provider = "Georgia Power";
}
else if (txtvar.BillText.IndexOf("City of Austin") > -1)
{
txtvar.Provider = "City of Austin";
}
//依此类推250次
因为它变得如此之大,所以我决定采用另一种方法来更清洁,更高效.我最终实现了一个映射,并将其存储在外部.psv文件中.
我将该映射保存到一个变量中(该变量只运行一次,大约需要35毫秒…
var providerMap =
System.IO.File.ReadLines(@"U:\Program\ApplicationFiles\ProvidersList.psv")
.Select(line => line.Split('|'))
.Select(parts => new Provider() { Pattern = parts[0], Name = parts[1] }).ToList();
…然后遍历每个账单(分配提供者大约需要2毫秒,而if语句花费的时间不到一半.
foreach (string bills in files)
{
string Provider = providerMap.First(p => txtvar.BillText.IndexOf(p.Pattern) > -1).Name;
OtherStuff();
}
尽管此解决方案更加简洁,但令人惊讶的是,如果不是,则比250慢得多.我使用秒表方法发现,更清洁的方法实际上是数百条if语句速度的两倍. (我测试了在if语句的开头和结尾处的帐单,并以相似的结果进行映射)
有人可以向我解释吗?也许我做错了什么?谢谢!
解决方法:
循环展开是一种通过将循环转换为语句序列来提高性能的技术.一个简单的例子
for(int i = 0; i < 3; i++)
{
Console.WriteLine(i);
}
可以展开到
Console.WriteLine(0);
Console.WriteLine(1);
Console.WriteLine(2);
有各种各样复杂的技术可以执行此操作,但要点是要减少机器码中循环变量,条件的求值和跳转指令的增量数量.请注意,此技术并不总是可以降低性能.有关更多讨论和示例,请参见loop unwinding.
你走了另一条路.您已经使用了很长的if-else构造并将其转换为
string Provider = providerMap.First(p => txtvar.BillText.IndexOf(p.Pattern) > -1).Name;
现在,First所做的实际上就是这样做(请注意,如果没有项目匹配,则First会抛出该错误):
Provider found = null;
foreach(var provider in providerMap)
{
if (txtvar.BillText.IndexOf(provider.Pattern) > -1)
{
found = provider;
break;
}
}
因此,您可以看到相反的方式:从一系列语句到循环.
我认为您已经获得了一些您没有提到的功能,即无需重新编译代码即可添加提供程序的功能,这很方便.