C# 为字符串连接优化聚合
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/354587/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Optimizing Aggregate for String Concatenation
提问by Daniel Earwicker
Update- for those of a facetious frame of mind, you can assume that Aggregate still produces the normal result whatever function is passed to it, including in the case being optimized.
更新- 对于那些喜欢开玩笑的人,您可以假设无论传递给它的函数是什么,Aggregate 仍然会产生正常的结果,包括在被优化的情况下。
I wrote this program to build a long string of integers from 0 to 19999 separate by commas.
我编写了这个程序来构建一个从 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");
}
}
}
When I run it, it says:
当我运行它时,它说:
5116ms
Over five seconds, terrible. Of course it's because the whole string is being copied each time around the loop.
超过五秒,太可怕了。当然,这是因为每次循环都会复制整个字符串。
But what if make one very small change indicated by the comment?
但是,如果对评论指示进行一个非常小的更改怎么办?
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");
}
}
}
Now when I run it, it says:
现在当我运行它时,它说:
42ms
Over 100x faster.
快 100 倍以上。
Question
题
What's in the MakeAggregateGoFaster namespace?
MakeAggregateGoFaster 命名空间中有什么?
Update 2:Wrote up my answer here.
更新 2:在这里写下我的答案。
采纳答案by mackenir
You are 'overriding' System.Linq.Aggregate with your own extension method in namespace MakeAggregateGoFaster.
您正在使用命名空间 MakeAggregateGoFaster 中自己的扩展方法“覆盖” System.Linq.Aggregate。
Perhaps specialised on IEnumerable<string>
and making use of a StringBuilder?
也许专门研究IEnumerable<string>
并使用 StringBuilder?
Maybe taking an Expression<Func<string, string, string>>
instead of a Func<string, string, string>
so it can analyse the expression tree and compile some code that uses StringBuilder instead of calling the function directly?
也许使用 aExpression<Func<string, string, string>>
代替 aFunc<string, string, string>
以便它可以分析表达式树并编译一些使用 StringBuilder 而不是直接调用函数的代码?
Just guessing.
只是猜测。
回答by Lasse V. Karlsen
Well, that would depend entirely on what code is in the MageAggregateGoFaster namespace now wouldn't it?
那么,这将完全取决于 MageAggregateGoFaster 命名空间中的代码现在不是吗?
This namespace is not part of the .NET runtime, so you've linked in some custom code.
此命名空间不是 .NET 运行时的一部分,因此您已链接到一些自定义代码中。
Personally I would think that something that recognizes string concatenation or similar, and builds up a list, or similar, then allocates one big StringBuilder and uses Append.
就我个人而言,我认为识别字符串连接或类似的东西,并建立一个列表或类似的东西,然后分配一个大的 StringBuilder 并使用 Append。
A dirty solution would be:
一个肮脏的解决方案是:
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();
}
}
}
dirty because this code, while doing what you say you experience with your program, does not use the function delegate at all. It will, however, bring down the execution time from around 2800ms to 11ms on my computer, and still produce the same results.
脏是因为这段代码在执行您所说的程序体验时,根本不使用函数委托。但是,它将在我的计算机上将执行时间从大约 2800 毫秒减少到 11 毫秒,并且仍然产生相同的结果。
Now, next time, perhaps you should ask a real question instead of just look how clever I amtype of chest-beating?
现在,下一次,也许你应该问一个真正的问题,而不是仅仅看我是多么聪明,是那种胸有成竹的人吗?
回答by Jimmy
Not answering the question, but I think the standard patterns here are to use StringBuilder or string.Join:
没有回答这个问题,但我认为这里的标准模式是使用 StringBuilder 或 string.Join:
string.join(", ",Enumerable.Range(0, size).Select(n => n.ToString()).ToArray())
回答by Jimmy
The reason I asked if it was a puzzle was because a puzzle is allowed to sacrifice robustness in varying degrees as long as it satisfies the letter of the stated problem. With that in mind, here goes:
我问它是否是拼图的原因是因为拼图可以在不同程度上牺牲鲁棒性,只要它满足所述问题的字母即可。考虑到这一点,这里是:
Solution 1 (runs instantly, problem doesn't validate):
解决方案 1(立即运行,问题未验证):
public static string Aggregate(this IEnumerable<string> l, Func<string, string, string> f) {
return "";
}
Solution 2 (runs about as fast as the problem requires, but ignores the delegate completely):
解决方案 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();
}
回答by Jimmy
Why not use one of the other forms of Aggregate?
为什么不使用其他形式的聚合之一?
Enumerable.Range(0, size ).Aggregate(new StringBuilder(),
(a, b) => a.Append(", " + b.ToString()),
(a) => a.Remove(0,2).ToString());
You can specify any type for your seed, perform whatever formatting or custom calls are needed in the first lambda function and then customize the output type in the second lambda function. The built in features already provide the flexibility you need. My runs went from 1444ms to 6ms.
您可以为种子指定任何类型,在第一个 lambda 函数中执行所需的任何格式设置或自定义调用,然后在第二个 lambda 函数中自定义输出类型。内置功能已经提供了您需要的灵活性。我的运行时间从 1444 毫秒变为 6 毫秒。