【问题标题】:if, else if vs. mapping performanceif, else if 与映射性能
【发布时间】:2013-09-24 15:03:29
【问题描述】:

我有一个性能问题。

我正在解析大型文本文件(账单)并根据账单上是否出现某些文本将服务提供商的名称分配给一个变量。

这是我正在做的一个小例子(别笑,我知道这很乱)。总而言之,大约有 250 个 if,else 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, else if 慢得多。我使用秒表方法发现,更清洁的方法实际上比数百个 if 语句慢两倍。 (我测试了 if 语句开头和结尾的账单,并得到了类似的结果)

谁能给我解释一下?也许我做错了什么?谢谢!

【问题讨论】:

  • 慢两倍是多少? 4秒而不是2秒?多少是不可接受的?
  • 另外,哪一部分比较慢?您的清洁方法不止做一件事 - 每张账单的循环速度较慢,还是只是从文件中读取映射会减慢速度? (我假设您没有为每张账单阅读您的提供商地图,对吧?)
  • 你有很多变量,包括两个完全独立的任务。解决方案的哪个部分较慢? Read 还是 Loop?此外,这里可能没有考虑到的东西——可维护性、250+ IfElse,甚至 Switch,如果你要不断地构建它,可能会成为一场噩梦。这些是我的想法;但如果没有更多信息,我帮不了你。
  • 映射的初始读取只发生一次,大约需要 35 毫秒。在 Foreach 中分配提供者是较慢的部分。它大约需要 2 毫秒,而旧的 if else 方法大约需要 0.75 毫秒。我知道差异很小,不会对我的程序产生太大影响。我只是想知道是否有人知道为什么需要两倍的时间。
  • 循环展开是一种常见的优化技术。你做了相反的事情,所以我并不惊讶它变慢了。

标签: c# performance if-statement mapping


【解决方案1】:

读取外部文件肯定会减慢您的速度。我假设你只读过一次,但它仍然会减慢你的速度。

1) 您是否尝试过缓存文件的内容以便无需再次阅读?如果映射没有太大变化,这可能是一种改进。

2) 另外,为什么不尝试将映射存储在 Dictionary 对象(或类似结构)中?如果你愿意,你可以把它放在一个单独的类文件中。这是一个硬编码的解决方案,不确定它在您的系统中的表现如何。

【讨论】:

  • 映射的初始读取只发生一次,大约需要 35 毫秒。在 Foreach 中分配提供者是较慢的部分。它大约需要 2 毫秒,而旧的 if else 方法大约需要 0.75 毫秒。我知道差异很小,不会对我的程序产生太大影响。我只是想知道是否有人知道为什么需要两倍的时间。
  • 另外,我将映射保存到公共通用列表变量。它有自己的类,带有 get set 运算符。字典会比通用列表更快吗?
  • Colton,我相信 Linq 函数也在幕后进行循环。考虑到这一点,如果映射位于哈希表中会怎样……我的假设是查找会比遍历通用 List 对象更快。
【解决方案2】:

循环展开是一种通过将循环转换为语句序列来提高性能的技术。一个简单的例子

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;
    }
}

所以你可以看到你走了相反的路:从语句序列到循环。

我认为您已经获得了一些您没有提到的东西,即无需重新编译代码即可添加提供程序的能力,这很方便。

【讨论】:

  • 非常有趣。我认为任何事情都会比数百个评估性 IF 语句更快。
  • @ColtonMilne 想一想,您正在做与 ifs 相同的工作,外加一堆工作来跟踪您的循环状态。也就是说,我很惊讶它有很大的影响。如果您有大文本文件,我希望缓慢的部分是读取文件并搜索它们。
  • 谢谢迈克。这就是我一直在寻找的。​​span>
【解决方案3】:

我会选择第三个选项,

1- 更易读

2-less 代码,也可以是性能+1。

3-更容易维护

4- dll 内没有字符串,这也减少了 dll 的大小

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-17
    • 2016-08-07
    • 2021-03-15
    • 2021-02-27
    • 2017-05-06
    • 1970-01-01
    相关资源
    最近更新 更多