私はC#で達成するのはかなり簡単な作業です。ユーザーは私に大きなバイナリファイル(数十GBのサイズ)を表すストリームを与えます。このファイルはたくさんのブロックから構成されています。私は各ブロックを読む必要があります。各ブロックでCPU集約的な分析を実行します。ユーザーに結果を正しい順序で伝えます。擬似コードでは、このコードは次のようになります。それだけでCPUを集中的分析のための1つのCPUコアを使用していますので、これは、ゆっくりと正常に動作しますが、並んだ入力と出力を持つ並列パイプライン
public IEnumerable<TResult> ReadFile(Stream inputStream) {
while(true) {
byte[] block = ReadNextBlock(stream);
if (block == null) {
break; // EOF
}
TResult result = PerformCpuIntensiveAnalysis(block);
yield return result;
}
}
。私がしたいのは、ブロックを1つずつ読み込み、並列に解析してから、ファイル内でブロックが検出されたのと同じ順序で結果をユーザーに返します。当然ながら、ファイル全体をメモリに読み込むことはできませんので、いつでもキューに保持するブロック数を制限したいと思います。
これには多くの解決策があり、私はカップルを試しました。しかし、何らかの理由で、私は非常に単純なアプローチを凌駕任意の解決策を見つけることができません。
public IEnumerable<TResult> ReadFile(Stream inputStream) {
while(true) {
var batch = new List<byte[]>();
for (int i=0; i<BATCH_SIZE; i++) {
byte[] block = ReadNextBlock(stream);
if (block == null) {
break;
}
batch.Add(block);
}
if (batch.Count == 0) {
break;
}
foreach(var result in batch
.AsParallel()
.AsOrdered()
.Select(block => PerformCpuIntensiveAnalysis(block))
.ToList()) {
yield return result;
}
}
}
私はTPL /データフローだけでなく、純粋に手動のアプローチを試してみた、そしてすべての場合には、私のコードを費やしていますほとんどの時間は同期を待っています。シリアルバージョンは約2倍の性能を発揮しますが、8コアのマシンではそれ以上の性能が期待できます。だから、私は何が間違っているの?
(私も、私は本当に私はちょうど簡潔にするため、ここでそれを使用しています、私のコードで「降伏を返す」ジェネレータパターンを使用していないことを明確にする必要があります。)
問題を確実に再現する良い[mcve]がなければ、確かに知ることは不可能です。しかし、ボトルネックがCPUではなくI/Oである可能性を考慮する必要があります。 I/Oがボトルネックになっている場合は、牛が家に帰るまでスレッドとCPUコアを追加することができます。それでもやはりうまくいくわけではありません。 –
@Peter Duniho:この他にどのような情報が必要ですか? I/OとCPUの面では、メモリバッファに小さなファイル(〜100 MB)をロードしてからテスト用のMemoryStreamとしてラップしようとしました。したがって、I/Oはボトルネック。 – Bugmaster
プロファイラ(例:PerfView)を使用してボトルネックの発生場所を特定しようとしましたか? – easuter