C #에는 3 바이트 배열이 있는데 하나로 결합해야합니다. 이 작업을 완료하는 가장 효율적인 방법은 무엇입니까?
답변
기본 유형 (바이트 포함)의 경우을 System.Buffer.BlockCopy
대신 사용하십시오 System.Array.Copy
. 더 빠릅니다.
루프에서 제안 된 각 메소드의 시간을 각각 10 바이트의 3 개의 배열을 사용하여 백만 번 실행했습니다. 결과는 다음과 같습니다.
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 초
마지막으로, 1 개 백만 요소 각 어레이의 크기를 증가시키고 각각의 루프를 실행하는 시험을 다시 실행 단지 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의 요점에 대해, 마지막 타이밍 테스트 (1 백만 요소, 4000 반복)를 다시 실행하여 각 배열마다 전체 배열을 반복하는 루프를 추가합니다 통과하다:
System.Array.Copy
-78.20550510 초를 사용하는 새로운 바이트 배열System.Buffer.BlockCopy
-77.89261900 초를 사용하는 새로운 바이트 배열- C # yield 연산자를 사용하는 IEnumerable <byte>-551.7150161 초
- LINQ의 Concat <>을 사용한 IEnumerable <byte>-448.1804799 초
요점은 결과 데이터 구조 의 생성 및 사용 의 효율성을 이해하는 것이 매우 중요하다는 것입니다 . 창작의 효율성에 집중하는 것만으로도 사용과 관련된 비 효율성을 간과 할 수 있습니다. 쿠도스, 존
답변
많은 답변이 명시된 요구 사항을 무시하는 것 같습니다.
- 결과는 바이트 배열이어야합니다
- 가능한 한 효율적이어야합니다
이 두 가지가 함께 LINQ 바이트 시퀀스를 배제합니다-모든 yield
것은 전체 시퀀스를 반복하지 않고 최종 크기를 얻는 것을 불가능하게 만듭니다.
이것이 실제 요구 사항 이 아닌 경우 LINQ는 완벽한 솔루션 (또는 IList<T>
구현) 일 수 있습니다. 그러나 Superdumbell이 자신이 원하는 것을 알고 있다고 가정합니다.
(편집 :.. 난 그냥 당신이를 호출 한 후 “소스”배열 중 하나의 데이터를 변경하면 어떻게되는지 생각해 배열의 복사본을 만들고 유유히을 읽는 사이에 큰 의미 차이가있어 다른 생각을 했어 Combine
(또는 무엇이든 ) 방법이지만 결과를 사용하기 전에-게으른 평가로 변경 사항을 볼 수 있습니다. 즉시 사본을 사용하면 변경되지 않습니다. 상황에 따라 다른 행동이 필요합니다.
여기에 내가 제안한 방법이 있습니다-다른 답변 중 일부에 포함 된 것과 매우 유사합니다. 🙂
public static byte[] Combine(byte[] first, byte[] second)
{
byte[] ret = new byte[first.Length + second.Length];
Buffer.BlockCopy(first, 0, ret, 0, first.Length);
Buffer.BlockCopy(second, 0, ret, first.Length, second.Length);
return ret;
}
public static byte[] Combine(byte[] first, byte[] second, byte[] third)
{
byte[] ret = new byte[first.Length + second.Length + third.Length];
Buffer.BlockCopy(first, 0, ret, 0, first.Length);
Buffer.BlockCopy(second, 0, ret, first.Length, second.Length);
Buffer.BlockCopy(third, 0, ret, first.Length + second.Length,
third.Length);
return ret;
}
public static byte[] Combine(params byte[][] arrays)
{
byte[] ret = new byte[arrays.Sum(x => x.Length)];
int offset = 0;
foreach (byte[] data in arrays)
{
Buffer.BlockCopy(data, 0, ret, offset, data.Length);
offset += data.Length;
}
return ret;
}
물론 “params”버전은 바이트 배열을 먼저 생성해야하므로 추가적인 비효율이 발생합니다.
답변
코드 청결을 위해 Matt의 LINQ 예제를 한 단계 더 발전 시켰습니다.
byte[] rv = a1.Concat(a2).Concat(a3).ToArray();
필자의 경우 배열이 작으므로 성능에 대해 걱정하지 않습니다.
답변
단순히 새로운 바이트 배열이 필요한 경우 다음을 사용하십시오.
byte[] Combine(byte[] a1, byte[] a2, byte[] a3)
{
byte[] ret = new byte[a1.Length + a2.Length + a3.Length];
Array.Copy(a1, 0, ret, 0, a1.Length);
Array.Copy(a2, 0, ret, a1.Length, a2.Length);
Array.Copy(a3, 0, ret, a1.Length + a2.Length, a3.Length);
return ret;
}
또는 IEnumerable이 하나만 필요한 경우 C # 2.0 yield 연산자를 사용해보십시오.
IEnumerable<byte> Combine(byte[] a1, byte[] a2, byte[] a3)
{
foreach (byte b in a1)
yield return b;
foreach (byte b in a2)
yield return b;
foreach (byte b in a3)
yield return b;
}
답변
실제로 Concat을 사용하는 데 몇 가지 문제가 발생했습니다 …
나는 다음이 간단하고 쉽고, 나에게 충돌하지 않고 충분히 잘 작동한다는 것을 알았으며 3 개가 아닌 여러 배열 (LINQ 사용)에서 작동합니다.
public static byte[] ConcatByteArrays(params byte[][] arrays)
{
return arrays.SelectMany(x => x).ToArray();
}
답변
메모리 스트림 클래스는이 작업을 꽤 잘 수행합니다. 버퍼 스트림을 메모리 스트림만큼 빠르게 실행할 수 없었습니다.
using (MemoryStream ms = new MemoryStream())
{
ms.Write(BitConverter.GetBytes(22),0,4);
ms.Write(BitConverter.GetBytes(44),0,4);
ms.ToArray();
}
답변
public static bool MyConcat<T>(ref T[] base_arr, ref T[] add_arr)
{
try
{
int base_size = base_arr.Length;
int size_T = System.Runtime.InteropServices.Marshal.SizeOf(base_arr[0]);
Array.Resize(ref base_arr, base_size + add_arr.Length);
Buffer.BlockCopy(add_arr, 0, base_arr, base_size * size_T, add_arr.Length * size_T);
}
catch (IndexOutOfRangeException ioor)
{
MessageBox.Show(ioor.Message);
return false;
}
return true;
}