【问题标题】:Optimizing Aggregate for String Concatenation [closed]优化字符串连接的聚合[关闭]
【发布时间】:2008-12-09 23:12:48
【问题描述】:

更新 - 对于那些喜欢开玩笑的人,您可以假设 Aggregate 仍然会产生正常的结果,无论传递给它的函数是什么,包括在被优化的情况下。

我编写了这个程序来构建一长串整数,从 0 到 19999,用逗号分隔。

using System;
using System.Linq;
using System.Diagnostics;

namespace ConsoleApplication5
{
    class Program
    {
        static void Main(string[] args)
        {
            const int size = 20000;

            Stopwatch stopwatch = new Stopwatch();

            stopwatch.Start();
            Enumerable.Range(0, size).Select(n => n.ToString()).Aggregate((a, b) => a + ", " + b);
            stopwatch.Stop();

            Console.WriteLine(stopwatch.ElapsedMilliseconds + "ms");
        }
    }
}

当我运行它时,它会说:

5116ms

超过五秒,太可怕了。当然这是因为每次循环都会复制整个字符串。

但是,如果根据评论做出一个非常小的更改呢?

using System;
using System.Linq;
using System.Diagnostics;

namespace ConsoleApplication5
{
    using MakeAggregateGoFaster;  // <---- inserted this

    class Program
    {
        static void Main(string[] args)
        {
            const int size = 20000;

            Stopwatch stopwatch = new Stopwatch();

            stopwatch.Start();
            Enumerable.Range(0, size).Select(n => n.ToString()).Aggregate((a, b) => a + ", " + b);
            stopwatch.Stop();

            Console.WriteLine(stopwatch.ElapsedMilliseconds + "ms");
        }
    }
}

现在当我运行它时,它会说:

42ms

快 100 倍以上。

问题

MakeAggregateGoFaster 命名空间中有什么?

更新 2: Wrote up my answer here.

【问题讨论】:

  • 你在问什么?您是否编写了 MakeAggregateGoFaster,这是一个难题?
  • 我搁置这个的原因是你给了我们一个“嘿,我是怎么让它更快的”而没有给我们代码的;造成这种情况,“我可以做十几种不同的事情来加快速度。”或者:“这是一个聪明的谜题,我希望你能解决它”——Stack Overflow 对这两件事都不好。

标签: c# optimization linq-to-objects


【解决方案1】:

为什么不使用其他形式的聚合?

Enumerable.Range(0, size ).Aggregate(new StringBuilder(),
        (a, b) => a.Append(", " + b.ToString()),
        (a) => a.Remove(0,2).ToString());

您可以为种子指定任何类型,在第一个 lambda 函数中执行任何需要的格式化或自定义调用,然后在第二个 lambda 函数中自定义输出类型。内置功能已经提供了您需要的灵活性。我的跑步从 1444 毫秒到 6 毫秒。

【讨论】:

  • 我不知道为什么,但我必须在您的示例中对 b 添加一个空检查才能使其正常工作:.Aggregate(new StringBuilder(), (a, b) =&gt; a.Append("\n" + (b == null ? "" : b.ToString())), (a) =&gt; a.Remove(0, 2).ToString());
【解决方案2】:

您正在使用命名空间中自己的扩展方法“覆盖” System.Linq.Aggregate 使AggregateGoFaster。

也许专门研究 IEnumerable&lt;string&gt; 并使用 StringBuilder?

也许使用Expression&lt;Func&lt;string, string, string&gt;&gt; 而不是Func&lt;string, string, string&gt;,这样它就可以分析表达式树并编译一些使用StringBuilder而不是直接调用函数的代码?

只是猜测。

【讨论】:

    【解决方案3】:

    没有回答问题,但我认为这里的标准模式是使用 StringBuilder 或 string.Join:

    string.Join(", ",Enumerable.Range(0, size).Select(n => n.ToString()).ToArray())
    

    【讨论】:

    • 我只是计时了,看起来 if (size > 1,000,000) 我的版本更快(当然添加了 using 语句)。
    • string.join 应该是 string.Join
    【解决方案4】:

    我之所以问它是否是一个谜题,是因为一个谜题可以在不同程度上牺牲稳健性,只要它满足所述问题的字母即可。考虑到这一点,这里是:

    解决方案 1(立即运行,问题未验证):

    public static string Aggregate(this IEnumerable<string> l, Func<string, string, string> f) {
         return "";
    }
    

    解决方案 2(运行速度与问题所需的速度差不多,但完全忽略委托):

    public static string Aggregate(this IEnumerable<string> l, Func<string, string, string> f) {
        StringBuilder sb = new StringBuilder();
        foreach (string item in l)
            sb.Append(", ").Append(item);
        return sb.Remove(0,2).ToString();
    }
    

    【讨论】:

      【解决方案5】:

      嗯,这完全取决于现在 MageAggregateGoFaster 命名空间中的代码是什么,不是吗?

      此命名空间不是 .NET 运行时的一部分,因此您已链接到一些自定义代码。

      我个人认为可以识别字符串连接或类似的东西,并建立一个列表或类似的东西,然后分配一个大的 StringBuilder 并使用 Append。

      一个肮脏的解决方案是:

      namespace MakeAggregateGoFaster
      {
          public static class Extensions
          {
              public static String Aggregate(this IEnumerable<String> source, Func<String, String, String> fn)
              {
                  StringBuilder sb = new StringBuilder();
                  foreach (String s in source)
                  {
                      if (sb.Length > 0)
                          sb.Append(", ");
                      sb.Append(s);
                  }
      
                  return sb.ToString();
              }
          }
      }
      

      很脏,因为这段代码在执行您所说的程序体验时,根本不使用函数委托。但是,它将在我的计算机上将执行时间从大约 2800 毫秒减少到 11 毫秒,并且仍然产生相同的结果。

      现在,下一次,也许你应该问一个真正的问题,而不是仅仅看看我有多聪明类型的捶胸顿足?

      【讨论】:

        猜你喜欢
        • 2010-09-29
        • 2020-02-29
        • 2011-06-12
        • 1970-01-01
        • 2017-06-10
        • 1970-01-01
        • 1970-01-01
        • 2018-06-08
        • 1970-01-01
        相关资源
        最近更新 更多