C# に 3 つのバイト配列があり、それを 1 つに結合する必要があります。このタスクを完了するための最も効率的な方法は何でしょうか?
ベストアンサー1
プリミティブ型(バイトを含む)の場合は、System.Buffer.BlockCopy
の代わりにSystem.Array.Copy
。もっと速いです。
提案された各メソッドを、それぞれ 10 バイトの配列 3 つを使用して 100 万回ループ実行して計測しました。結果は次のとおりです。
- 新しいバイト配列の使用
System.Array.Copy
- 0.2187556 秒 - 新しいバイト配列の使用
System.Buffer.BlockCopy
- 0.1406286 秒 - C# yield 演算子を使用した IEnumerable<byte> - 0.0781270 秒
- LINQ の Concat<> を使用した IEnumerable<byte> - 0.0781270 秒
各配列のサイズを 100 要素に増やし、テストを再実行しました。
- 新しいバイト配列の使用
System.Array.Copy
- 0.2812554 秒 - 新しいバイト配列の使用
System.Buffer.BlockCopy
- 0.2500048秒 - C# yield 演算子を使用した IEnumerable<byte> - 0.0625012 秒
- LINQ の Concat<> を使用した IEnumerable<byte> - 0.0781265 秒
各配列のサイズを 1000 要素に増やし、テストを再実行しました。
- 新しいバイト配列の使用
System.Array.Copy
- 1.0781457 秒 - 新しいバイト配列の使用
System.Buffer.BlockCopy
- 1.0156445 秒 - C# yield 演算子を使用した IEnumerable<byte> - 0.0625012 秒
- LINQ の Concat<> を使用した IEnumerable<byte> - 0.0781265 秒
最後に、各配列のサイズを 100 万要素に増やし、各ループを4000 回だけ実行してテストを再実行しました。
- 新しいバイト配列の使用
System.Array.Copy
- 13.4533833 秒 - 新しいバイト配列の使用
System.Buffer.BlockCopy
- 13.1096267 秒 - C# の yield 演算子を使用した IEnumerable<byte> - 0 秒
- LINQ の Concat<> を使用した IEnumerable<byte> - 0 秒
したがって、新しいバイト配列が必要な場合は、
byte[] rv = new byte[a1.Length + a2.Length + a3.Length];
System.Buffer.BlockCopy(a1, 0, rv, 0, a1.Length);
System.Buffer.BlockCopy(a2, 0, rv, a1.Length, a2.Length);
System.Buffer.BlockCopy(a3, 0, rv, a1.Length + a2.Length, a3.Length);
ただし、 を使用できる場合はIEnumerable<byte>
、 LINQ の Concat<> メソッドを必ず使用してください。これは C# の yield 演算子よりもわずかに遅いだけですが、より簡潔でエレガントです。
IEnumerable<byte> rv = a1.Concat(a2).Concat(a3);
任意の数の配列があり、.NET 3.5 を使用している場合は、System.Buffer.BlockCopy
次のようにソリューションをより汎用的にすることができます。
private byte[] Combine(params byte[][] arrays)
{
byte[] rv = new byte[arrays.Sum(a => a.Length)];
int offset = 0;
foreach (byte[] array in arrays) {
System.Buffer.BlockCopy(array, 0, rv, offset, array.Length);
offset += array.Length;
}
return rv;
}
*注: 上記のブロックを機能させるには、先頭に次の名前空間を追加する必要があります。
using System.Linq;
後続のデータ構造 (バイト配列と IEnumerable<byte>) の反復処理に関する Jon Skeet の指摘については、最後のタイミング テスト (100 万要素、4000 回の反復処理) を再実行し、各パスで配列全体を反復処理するループを追加しました。
- 新しいバイト配列の使用
System.Array.Copy
- 78.20550510 秒 - 新しいバイト配列の使用
System.Buffer.BlockCopy
- 77.89261900 秒 - C# yield 演算子を使用した IEnumerable<byte> - 551.7150161 秒
- LINQ の Concat<> を使用した IEnumerable<byte> - 448.1804799 秒
重要なのは、結果として得られるデータ構造の作成と使用の両方の効率性を理解することが非常に重要であるということです。作成の効率性だけに焦点を当てると、使用に関連する非効率性を見落とす可能性があります。Jon さん、おめでとうございます。