Stream.CopyToのデフォルト実装がパイプされていないことに驚きました。配管がないということは、目的地への書込み中に読取りが行われないことを意味します。Stream.CopyToの高速版/パイプ版
これは、ほとんどの場合、パフォーマンスはパフォーマンスの半分であることを意味します。 (ファイルストリームをC:から別のHDDにコピー)
高速なパイプでコピーされたCopyToを実装するにはどうすればよいですか?
Stream.CopyToのデフォルト実装がパイプされていないことに驚きました。配管がないということは、目的地への書込み中に読取りが行われないことを意味します。Stream.CopyToの高速版/パイプ版
これは、ほとんどの場合、パフォーマンスはパフォーマンスの半分であることを意味します。 (ファイルストリームをC:から別のHDDにコピー)
高速なパイプでコピーされたCopyToを実装するにはどうすればよいですか?
これは私が考えることができる最も強力な実装です:
public void CopyTo_Fast(Stream Destination, int Buffersize)
{
byte[] BufferA = new byte[Buffersize];
byte[] BufferB = new byte[Buffersize];
int Abytes = Read(BufferA, 0, BufferA.Length);//read first buffer
if (Abytes < Buffersize) //trivial case stream too small for two buffers
{
Destination.Write(BufferA, 0, Abytes);
return;
}
int Bbytes = 0;
Task Writer;
Task Reader;
while(true) //Read to end
{
Writer = Task.Run(() => Destination.Write(BufferA, 0, Abytes));
Reader = Task.Run(() => Bbytes = Read(BufferB, 0, Buffersize));
Task.WaitAll(Writer, Reader);
if (Bbytes == 0) return;
Writer = Task.Run(() => Destination.Write(BufferB, 0, Bbytes));
Reader = Task.Run(() => Abytes = Read(BufferA, 0, Buffersize));
Task.WaitAll(Writer, Reader);
if (Abytes == 0) return;
}
}
ファイルがシーケンシャル先読みを持っていると、オペレーティングシステムから直接バッファリングする書き込み。このような場合、最も基本的な実装のパフォーマンスを改善する可能性は低いです(Copy
実装)。
同様に、TCPソケットはかなりのデータをバッファしますので、プロトコルが大量のデータ送信を許可する限り、基本的なCopy
もそれに適しています。
バッファリングされていない状態でファイルを開くと(非常にまれですが、通常は、OSよりも優れていることがわかっている独自のキャッシングを実行できる場合はこれを行う)、ネットワークプロトコルが不適切に設計されていると、独自の特別なバッファリングが大幅に改善される可能性があります。
このような何かが、それらのほとんどの場合には十分なはず...
static async Task CopyMaybeFaster(Stream src, Stream dst)
{
byte[] buffer = new byte[65536];
int curoff = 0;
Task<int> readTask = src.ReadAsync(buffer, curoff, 32768);
Task writeTask = Task.CompletedTask;
int len;
while ((len = await readTask.ConfigureAwait(false)) != 0)
{
await writeTask.ConfigureAwait(false);
writeTask = dst.WriteAsync(buffer, curoff, len);
curoff ^= 32768;
readTask = src.ReadAsync(buffer, curoff, 32768);
}
await writeTask.ConfigureAwait(false);
}
このコードはパイプされていません。ここであなたがしているのは、読んで待っていて、書いているのを待っています。コントロールは呼び出し元に戻り、同時に読み取りも書き込みも行いません。これはまったく速くはありません。 –
再度読み込み、同時に読み書きを行います。 –
いいえ、タスクを定義してもタスクは実行されません。一度に1つのタスクを待つだけです。両方のタスクでTask.WaitAllを実行する場合、コードはPipedになります。 –
これは非常に悪いの提案です - 同期コードの上に非同期ラッパーは良い練習(真asyncオプションが存在している場合は特に)ではありません、タスクを待っているとデッドロックが発生する可能性があります。さらに、OPはネット版が遅いという実証されていない主張をしましたが、この回答は性能部分をまったくカバーしていません。 –
同期パイプバージョンのマルチスレッドが必要です。すべての例では、古いBeginRead構文とEndRead構文を使用していました。これは保守がずっと簡単です。私はパフォーマンスの違いと非同期バージョンを追加します。このコードにはデッドロックもありません。 –