在SO上看到一个问题,关于加入字符串我已经做了一些测试,并且知道在foreach中加入字符串比使用for循环并使用数组中的索引要慢。由于绑定检查数组,for循环不应该更慢吗? (对foreach上不存在的字符串[i]的绑定检查)。
我不明白的另一件事是列表中的string.Join()慢度......
编辑:将错误和更新的源更新为最终源(删除最后一个“,”)
以下是测试结果:
DEBUG: AMD PHENOM II X4 3GHZ StringBuilder foreach System.Action Time: 4077ms (12025926) StringBuilder for System.Action Time: 4032ms (11895082) String.Join System.Action Time: 5338ms (15744918) INTEL XEON W3503 @ 2.4GHZ / 12GB DDR3 StringBuilder foreach System.Action Time: 4661ms (10926950) StringBuilder for System.Action Time: 4202ms (9849590) String.Join System.Action Time: 6466ms (15156149) RELEASE: AMD PHENOM II X4 3GHZ StringBuilder foreach System.Action Time: 3897ms (11496978) StringBuilder for System.Action Time: 3719ms (10970899) String.Join System.Action Time: 5307ms (15655162) INTEL XEON W3503 @ 2.4GHZ / 12GB DDR3 StringBuilder foreach System.Action Time: 4533ms (10625128) StringBuilder for System.Action Time: 4168ms (9770765) String.Join System.Action Time: 7173ms (16813036) (why in the world xeon slower than in debug with string.join?) FOR A GOOD LAUGH LOOK AT THE END.
以下是来源:
public static void Main(string[] Args)
{
List<string> strings = new List<string>() {};
for (double d = 0; d < 12000; d++) {
strings.Add(d.ToString());
}
GC.Collect();
GC.WaitForPendingFinalizers();
Performance(() =>
{
StringBuilder sb = new StringBuilder();
foreach (string s in strings)
{
sb.Append(s);
sb.Append(",");
}
sb.Remove(sb.Length - 1, 1);
}, "StringBuilder foreach");
GC.Collect();
GC.WaitForPendingFinalizers();
Performance(() =>
{
StringBuilder sb = new StringBuilder();
int max = strings.Count-1;
int i;
for (i = 0; i < max; i++)
{
sb.Append(strings[i]);
sb.Append(",");
}
sb.Append(strings[i]);
}, "StringBuilder for");
GC.Collect();
GC.WaitForPendingFinalizers();
Performance(() =>
{
string s = string.Join(",", strings);
}, "String.Join");
}
public static void Performance(Action fn, string prefix)
{
var timer = new Stopwatch();
timer.Start();
for (var i = 0; i < 10000; ++i)
{
fn();
}
timer.Stop();
Console.WriteLine("{0} {1} Time: {2}ms ({3})", prefix, fn.ToString(), timer.ElapsedMilliseconds, timer.ElapsedTicks);
}
字符串是否像foreach中的值类型一样被复制?由于速度几乎相同......
编辑:
澄清为什么int max = strings.Count-1;
可能是与人们所说的相反的优化(并且测试证明是这样):
我们不是在处理数组,而且集合来自外部作用域到迭代它的方法。如果它是在for循环中的strings.Length,那可能会改变(就像另一个线程更改集合)..但这不是原因,原因是我们正在读取变量而不是调用方法(属性获取)和它只提供5%的性能。这不是绑定检查的编译时优化,因为没有人能提前知道“最大”值。它取决于每次调用方法时字符串的内容。
EDIT2:
在发布时测试中有一个更大的字符串,但数量相同,请大笑String.Join():
List<string> strings = new List<string>() {};
for (double d = 0; d < 12000; d++) {
strings.Add("ikugluglizuglkuhiugpiugiugholiugholiughpiuhziuhzuiugloiu" + d.ToString());
}
// AMD PHENOM:
// StringBuilder foreach System.Action Time: 10080ms (29732687)
// StringBuilder for System.Action Time: 9659ms (28490593)
// String.Join System.Action Time: 24509ms (72292291)
// INTEL XEON:
// StringBuilder foreach System.Action Time: 9790ms (22947294)
// StringBuilder for System.Action Time: 9140ms (21425490)
// String.Join System.Action Time: 21114ms (49490839)
它可能适用于数组,但是在集合中String.Join粗暴地吮吸,对于大字符串更是如此!
如果您想比较,仅供参考:
Windows 7 64bit
CPU Type QuadCore AMD Phenom II X4 945
CPU Clock 3000 MHz
L3 Cache 6 MB (On-Die, ECC, NB-Speed)
North Bridge Clock 2010.8 MHz
Memory 8190 MB
Memory Bus 804.3 MHz DDR3-1600
Motherboard Chipset AMD 790X, AMD K10
Memory Timings 8-9-9-24 (CL-RCD-RP-RAS)
Command Rate (CR) 1T
答案 0 :(得分:2)
由于对数组的绑定检查,for循环不应该更慢吗?
不,CLR可以将其优化为1以检查它是否可以验证边界。
int max = strings.Count - 1;
糟糕的优化。在FX 1.1中,它会花费你。 (这也是不正确的。)
foreach
必须做更多的工作(通过Eumerator)。注意差异很小。
答案 1 :(得分:1)
查看ILSpy中的IL代码。
foreach循环有一个隐式的尝试..最后,for循环没有。
在foreach中也有对MoveNext的隐式调用。 MoveNext有很多开销,包括在最终对列表进行索引操作以获取条目之前的版本更改检查。
理论上foreach可以更快,但它显然比裸循环做更多的工作。
这是在vs2010之下。不确定其他版本如何处理这个问题。