HttpClient,HttpListenerに関係して、Streamのデータを全てbyte[]に読み込む方法を探してみると、
「StreamReader でファイルをすべて読み込む」とかが出てきてそれを参考にコードを書いてみる。
public static byte[] Source { get; set; } = Enumerable.Range(32, 80).Select(x => (byte) x).ToArray();
[Benchmark]
public byte[] StringStreamCopy()
{
var mem1 = new MemoryStream(Source);
System.IO.StreamReader reader = new System.IO.StreamReader(mem1, Encoding.UTF8);
string content = reader.ReadToEnd();
var result = Encoding.UTF8.GetBytes(content);
return result;
}
おー、きちんとコピーされている。
よかった。よかった。おしまい。
じゃなくて、動いたのはいいとして、これ、余計な仕事しまくっているよね。
今回のケース、元のstreamのデータはutf8です。
それをstring(=utf16)に読み込むから、変換が。
それを、byteに戻すのに、また変換が。
この無駄は省きたいので、もっと調べていくと、どうもMemoryStreamを使うとよいらしい。
それを含めて、いろいろ書いてみてベンチマークを取ってみました。
public class BenchTarget
{
public static byte[] Source { get; set; } = Enumerable.Range(32, 80).Select(x => (byte) x).ToArray();
public static byte[] Buffer { get; set; } = new byte[8192];
public static MemoryStream ShareMemoryStream { get; set; } = new MemoryStream(Buffer);
[Benchmark]
public byte[] ToArray()
{
return Source.ToArray();
}
[Benchmark]
public byte[] MemoryStreamCopyUseBuffer()
{
var mem1 = new MemoryStream(Source);
var mem2 = new MemoryStream(Buffer);
mem1.CopyTo(mem2);
var result = Buffer.AsSpan(0, (int)mem2.Position).ToArray();
return result;
}
[Benchmark]
public byte[] MemoryStreamCopyUseShare()
{
var mem1 = new MemoryStream(Source);
var mem2 = ShareMemoryStream;
mem2.Position = 0;
mem1.CopyTo(mem2);
var result = Buffer.AsSpan(0, (int)mem2.Position).ToArray();
return result;
}
[Benchmark]
public byte[] MemoryStreamCopyNoBuffer()
{
var mem1 = new MemoryStream(Source);
var mem2 = new MemoryStream();
mem1.CopyTo(mem2);
var result = mem2.ToArray();
return result;
}
[Benchmark]
public byte[] StringStreamCopy()
{
var mem1 = new MemoryStream(Source);
System.IO.StreamReader reader = new System.IO.StreamReader(mem1, Encoding.UTF8);
string content = reader.ReadToEnd();
var result = Encoding.UTF8.GetBytes(content);
return result;
}
}
//| Method | Mean | Error | StdDev | Median |
//|-------------------------- |----------:|---------:|---------:|----------:|
//| ToArray | 30.79 ns | 0.418 ns | 0.370 ns | 30.75 ns |
//| MemoryStreamCopyUseBuffer | 47.78 ns | 0.897 ns | 1.524 ns | 46.99 ns |
//| MemoryStreamCopyUseShare | 43.58 ns | 0.671 ns | 0.560 ns | 43.41 ns |
//| MemoryStreamCopyNoBuffer | 66.72 ns | 1.390 ns | 1.365 ns | 66.39 ns |
//| StringStreamCopy | 338.48 ns | 6.512 ns | 5.084 ns | 337.17 ns |
やっぱり、StringStreamCopyが遅いですね。5~10倍くらい。
ToArrayは単純コピーなのでやはり速い。
Streamで書くとMemoryStreamを使うと、多少のオーバーヘッドで簡単に書けます。
繰り返し処理の場合、Bufferを使いまわすと処理が速くなりそうです。
ただ、今回の様な書き方は、スレッドセーフじゃないのでマルチスレッドで使う場合は、Lockが必要です。