Добавить два или более байтовых массивов в C # - PullRequest
30 голосов
/ 22 мая 2009

Есть ли лучший (см. Ниже) способ добавить два байтовых массива в C #?

Притворяясь, что у меня полный контроль, я могу сделать первый байтовый массив достаточно большим, чтобы в конце удерживать второй байтовый массив, и использовать функцию Array.CopyTo . Или я могу перебрать отдельные байты и сделать присваивание.

Есть ли лучшие способы? Я не могу себе представить, что преобразование байтовых массивов в строку и их объединение и обратное преобразование будет лучше, чем любой из указанных выше методов.

С точки зрения лучшего / лучшего (по порядку):

  1. быстрый
  2. Наименьшее потребление ОЗУ

Ограничением является то, что я должен работать в среде .NET 2.0.

Рекомендуется два варианта: MemoryStream и BlockCopy. Я выполнил простой тест скорости 10 000 000 циклов 3 раза и получил следующие результаты:

Среднее из 3 циклов по 10 000 000 циклов в миллисекундах:

  • BlockCopy Time: 1154 , с диапазоном 13 миллисекунд
  • MemoryStream GetBuffer Time: 1470, с диапазоном 14 миллисекунд
  • MemoryStream ToArray Time: 1895, с диапазоном 3 миллисекунды
  • Время копирования: 2079, с диапазоном 19 миллисекунд
  • Байт за байтом Время: 2203, с диапазоном 10 миллисекунд

Результаты списка AddRange более 10 миллионов циклов: Список Время: 16694

Относительное потребление ОЗУ (1 - базовый уровень, выше - хуже):

  • Побайтно: 1
  • BlockCopy: 1
  • Копировать в: 1
  • MemoryStream GetBuffer: 2,3
  • MemoryStream ToArray: 3,3
  • Список : 4,2

Тест показывает, что в общем случае, если вы не делаете много байтовых копий [, которыми я ], просмотр байтовых копий не стоит внимания [например, 10 миллионов пробежек дают разницу в 1,1 секунды].

Ответы [ 8 ]

24 голосов
/ 22 мая 2009

Вы хотите BlockCopy

Согласно этому сообщению в блоге это быстрее, чем Array.CopyTo.

14 голосов
/ 22 мая 2009

Вы также можете использовать подход с MemoryStream. Предположим, что b1 и b2 - двухбайтовые массивы, вы можете получить новый, b3, используя MemoryStream следующим образом:

var s = new MemoryStream();
s.Write(b1, 0, b1.Length);
s.Write(b2, 0, b2.Length);
var b3 = s.ToArray();

Это должно работать без LINQ и фактически немного быстрее.

11 голосов
/ 22 мая 2009

Создайте новый MemoryStream , передав в конструктор буфер, точно равный размеру объединенного. Запишите отдельные массивы и, наконец, используйте буфер:

byte[] deadBeef = new byte[] { 0xDE, 0xAD, 0xBE, 0xEF};
byte[] baadF00d = new byte[] { 0xBA, 0xAD, 0xF0, 0x0D};
int newSize = deadBeef.Length + baadF00d.Length;
var ms = new MemoryStream(new byte[newSize], 0, newSize, true, true);
ms.Write(deadBeef, 0, deadBeef.Length);
ms.Write(baadF00d, 0, baadF00d.Length);
byte[] merged = ms.GetBuffer();

Многие низкоуровневые функции ввода / вывода в .NET принимают байтовые массивы и смещения. Это было сделано для предотвращения ненужных копий. Убедитесь, что вам действительно нужен объединенный массив, если он чувствителен к производительности, иначе просто используйте буферы и смещения.

6 голосов
/ 22 мая 2009

Другой вариант, хотя я не проверял его, чтобы увидеть, как он работает с точки зрения скорости и потребления памяти, подойдет LINQ:

byte[] combined = bytesOne.Concat(bytesTwo).Concat(bytesThree).ToArray();

... где bytesOne, bytesTwo и bytesThree - это байтовые массивы. Поскольку Concat использует отложенное выполнение, это не должно создавать промежуточных массивов и не должно дублировать исходные массивы, пока он не создаст окончательный объединенный массив в конце.

Редактировать: LINQBridge позволит вам использовать LINQ-to-Objects (это пример) в каркасе 2.0. Я понимаю, если вы не хотите зависеть от этого, но это вариант.

3 голосов
/ 22 мая 2009

Если у вас есть массивы, размер которых будет меняться время от времени, вам, вероятно, лучше использовать List<T>. Затем вы можете просто вызвать AddRange() метод списка.

В противном случае Array.Copy () или Array.CopyTo () так же хороши, как и все, что вы можете увидеть.

2 голосов
/ 22 мая 2009

Вам нужен вывод, чтобы фактически быть байтовым массивом?

Если нет, вы можете создать себе «умный курсор» (который похож на то, что делает LINQ): создайте пользовательский IEnumerator , который сначала будет выполнять итерацию первого массива, и просто продолжать второй, без прерывания.

Это работало бы в среде 2.0 быстро (в том смысле, что объединение массивов практически не требует затрат) и использовало бы не больше ОЗУ, чем уже потребляют массивы.

2 голосов
/ 22 мая 2009

Научили ли вы использовать List или ArrayList вместо Array? С этими типами они могут увеличиваться или уменьшаться и добавляться через InsertRange

0 голосов
/ 22 мая 2009

Ваш первый вариант сделать первый массив достаточно большим, чтобы содержать второй массив, и использовать Array.CopyTo, в итоге будет примерно таким же, как итерация по каждому элементу вручную и выполнение назначения. Array.CopyTo () просто делает его более лаконичным.

Преобразование в строку и обратно в массив будет ужасно медленным в отличие от вышеизложенного. И, вероятно, будет использовать больше памяти.

...