処理を高速化するには、バッチ トランザクション (エンティティ グループ トランザクション) を使用して、1 回のリクエストで最大 100 個のアイテムをコミットできるようにする必要があります。
foreach (var item in myItemsToAdd)
{
this.Context.AddObject(TableName, item);
}
this.Context.SaveChanges(SaveChangesOptions.Batch);
これをPartitioner.Create (+ AsParallel) と組み合わせて、100 項目のバッチごとに異なるスレッド/コアで複数の要求を送信して、物事を非常に高速にすることができます。
ただし、これらすべてを実行する前に、バッチ トランザクションを使用する際の制限(100 アイテム、トランザクションごとに 1 パーティションなど) をお読みください。
アップデート:
ここではトランザクションを使用できないため、その他のヒントをいくつか示します。テーブル ストレージを使用する場合のパフォーマンスの向上に関するこの MSDN スレッドをご覧ください。違いを示すためにいくつかのコードを書きました:
private static void SequentialInserts(CloudTableClient client)
{
var context = client.GetDataServiceContext();
Trace.WriteLine("Starting sequential inserts.");
var stopwatch = new Stopwatch();
stopwatch.Start();
for (int i = 0; i < 1000; i++)
{
Trace.WriteLine(String.Format("Adding item {0}. Thread ID: {1}", i, Thread.CurrentThread.ManagedThreadId));
context.AddObject(TABLENAME, new MyEntity()
{
Date = DateTime.UtcNow,
PartitionKey = "Test",
RowKey = Guid.NewGuid().ToString(),
Text = String.Format("Item {0} - {1}", i, Guid.NewGuid().ToString())
});
context.SaveChanges();
}
stopwatch.Stop();
Trace.WriteLine("Done in: " + stopwatch.Elapsed.ToString());
}
したがって、これを初めて実行すると、次の出力が得られます。
Starting sequential inserts.
Adding item 0. Thread ID: 10
Adding item 1. Thread ID: 10
..
Adding item 999. Thread ID: 10
Done in: 00:03:39.9675521
1000 個のアイテムを追加するには 3 分以上かかります。ここで、MSDN フォーラムのヒントに基づいて app.config を変更しました (maxconnection は 12 * CPU コア数である必要があります)。
<system.net>
<settings>
<servicePointManager expect100Continue="false" useNagleAlgorithm="false"/>
</settings>
<connectionManagement>
<add address = "*" maxconnection = "48" />
</connectionManagement>
</system.net>
アプリケーションを再度実行すると、次の出力が得られます。
Starting sequential inserts.
Adding item 0. Thread ID: 10
Adding item 1. Thread ID: 10
..
Adding item 999. Thread ID: 10
Done in: 00:00:18.9342480
3分以上から18秒まで。なんという違いでしょう!しかし、私たちはもっとうまくやることができます。以下は、Partitioner を使用してすべての項目を挿入するコードです (挿入は並行して行われます)。
private static void ParallelInserts(CloudTableClient client)
{
Trace.WriteLine("Starting parallel inserts.");
var stopwatch = new Stopwatch();
stopwatch.Start();
var partitioner = Partitioner.Create(0, 1000, 10);
var options = new ParallelOptions { MaxDegreeOfParallelism = 8 };
Parallel.ForEach(partitioner, options, range =>
{
var context = client.GetDataServiceContext();
for (int i = range.Item1; i < range.Item2; i++)
{
Trace.WriteLine(String.Format("Adding item {0}. Thread ID: {1}", i, Thread.CurrentThread.ManagedThreadId));
context.AddObject(TABLENAME, new MyEntity()
{
Date = DateTime.UtcNow,
PartitionKey = "Test",
RowKey = Guid.NewGuid().ToString(),
Text = String.Format("Item {0} - {1}", i, Guid.NewGuid().ToString())
});
context.SaveChanges();
}
});
stopwatch.Stop();
Trace.WriteLine("Done in: " + stopwatch.Elapsed.ToString());
}
そして結果:
Starting parallel inserts.
Adding item 0. Thread ID: 10
Adding item 10. Thread ID: 18
Adding item 999. Thread ID: 16
..
Done in: 00:00:04.6041978
出来上がり、3 分 39 秒から 18 秒に落ち、今では 4 秒にさえ落ちています。