概括
在处理大型文本文件时,我遇到了以下(意外)我无法解释的性能下降。我对这个问题的目标是:
- 了解导致下述减速的原因
- 了解如何快速初始化大型非原始数组
问题
- 数组包含非原始参考项
- 不是
int[]
但MyComplexType[]
MyComplexType
是一个类,而不是一个结构MyComplexType
包含一些string
属性
- 不是
- 数组是预分配的
- 数组很大
- 如果在没有分配给数组的情况下创建和使用项目,则程序很快
- 如果创建了一个项目,然后将其分配给一个数组项目,则程序会显着减慢。
- 我希望数组项分配是一个简单的引用分配,但根据我在下面的测试程序的结果,情况似乎并非如此
测试程序
考虑以下C#
程序:
namespace Test
{
public static class Program
{
// Simple data structure
private sealed class Item
{
public Item(int i)
{
this.Name = "Hello " + i;
//this.Name = "Hello";
//this.Name = null;
}
public readonly string Name;
}
// Test program
public static void Main()
{
const int length = 1000000;
var items = new Item[length];
// Create one million items but don't assign to array
var w = System.Diagnostics.Stopwatch.StartNew();
for (var i = 0; i < length; i++)
{
var item = new Item(i);
if (!string.IsNullOrEmpty(item.Name)) // reference the item and its Name property
{
items[i] = null; // do not remember the item
}
}
System.Console.Error.WriteLine("Without assignment: " + w.Elapsed);
// Create one million items and assign to array
w.Restart();
for (var i = 0; i < length; i++)
{
var item = new Item(i);
if (!string.IsNullOrEmpty(item.Name)) // reference the item and its Name property
{
items[i] = item; // remember the item
}
}
System.Console.Error.WriteLine(" With assignment: " + w.Elapsed);
}
}
}
它包含两个几乎相同的循环。每个循环创建一百万个Item
类实例。第一个循环使用创建的项目,然后丢弃引用(不将其保留在items
数组中)。第二个循环使用创建的项目,然后将引用存储在items
数组中。数组项分配是循环之间的唯一区别。
我的结果
当我
Release
在我的机器上运行构建(打开优化)时,我得到以下结果:Without assignment: 00:00:00.2193348 With assignment: 00:00:00.8819170
有数组赋值的循环比没有赋值的循环要慢得多(慢约 4 倍)。
如果我更改
Item
构造函数以将常量字符串分配给Name
属性:public Item(int i) { //this.Name = "Hello " + i; this.Name = "Hello"; //this.Name = null; }
我得到以下结果:
Without assignment: 00:00:00.0228067 With assignment: 00:00:00.0718317
有分配的循环仍然比没有分配的循环慢约 3 倍
最后,如果我分配
null
给该Name
属性:public Item(int i) { //this.Name = "Hello " + i; //this.Name = "Hello"; this.Name = null; }
我得到以下结果:
Without assignment: 00:00:00.0146696 With assignment: 00:00:00.0105369
一旦没有分配字符串,没有分配的版本最终会稍微慢一些(我假设因为所有这些实例都被释放以进行垃圾收集)
问题
为什么数组项分配会大大减慢测试程序的速度?
是否有可以加快分配速度的属性/语言结构/等?
PS:我尝试使用 dotTrace 调查减速,但没有定论。我看到的一件事是,在有赋值的循环中,字符串复制和垃圾收集的开销比在没有赋值的循环中要多得多(尽管我预计会反过来)。