tcp プロキシ コードを書いているとします。着信ストリームから読み取り、出力ストリームに書き込みます。Stream.Copy がバッファを使用することは知っていますが、私の質問は次のとおりです。Stream.Copy メソッドは、入力ストリームから次のチャンクをフェッチしている間に出力ストリームに書き込みますか、それとも「入力からチャンクを読み取り、チャンクを出力に書き込みます」のようなループですか? 、入力からチャンクを読み取るなど」?
4 に答える
.NET4.5での実装は次のCopyTo
とおりです。
private void InternalCopyTo(Stream destination, int bufferSize)
{
int num;
byte[] buffer = new byte[bufferSize];
while ((num = this.Read(buffer, 0, buffer.Length)) != 0)
{
destination.Write(buffer, 0, num);
}
}
ご覧のとおり、ソースから読み取り、宛先に書き込みます。これはおそらく改善される可能性があります;)
編集:パイプバージョンの可能な実装は次のとおりです。
public static void CopyToPiped(this Stream source, Stream destination, int bufferSize = 0x14000)
{
byte[] readBuffer = new byte[bufferSize];
byte[] writeBuffer = new byte[bufferSize];
int bytesRead = source.Read(readBuffer, 0, bufferSize);
while (bytesRead > 0)
{
Swap(ref readBuffer, ref writeBuffer);
var iar = destination.BeginWrite(writeBuffer, 0, bytesRead, null, null);
bytesRead = source.Read(readBuffer, 0, bufferSize);
destination.EndWrite(iar);
}
}
static void Swap<T>(ref T x, ref T y)
{
T tmp = x;
x = y;
y = tmp;
}
基本的に、チャンクを同期的に読み取り、非同期で宛先へのコピーを開始してから、次のチャンクを読み取り、書き込みが完了するのを待ちます。
いくつかのパフォーマンステストを実行しました。
- sを使用
MemoryStream
すると、IO完了ポート(AFAIK)を使用しないため、大幅な改善は期待できませんでした。実際、パフォーマンスはほぼ同じです - 異なるドライブ上のファイルを使用すると、パイプバージョンのパフォーマンスが向上すると期待していましたが、そうではありません...実際にはわずかに遅くなります(5〜10%)
したがって、それは明らかに何の利益ももたらさないので、おそらくそれがこのように実装されていない理由です...
Reflectorによると、そうではありません。このような動作は、並行性が導入されるため、文書化することをお勧めします。これは、一般的に行うのは決して安全ではありません。したがって、「パイプ」しない API 設計は適切です。
したがって、これはStream.Copy
多かれ少なかれ頭が良いかどうかだけの問題ではありません。並行してコピーすることは、実装の詳細ではありません。
Stream.Copyは同期操作です。非同期の読み取り/書き込みを使用して読み取りと書き込みを同時に行うことを期待するのは合理的ではないと思います。
非同期バージョン( RandomAccessStream.CopyAsyncなど)は、読み取りと書き込みを同時に使用することを期待しています。
注:コピー中に複数のスレッドを使用することは望ましくない動作ですが、非同期の読み取りと書き込みを使用してそれらを同時に実行することは問題ありません。
次のチャンクをフェッチすると、出力に使用されている間にバッファが上書きされる可能性があるため、次のチャンクをフェッチしている間は (1 つのバッファを使用している場合)、出力ストリームへの書き込みは不可能です。
ダブルバッファリングを使用すると言うことができますが、ダブルサイズのバッファを使用するのとほとんど同じです。