Lee*_*Lee 264
.NET性能大师Rico Mariani有一篇关于这个主题的文章.它并不像人们怀疑的那么简单.基本建议如下:
如果您的模式如下:
x = f1(...) + f2(...) + f3(...) + f4(...)
这是一个concat,它很活泼,StringBuilder可能无济于事.
如果您的模式如下:
if (...) x += f1(...)
if (...) x += f2(...)
if (...) x += f3(...)
if (...) x += f4(...)
那么你可能想要StringBuilder.
另一篇支持这一主张的文章来自Eric Lippert,他在其中描述了+
以详细方式对一行连接执行的优化.
The*_*tan 148
该StringBuilder.Append()
方法比使用+运算符要好得多.但我发现,当执行1000个或更少的连接时,+
效率甚至更高String.Join()
.
StringBuilder sb = new StringBuilder();
sb.Append(someString);
Run Code Online (Sandbox Code Playgroud)
唯一的问题StringBuilder
是您必须使用公共分隔符连接字符串.(编辑:)正如@ryanversaw指出的那样,你可以制作分隔符string.Empty.
string key = String.Join("_", new String[]
{ "Customers_Contacts", customerID, database, SessionID });
Run Code Online (Sandbox Code Playgroud)
Mr_*_*een 78
有6种类型的字符串连接:
+
)符号.string.Concat()
.string.Join()
.string.Format()
.string.Append()
.StringBuilder
.在一个实验中,已经证明,string.Concat()
如果单词小于1000(大约)并且如果单词大于1000则StringBuilder
应该使用最好的方法.
有关更多信息,请查看此站点.
string.Join()vs string.Concat()
这里的string.Concat方法相当于带有空分隔符的string.Join方法调用.附加一个空字符串很快,但不这样做更快,所以string.Concat方法在这里会更好.
pal*_*rse 54
来自Chinh Do - StringBuilder并不总是更快:
经验法则
连接三个或更少的动态字符串值时,请使用传统的字符串连接.
连接三个以上的动态字符串值时,请使用StringBuilder.
从多个字符串文字构建一个大字符串时,请使用@ string literal或inline +运算符.
大多数时候StringBuilder是你最好的选择,但有一些案例如该帖子所示,你至少应该考虑每种情况.
这是我用于大型NLP应用程序十多年来发展最快的方法.我有IEnumerable<T>
和其他输入类型的变体,有和没有不同类型的分隔符(Char
,String
),但在这里我展示了将数组中的所有字符串连接成单个字符串,没有分隔符的简单情况 .这里的最新版本是在C#7和.NET 4.7上开发和单元测试的.
提高性能有两个关键因素; 首先是预先计算所需的确切总大小.当输入是如此处所示的数组时,此步骤是微不足道的.为了处理IEnumerable<T>
代替,这是值得第一收集该总弦到临时数组计算(该阵列需要避免调用ToString()
因为技术上比每个元素一次,给出的副作用的可能性,这样做可能会改变预期的语义一个'字符串连接'操作).
接下来,给定最终字符串的总分配大小,通过就地构建结果字符串可以获得性能的最大提升.这样做需要(可能是有争议的)暂停新的不变性的技术,该新String
的最初被分配为零.除此之外,还有任何此类争议......
...请注意,这是此页面上唯一的批量连接解决方案,它完全避免了构造函数的额外轮次分配和复制
String
.
完整代码:
/// <summary>
/// Concatenate the strings in 'rg', none of which may be null, into a single String.
/// </summary>
public static unsafe String StringJoin(this String[] rg)
{
int i;
if (rg == null || (i = rg.Length) == 0)
return String.Empty;
if (i == 1)
return rg[0];
String s, t;
int cch = 0;
do
cch += rg[--i].Length;
while (i > 0);
if (cch == 0)
return String.Empty;
i = rg.Length;
fixed (Char* _p = (s = new String(default(Char), cch)))
{
Char* pDst = _p + cch;
do
if ((t = rg[--i]).Length > 0)
fixed (Char* pSrc = t)
memcpy(pDst -= t.Length, pSrc, (UIntPtr)(t.Length << 1));
while (pDst > _p);
}
return s;
}
[DllImport("MSVCR120_CLR0400", CallingConvention = CallingConvention.Cdecl)]
static extern unsafe void* memcpy(void* dest, void* src, UIntPtr cb);
Run Code Online (Sandbox Code Playgroud)
我应该提一下,这段代码与我自己使用的内容略有不同.在原文中,我从C#调用cpblk IL指令进行实际复制.为了简化和在这里的代码的可移植性,我用P/Invoke 代替了,正如你所看到的.为了在x64上获得最高性能(但可能不是x86),您可能需要使用cpblk方法.memcpy
小智 6
尝试这 2 段代码,您就会找到解决方案。
static void Main(string[] args)
{
StringBuilder s = new StringBuilder();
for (int i = 0; i < 10000000; i++)
{
s.Append( i.ToString());
}
Console.Write("End");
Console.Read();
}
Run Code Online (Sandbox Code Playgroud)
VS
static void Main(string[] args)
{
string s = "";
for (int i = 0; i < 10000000; i++)
{
s += i.ToString();
}
Console.Write("End");
Console.Read();
}
Run Code Online (Sandbox Code Playgroud)
你会发现第一个代码会很快结束,并且内存会很大。
第二个代码也许内存会没问题,但需要更长的时间……更长的时间。因此,如果您的应用程序面向大量用户并且需要速度,请使用第一个。如果您有一个短期的单用户应用程序,也许您可以同时使用两者,或者第二个对于开发人员来说会更“自然”。
干杯。
添加到其他答案,请记住,StringBuilder可以被告知要分配的初始内存量.
所述容量参数定义可被存储在由当前实例所分配的存储器的字符的最大数目.其值分配给Capacity属性.如果要存储在当前实例中的字符数超过此容量值,则StringBuilder对象会分配额外的内存来存储它们.
如果capacity为零,则使用特定于实现的默认容量.
重复附加到尚未预先分配的StringBuilder可能会导致大量不必要的分配,就像重复连接常规字符串一样.
如果您知道最后一个字符串将持续多长时间,可以简单地计算它,或者可以对常见情况进行有根据的猜测(分配太多不一定是坏事),您应该将这些信息提供给构造函数或者容量财产.特别是在运行性能测试时,将StringBuilder与其他方法(如String.Concat)进行比较,这些方法在内部执行相同的操作.您在网上看到的任何测试都没有包含StringBuilder在其比较中的预分配是错误的.
如果你不能对大小做任何猜测,你可能正在编写一个实用函数,它应该有自己的可选参数来控制预分配.
小智 5
以下可能是连接多个字符串的另一种替代解决方案。
String str1 = "sometext";
string str2 = "some other text";
string afterConcate = $"{str1}{str2}";
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
195525 次 |
最近记录: |