Array.Copy и Buffer.BlockCopy делают одно и то же, но BlockCopy
нацелены на быстрое копирование примитивных массивов на уровне байтов, тогда как Copy
это реализация общего назначения. У меня вопрос - при каких обстоятельствах вам следует использовать BlockCopy
? Следует ли вам использовать его в любое время, когда вы копируете массивы примитивных типов, или вам следует использовать его только в том случае, если вы кодируете для повышения производительности? Есть ли что-нибудь опасное в использовании Buffer.BlockCopy
over Array.Copy
?
124
Marshal.Copy
:-). Хорошо, используйтеArray.Copy
для ссылочных типов, сложных типов значений и, если тип не меняется,Buffer.BlockCopy
для «преобразования» между типами значений, байтовыми массивами и байтовой магией. F.ex. комбинация сStructLayout
довольно эффективна, если вы знаете, что делаете. Что касается производительности, кажется, что неуправляемый вызовmemcpy
/cpblk
является самым быстрым для этого - см. Code4k.blogspot.nl/2010/10/… .byte[]
. В версии Release разницы не было. ИногдаArray.Copy
, иногдаBuffer.BlockCopy
(немного) быстрее.Array.Copy
это скорее специализированная версия - например, она может копировать только те же ранговые массивы.Ответы:
Поскольку параметры должны быть
Buffer.BlockCopy
основаны на байтах, а не на индексах, вы с большей вероятностью испортите свой код, чем если бы вы его использовалиArray.Copy
, поэтому я бы использовал толькоBuffer.BlockCopy
в критичном для производительности разделе моего кода.источник
UInt16
это два байта на элемент. Если вы передадите этот массив в BlockCopy вместе с количеством элементов в массиве, конечно, будет скопирована только половина массива. Чтобы это работало правильно, вам нужно будет передать количество элементов, умноженное на размер каждого элемента (2), в качестве параметра длины. msdn.microsoft.com/en-us/library/… и выполните поискINT_SIZE
в примерах.прелюдия
Я присоединяюсь к вечеринке поздно, но с 32k просмотров, это стоит сделать правильно. Большая часть кода микробенчмаркинга в опубликованных ответах до сих пор страдает одним или несколькими серьезными техническими недостатками, включая отсутствие перемещения выделения памяти из тестовых циклов (что приводит к серьезным артефактам сборки мусора), отсутствие тестирования переменных и детерминированных потоков выполнения, разогрева JIT, и не отслеживая вариабельность внутри теста. Кроме того, в большинстве ответов не проверялось влияние различных размеров буфера и различных типов примитивов (в отношении 32-битных или 64-битных систем). Чтобы ответить на этот вопрос более всесторонне, я подключил его к разработанной мной специальной структуре микробенчмаркинга, которая максимально сокращает большинство распространенных ошибок. Тесты проводились в режиме выпуска .NET 4.0 как на 32-разрядной, так и на 64-разрядной машинах. Результаты были усреднены по 20 тестам, в каждом из которых было 1 миллион попыток на метод. Протестированные примитивные типы были
byte
(1 байт),int
(4 байта) иdouble
(8 байтов). Были протестированы три метода:Array.Copy()
,Buffer.BlockCopy()
, и просто за индексом присваивания в цикле. Данные слишком объемны, чтобы публиковать их здесь, поэтому я резюмирую важные моменты.Выводы
Array.Copy()
илиBuffer.BlockCopy()
все 3 примитивных типа, протестированных как на 32-разрядных, так и на 64-разрядных машинах. Кроме того, у подпрограммы явного копирования цикла заметно меньшая изменчивость производительности по сравнению с двумя альтернативами. Хорошая производительность почти наверняка связана с локальностью ссылки, используемой кэшированием памяти L1 / L2 / L3 ЦП в сочетании с отсутствием накладных расходов на вызов методов.double
буферов на 32-битных машинах : процедура копирования явного цикла лучше, чем обе альтернативы, для всех протестированных размеров буфера до 100 КБ. Улучшение на 3-5% лучше, чем при использовании других методов. Это связано с тем, что производительностьArray.Copy()
иBuffer.BlockCopy()
полностью ухудшаются при передаче собственной 32-битной ширины. Таким образом, я предполагаю, что тот же эффект применим и кlong
буферам.byte[]
, где явное копирование цикла может стать в 7 раз или более медленнее при больших размерах буфера.Array.Copy()
иBuffer.BlockCopy()
выполняются почти одинаково. В среднем,Array.Copy()
кажется, есть очень небольшое преимущество около 2% или меньше затраченного времени (но обычно на 0,2% - 0,5% лучше), хотяBuffer.BlockCopy()
иногда и побеждает. По неизвестным причинамBuffer.BlockCopy()
имеет заметно более высокую вариабельность внутри теста, чемArray.Copy()
. Этот эффект не удалось устранить, несмотря на то, что я пробовал несколько способов смягчения последствий и не имел действенной теории о том, почему.Array.Copy()
это «умнее», более общий и гораздо более безопасный метод, помимо того, что он немного быстрее и в среднем имеет меньшую вариабельность, ему следует предпочестьBuffer.BlockCopy()
почти во всех распространенных случаях. Единственный вариант использования, когдаBuffer.BlockCopy()
будет значительно лучше, - это когда типы значений исходного и целевого массива различаются (как указано в ответе Кена Смита). Хотя этот сценарий не является распространенным, здесь онArray.Copy()
может работать очень плохо из-за постоянного «безопасного» преобразования типа значения по сравнению с прямым преобразованиемBuffer.BlockCopy()
.Array.Copy()
это быстрее, чемBuffer.BlockCopy()
при копировании однотипных массивов, можно найти здесь .источник
Array.Clear()
первый начинает бить явный клиринг присваивания цикла массива (параметр дляfalse
,0
илиnull
). Это согласуется с моими аналогичными выводами выше. Эти отдельные тесты были обнаружены в Интернете здесь: manski.net/2012/12/net-array-clear-vs-arrayx-0-performanceLoop Results for 1000000 iterations 17.9515ms. Buffer.BlockCopy Results for 1000000 iterations 39.8937ms. Array.Copy Results for 1000000 iterations 45.9059ms
Однако, если размер копии> ~ 20 байт, явный цикл будет значительно медленнее.Другой пример того, когда имеет смысл использовать,
Buffer.BlockCopy()
- это когда вам предоставляется массив примитивов (скажем, шорты), и вам нужно преобразовать его в массив байтов (скажем, для передачи по сети). Я часто использую этот метод при работе со звуком из Silverlight AudioSink. Он предоставляет образец в видеshort[]
массива, но вам необходимо преобразовать его вbyte[]
массив при создании пакета, который вы отправляетеSocket.SendAsync()
. Вы можете использоватьBitConverter
и перебирать массив один за другим, но это намного быстрее (примерно в 20 раз в моем тестировании) просто для этого:И тот же трюк работает и в обратном направлении:
Это примерно так же близко, как в безопасном C #, к тому типу
(void *)
управления памятью, который так распространен в C и C ++.источник
MemoryMarshal.AsBytes<T>
илиMemoryMarshal.Cast<TFrom, TTo>
позволить вам интерпретировать вашу последовательность одного примитива как последовательность другого примитива.Судя по моему тестированию, производительность не является причиной предпочесть Buffer.BlockCopy перед Array.Copy. Из моего тестирования Array.Copy на самом деле быстрее, чем Buffer.BlockCopy.
Пример вывода:
источник
ArrayCopy умнее BlockCopy. Он выясняет, как копировать элементы, если источник и место назначения - один и тот же массив.
Если мы заполним массив int значениями 0,1,2,3,4 и применим:
как и ожидалось, мы получаем 0,0,1,2,3.
Попробуйте это с помощью BlockCopy, и мы получим: 0,0,2,3,4. Если я
array[0]=-1
назначу после этого, он станет -1,0,2,3,4, как и ожидалось, но если длина массива четная, например, 6, мы получим -1,256,2,3,4,5. Опасный материал. Не используйте BlockCopy, кроме как для копирования одного массива байтов в другой.Есть еще один случай, когда вы можете использовать только Array.Copy: если размер массива больше 2 ^ 31. Array.Copy имеет перегрузку с
long
параметром размера. В BlockCopy этого нет.источник
Чтобы взвесить этот аргумент, можно легко ввести в заблуждение, если не соблюдать осторожность при создании этого теста. Я написал очень простой тест, чтобы проиллюстрировать это. В моем тесте ниже, если я поменяю порядок своих тестов между запуском Buffer.BlockCopy первым или Array.Copy, тот, который идет первым, почти всегда будет самым медленным (хотя и близким). Это означает, что по ряду причин, по которым я не буду просто запускать тесты несколько раз, например, один за другим не даст точных результатов.
Я прибег к сохранению теста, как есть, с 1000000 попыток каждая для массива из 1000000 последовательных удвоений. Однако затем я игнорирую первые 900000 циклов и усредняю остаток. В этом случае буфер лучше.
https://github.com/chivandikwa/Random-Benchmarks
источник
Просто хочу добавить свой тестовый пример, который снова показывает, что BlockCopy не имеет преимущества «ПРОИЗВОДИТЕЛЬНОСТЬ» по сравнению с Array.Copy. Кажется, что они имеют одинаковую производительность в режиме выпуска на моей машине (для копирования 50 миллионов целых чисел требуется около 66 мсек). В режиме отладки BlockCopy немного быстрее.
источник