[C#] C #에서 둘 이상의 바이트 배열을 결합하는 가장 좋은 방법

C #에는 3 바이트 배열이 있는데 하나로 결합해야합니다. 이 작업을 완료하는 가장 효율적인 방법은 무엇입니까?



답변

기본 유형 (바이트 포함)의 경우을 System.Buffer.BlockCopy대신 사용하십시오 System.Array.Copy. 더 빠릅니다.

루프에서 제안 된 각 메소드의 시간을 각각 10 바이트의 3 개의 배열을 사용하여 백만 번 실행했습니다. 결과는 다음과 같습니다.

  1. System.Array.Copy -0.2187556 초를 사용하는 새로운 바이트 배열
  2. System.Buffer.BlockCopy -0.1406286 초를 사용하는 새로운 바이트 배열
  3. C # yield 연산자를 사용하는 IEnumerable <byte>-0.0781270 초
  4. LINQ의 Concat <>을 사용한 IEnumerable <byte>-0.0781270 초

각 배열의 크기를 100 개 요소로 늘리고 테스트를 다시 실행했습니다.

  1. System.Array.Copy -0.2812554 초를 사용하는 새로운 바이트 배열
  2. System.Buffer.BlockCopy -0.2500048 초를 사용하는 새로운 바이트 배열
  3. C # yield 연산자를 사용하는 IEnumerable <byte>-0.0625012 초
  4. LINQ의 Concat <>을 사용한 IEnumerable <byte>-0.0781265 초

각 배열의 크기를 1000 요소로 늘리고 테스트를 다시 실행했습니다.

  1. System.Array.Copy -1.0781457 초를 사용하는 새로운 바이트 배열
  2. System.Buffer.BlockCopy -1.0156445 초를 사용하는 새로운 바이트 배열
  3. C # yield 연산자를 사용하는 IEnumerable <byte>-0.0625012 초
  4. LINQ의 Concat <>을 사용한 IEnumerable <byte>-0.0781265 초

마지막으로, 1 개 백만 요소 각 어레이의 크기를 증가시키고 각각의 루프를 실행하는 시험을 다시 실행 단지 4000 시간 :

  1. System.Array.Copy -13.4533833 초를 사용하는 새로운 바이트 배열
  2. System.Buffer.BlockCopy -13.1096267 초를 사용하는 새로운 바이트 배열
  3. C # yield 연산자를 사용하는 IEnumerable <byte>-0 초
  4. 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 반복)를 다시 실행하여 각 배열마다 전체 배열을 반복하는 루프를 추가합니다 통과하다:

  1. System.Array.Copy -78.20550510 초를 사용하는 새로운 바이트 배열
  2. System.Buffer.BlockCopy -77.89261900 초를 사용하는 새로운 바이트 배열
  3. C # yield 연산자를 사용하는 IEnumerable <byte>-551.7150161 초
  4. 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;
    }