添加到预先分配的列表(这是Clear
给你的)比随着项目的添加而增加列表要快得多。有关演示这一点的代码,请参见下面的第二个示例。
我不知道你的Coord
类和结构是什么样的,所以我拼凑了一些。我还修改了您的程序,以将创建结构/类所花费的时间与将其添加到列表所花费的时间分开。这是输出。第一个数字是运行测试所花费的总时间。第二个数字是创建类和结构所花费的时间(不包括将它们添加到列表中所花费的时间):
Classes: 1404 ms (922.253)
Structs: 803 ms (215.9278)
===================
Classes: 1231 ms (895.7751)
Structs: 520 ms (215.7464)
===================
Classes: 1251 ms (911.6303)
Structs: 523 ms (220.119)
===================
Classes: 1337 ms (990.2042)
Structs: 519 ms (215.3085)
===================
Classes: 1251 ms (909.4082)
Structs: 521 ms (215.2579)
===================
Classes: 1237 ms (894.4974)
Structs: 522 ms (216.5798)
===================
Classes: 1289 ms (947.2457)
Structs: 525 ms (217.9129)
===================
Classes: 1226 ms (887.7574)
Structs: 520 ms (214.7768)
===================
此测试在 .NET 4.5、64 位、发布模式下运行,调试器已分离。
当然,由于 JIT 时间,第一次迭代是异常的。以第 3 次迭代为例,它非常具有代表性。课程花费了 1,251 毫秒,其中 911 毫秒是创建时间。剩下 340 毫秒用于添加和开销。
结构体耗时 523 毫秒,其中 215 毫秒是创建时间。剩下 308 毫秒用于添加和开销。称之为洗头。
您所看到的是创建类(必须在堆上分配并将对它的引用复制到列表)与在堆栈上创建结构并将该非常小的结构复制到列表的内部数组中的区别。
我的测试没有说明第一次和第二次迭代之间的差异有多少是 JIT 时间,有多少是列表重新分配。您必须对添加进行计时(就像我对创建所做的那样)才能看到差异。
不过请理解,我们说的是 1000 万次迭代的 700 毫秒差异。您必须创建很多这样的东西才能在任何非平凡程序的运行时产生任何真正的差异。
代码如下。
private struct CoordStruct
{
public readonly int X;
public readonly int Y;
public CoordStruct(int x, int y)
{
X = x;
Y = y;
}
}
private class CoordClass
{
public readonly int X;
public readonly int Y;
public CoordClass(int x, int y)
{
X = x;
Y = y;
}
}
private void DoStuff()
{
const int Iterations = 10000000;
var classes = new List<CoordClass>();
var structs = new List<CoordStruct>();
var sw = new Stopwatch();
while (true)
{
TimeSpan createTimeStruct = TimeSpan.Zero;
TimeSpan createTimeClass = TimeSpan.Zero;
classes.Clear();
structs.Clear();
// force garbage collection so that it doesn't happen
// in the middle of things.
GC.Collect();
GC.WaitForPendingFinalizers();
sw.Reset();
sw.Start();
for (var i = 0; i < Iterations; i++)
{
var start = sw.Elapsed;
var c = new CoordClass(23, 24);
var stop = sw.Elapsed;
createTimeClass += (stop - start);
classes.Add(c);
}
sw.Stop();
Console.WriteLine("Classes: {0} ms ({1})", sw.ElapsedMilliseconds, createTimeClass.TotalMilliseconds);
sw.Reset();
sw.Start();
for (var i = 0; i < Iterations; i++)
{
var start = sw.Elapsed;
var c = new CoordStruct(23, 24);
var stop = sw.Elapsed;
createTimeStruct += (stop - start);
structs.Add(c);
}
sw.Stop();
Console.WriteLine("Structs: {0} ms ({1})", sw.ElapsedMilliseconds, createTimeStruct.TotalMilliseconds);
Console.WriteLine("===================");
}
}
现在,如果您想查看添加到空列表和添加到预分配列表之间的区别,请运行以下代码:
private void DoStuff()
{
const int Iterations = 10000000;
while (true)
{
GC.Collect();
GC.WaitForPendingFinalizers();
var sw = Stopwatch.StartNew();
var structs = new List<CoordStruct>();
AddItems(structs, Iterations);
sw.Stop();
Console.WriteLine("Empty list: {0:N0} ms", sw.ElapsedMilliseconds);
sw.Restart();
structs = new List<CoordStruct>(Iterations);
AddItems(structs, Iterations);
sw.Stop();
Console.WriteLine("Pre-allocated list: {0:N0} ms", sw.ElapsedMilliseconds);
Console.WriteLine("===================");
}
}
private void AddItems(List<CoordStruct> list, int nItems)
{
for (var i = 0; i < nItems; ++i)
{
list.Add(new CoordStruct(23, 24));
}
}
在我的机器上,空列表大约需要 140 毫秒,而预分配列表大约需要 100 毫秒。