编辑发布.Net Core 2.1
重复测试.Net Core 2.1的发布,我得到了这样的结果
“Concat”的1000000次迭代耗时842ms。
1000000次“new String”迭代耗时1009ms。
“sb”的1000000次迭代耗时902ms。
简而言之,如果您使用.Net Core 2.1或更高版本,则Concat
为王。
我编辑了这个问题,以纳入评论中提出的有效点。
我正在考虑my answer to a previous question,我开始怀疑,是吗,
return new string(charSequence.ToArray());
将IEnumerable<char>
转换为string
的最佳方法。我做了一点搜索,发现这个问题已经问here。那个答案断言,
string.Concat(charSequence)
是一个更好的选择。在回答这个问题之后,还提出了StringBuilder
枚举方法,
var sb = new StringBuilder();
foreach (var c in chars)
{
sb.Append(c);
}
return sb.ToString();
虽然这可能有点笨拙我把它包括在内以保证完整性。我决定做一点测试,使用的代码在底部。
当在发布模式中构建时,通过优化,并在没有附加调试器的情况下从命令行运行,我得到这样的结果。
“Concat”的1000000次迭代耗时1597ms。
1000000次“new String”迭代花了869ms。
“sb”的1000000次迭代耗时748ms。
据我估算,new string(...ToArray())
接近string.Concat
方法的两倍。 StringBuilder
稍微快一些,但使用起来很尴尬,但可能是一个扩展。
我应该坚持new string(...ToArray())
还是,有什么我想念的吗?
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
class Program
{
private static void Main()
{
const int iterations = 1000000;
const string testData = "Some reasonably small test data";
TestFunc(
chars => new string(chars.ToArray()),
TrueEnumerable(testData),
10,
"new String");
TestFunc(
string.Concat,
TrueEnumerable(testData),
10,
"Concat");
TestFunc(
chars =>
{
var sb = new StringBuilder();
foreach (var c in chars)
{
sb.Append(c);
}
return sb.ToString();
},
TrueEnumerable(testData),
10,
"sb");
Console.WriteLine("----------------------------------------");
TestFunc(
string.Concat,
TrueEnumerable(testData),
iterations,
"Concat");
TestFunc(
chars => new string(chars.ToArray()),
TrueEnumerable(testData),
iterations,
"new String");
TestFunc(
chars =>
{
var sb = new StringBuilder();
foreach (var c in chars)
{
sb.Append(c);
}
return sb.ToString();
},
TrueEnumerable(testData),
iterations,
"sb");
Console.ReadKey();
}
private static TResult TestFunc<TData, TResult>(
Func<TData, TResult> func,
TData testData,
int iterations,
string stage)
{
var dummyResult = default(TResult);
var stopwatch = Stopwatch.StartNew();
for (var i = 0; i < iterations; i++)
{
dummyResult = func(testData);
}
stopwatch.Stop();
Console.WriteLine(
"{0} iterations of \"{2}\" took {1}ms.",
iterations,
stopwatch.ElapsedMilliseconds,
stage);
return dummyResult;
}
private static IEnumerable<T> TrueEnumerable<T>(IEnumerable<T> sequence)
{
foreach (var t in sequence)
{
yield return t;
}
}
}
答案 0 :(得分:6)
值得注意的是,这些结果虽然从纯粹主义者的观点来看属于IE的情况,但并不总是如此。例如,如果您实际上有一个char数组,即使您将它作为IEnumerable传递,调用字符串构造函数也会更快。
结果:
Sending String as IEnumerable<char>
10000 iterations of "new string" took 157ms.
10000 iterations of "sb inline" took 150ms.
10000 iterations of "string.Concat" took 237ms.
========================================
Sending char[] as IEnumerable<char>
10000 iterations of "new string" took 10ms.
10000 iterations of "sb inline" took 168ms.
10000 iterations of "string.Concat" took 273ms.
守则:
static void Main(string[] args)
{
TestCreation(10000, 1000);
Console.ReadLine();
}
private static void TestCreation(int iterations, int length)
{
char[] chars = GetChars(length).ToArray();
string str = new string(chars);
Console.WriteLine("Sending String as IEnumerable<char>");
TestCreateMethod(str, iterations);
Console.WriteLine("===========================================================");
Console.WriteLine("Sending char[] as IEnumerable<char>");
TestCreateMethod(chars, iterations);
Console.ReadKey();
}
private static void TestCreateMethod(IEnumerable<char> testData, int iterations)
{
TestFunc(chars => new string(chars.ToArray()), testData, iterations, "new string");
TestFunc(chars =>
{
var sb = new StringBuilder();
foreach (var c in chars)
{
sb.Append(c);
}
return sb.ToString();
}, testData, iterations, "sb inline");
TestFunc(string.Concat, testData, iterations, "string.Concat");
}
答案 1 :(得分:0)
好吧,我刚刚编写了一个小测试,尝试了3种不同的方法从IEnumerable创建一个字符串:
StringBuilder
并重复调用其Append(char ch)
方法。string.Concat<T>
String
构造函数。生成随机1,000个字符序列并从中构建字符串的10,000次迭代,我在发布版本中看到以下时间:
StringBuilder
明显的赢家。不过,我正在使用静态StringBuilder
(单例)实例。 Dunno,如果这有很大的不同。
这是源代码:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
namespace ConsoleApplication6
{
class Program
{
static readonly RandomNumberGenerator Random = RandomNumberGenerator.Create() ;
static readonly byte[] buffer = {0,0} ;
static char RandomChar()
{
ushort codepoint ;
do
{
Random.GetBytes(buffer) ;
codepoint = BitConverter.ToChar(buffer,0) ;
codepoint &= 0x007F ; // restrict to Unicode C0 ;
} while ( codepoint < 0x0020 ) ;
return (char) codepoint ;
}
static IEnumerable<char> GetRandomChars( int count )
{
if ( count < 0 ) throw new ArgumentOutOfRangeException("count") ;
while ( count-- >= 0 )
{
yield return RandomChar() ;
}
}
enum Style
{
StringBuilder = 1 ,
StringConcatFunction = 2 ,
StringConstructor = 3 ,
}
static readonly StringBuilder sb = new StringBuilder() ;
static string MakeString( Style style )
{
IEnumerable<char> chars = GetRandomChars(1000) ;
string instance ;
switch ( style )
{
case Style.StringConcatFunction :
instance = String.Concat<char>( chars ) ;
break ;
case Style.StringBuilder :
foreach ( char ch in chars )
{
sb.Append(ch) ;
}
instance = sb.ToString() ;
break ;
case Style.StringConstructor :
instance = new String( chars.ToArray() ) ;
break ;
default :
throw new InvalidOperationException() ;
}
return instance ;
}
static void Main( string[] args )
{
Stopwatch stopwatch = new Stopwatch() ;
foreach ( Style style in Enum.GetValues(typeof(Style)) )
{
stopwatch.Reset() ;
stopwatch.Start() ;
for ( int i = 0 ; i < 10000 ; ++i )
{
MakeString( Style.StringBuilder ) ;
}
stopwatch.Stop() ;
Console.WriteLine( "Style={0}, elapsed time is {1}" ,
style ,
stopwatch.Elapsed
) ;
}
return ;
}
}
}