概要
大きなテキスト ファイルを処理しているときに、説明できない次の (予期しない) パフォーマンスの低下に遭遇しました。この質問に対する私の目的は次のとおりです。
- 以下に説明する速度低下の原因を理解する
- 大規模な非プリミティブ配列をすばやく初期化する方法を見つける
問題
- 配列に非プリミティブ参照項目が含まれています
- ではない
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);
}
}
}
2 つのほぼ同一のループが含まれています。ループごとに、Item
クラスのインスタンスが 100 万個作成されます。最初のループは、作成された項目を使用してから、参照を破棄します (items
配列に保持しません)。2 番目のループは、作成された項目を使用し、参照を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 を使用してスローダウンを調査しようとしましたが、決定的ではありませんでした。私が目にしたことの 1 つは、代入を使用しないループよりも、代入を使用するループの方が、文字列のコピーとガベージ コレクションのオーバーヘッドがはるかに多いことでした (私はその逆を予想していましたが)。