Array.Copy e Buffer.BlockCopy fazem a mesma coisa, mas BlockCopy
têm como objetivo a cópia rápida da matriz primitiva no nível de bytes, enquanto Copy
é a implementação de uso geral. Minha pergunta é - em que circunstâncias você deve usar BlockCopy
? Você deve usá-lo a qualquer momento quando estiver copiando matrizes de tipo primitivo ou deve usá-lo apenas se estiver codificando para desempenho? Existe algo inerentemente perigoso sobre o uso Buffer.BlockCopy
excessivo Array.Copy
?
124
Marshal.Copy
:-). Bem, useArray.Copy
para tipos de referência, tipos de valor complexos e, se o tipo não for alterado,Buffer.BlockCopy
para "conversão" entre tipos de valor, matrizes de bytes e magia de bytes. F.ex. a combinação comStructLayout
é bastante poderosa se você souber o que está fazendo. Quanto ao desempenho, parece que uma chamada não gerenciada paramemcpy
/cpblk
é a mais rápida para isso - consulte code4k.blogspot.nl/2010/10/… .byte[]
. Não houve diferença na versão Release. Às vezesArray.Copy
, às vezesBuffer.BlockCopy
(um pouco) mais rápido.Array.Copy
é uma versão especializada - por exemplo, ele pode copiar apenas as mesmas matrizes de classificação.Respostas:
Como os parâmetros
Buffer.BlockCopy
são baseados em bytes e não em índice, é mais provável que você estrague seu código do que se você usarArray.Copy
, então eu usaria apenasBuffer.BlockCopy
em uma seção crítica do desempenho do meu código.fonte
UInt16
são dois bytes por elemento. Se você passar essa matriz para o BlockCopy junto com o número de elementos na matriz, é claro que apenas metade da matriz será copiada. Para que isso funcione corretamente, você precisará passar o número de elementos vezes o tamanho de cada elemento (2) como parâmetro de comprimento. msdn.microsoft.com/en-us/library/… e procureINT_SIZE
nos exemplos.Prelúdio
Estou entrando na festa tarde, mas com 32 mil visualizações, vale a pena fazer isso direito. A maioria do código de marca de microbench nas respostas postadas até agora sofre de uma ou mais falhas técnicas graves, incluindo não mover alocações de memória para fora dos loops de teste (que introduz artefatos graves de GC), não testar fluxos de execução variáveis versus determinísticos, aquecimento do JIT, e não rastrear a variabilidade intra-teste. Além disso, a maioria das respostas não testou os efeitos de tamanhos variados de buffer e tipos primitivos variados (em relação aos sistemas de 32 ou 64 bits). Para abordar essa questão de maneira mais abrangente, vinculei-a a uma estrutura de microbenchmarking personalizada que desenvolvi que reduz a maior parte das "dicas" comuns na medida do possível. Os testes foram executados no modo de versão .NET 4.0 em máquinas de 32 bits e de 64 bits. A média dos resultados foi de mais de 20 execuções de teste, nas quais cada execução teve 1 milhão de tentativas por método. Os tipos primitivos testados foram
byte
(1 byte),int
(4 bytes) edouble
(8 bytes). Três métodos foram testados:Array.Copy()
,Buffer.BlockCopy()
, e simples de atribuição num ciclo per-índice. Os dados são muito volumosos para serem postados aqui, então vou resumir os pontos importantes.The Takeaways
Array.Copy()
ouBuffer.BlockCopy()
para todos os 3 tipos de primitivas testadas em ambas as máquinas de 32 bits e de 64 bits. Além disso, a rotina de cópia de loop explícita tem uma variação notavelmente menor no desempenho em comparação com as duas alternativas. O bom desempenho é quase certamente devido à localidade de referência explorada pelo cache de memória CPU L1 / L2 / L3 em conjunto com nenhuma sobrecarga de chamada de método.double
buffers em máquinas de 32 bits : A rotina de cópia de loop explícita é melhor que as duas alternativas para todos os tamanhos de buffer testados até 100k. A melhoria é 3-5% melhor que os outros métodos. Isso ocorre porque o desempenhoArray.Copy()
eBuffer.BlockCopy()
fica totalmente degradado ao passar a largura nativa de 32 bits. Portanto, presumo que o mesmo efeito se aplicaria aoslong
buffers também.byte[]
que a cópia explícita de loop pode se tornar 7x ou mais lenta em tamanhos de buffer grandes.Array.Copy()
eBuffer.BlockCopy()
executados quase de forma idêntica. Em média,Array.Copy()
parece ter uma margem muito pequena de cerca de 2% ou menos do tempo gasto (mas é típico de 0,2% a 0,5% melhor), emboraBuffer.BlockCopy()
ocasionalmente o superasse. Por razões desconhecidas,Buffer.BlockCopy()
apresenta uma variabilidade intra-teste notavelmente maior queArray.Copy()
. Esse efeito não pôde ser eliminado, apesar de eu tentar várias mitigações e não ter uma teoria operacional sobre o porquê.Array.Copy()
ser um método "mais inteligente", mais geral e muito mais seguro, além de ser um pouco mais rápido e com menor variabilidade em média, deve ser preferidoBuffer.BlockCopy()
em quase todos os casos comuns. O único caso de uso emBuffer.BlockCopy()
que será significativamente melhor é quando os tipos de valores da matriz de origem e de destino são diferentes (como apontado na resposta de Ken Smith). Embora esse cenário não seja comum, eleArray.Copy()
pode ter um desempenho muito ruim aqui devido à conversão contínua do tipo de valor "seguro", em comparação à conversão direta deBuffer.BlockCopy()
.Array.Copy()
é mais rápida do queBuffer.BlockCopy()
a cópia de matriz do mesmo tipo, pode ser encontrada aqui .fonte
Array.Clear()
primeiro começa a bater uma compensação atribuição de loop explícita de uma matriz (configuração parafalse
,0
, ounull
). Isso é consistente com minhas descobertas semelhantes acima. Esses benchmarks separados foram descobertos on-line aqui: 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
No entanto, se o tamanho da cópia for> ~ 20 bytes, o loop explícito será significativamente mais lento.Outro exemplo de quando faz sentido usar
Buffer.BlockCopy()
é quando você recebe uma matriz de primitivas (digamos, shorts) e precisa convertê-lo em uma matriz de bytes (digamos, para transmissão em uma rede). Uso esse método frequentemente ao lidar com áudio do Silverlight AudioSink. Ele fornece a amostra como umashort[]
matriz, mas você precisa convertê-la em umabyte[]
matriz ao criar o pacote ao qual você enviaSocket.SendAsync()
. Você pode usarBitConverter
e percorrer a matriz um por um, mas é muito mais rápido (cerca de 20x nos meus testes) apenas para fazer isso:E o mesmo truque também funciona ao contrário:
Isso é o mais próximo que você obtém do C # seguro do
(void *)
tipo de gerenciamento de memória tão comum em C e C ++.fonte
MemoryMarshal.AsBytes<T>
ouMemoryMarshal.Cast<TFrom, TTo>
permita que você interprete sua sequência de um primitivo como um sequeno de outro primitivo.Com base nos meus testes, o desempenho não é um motivo para preferir Buffer.BlockCopy sobre Array.Copy. Do meu teste Array.Copy é realmente mais rápido que Buffer.BlockCopy.
Saída de exemplo:
fonte
ArrayCopy é mais inteligente que BlockCopy. Ele descobre como copiar elementos se a origem e o destino forem da mesma matriz.
Se preenchermos uma matriz int com 0,1,2,3,4 e aplicamos:
terminamos com 0,0,1,2,3 conforme o esperado.
Tente isso com o BlockCopy e obteremos: 0,0,2,3,4. Se eu atribuir
array[0]=-1
depois disso, ele se tornará -1,0,2,3,4 conforme o esperado, mas se o comprimento da matriz for par, como 6, obteremos -1,256,2,3,4,5. Coisas perigosas. Não use o BlockCopy além de copiar uma matriz de bytes para outra.Há outro caso em que você só pode usar Array.Copy: se o tamanho da matriz for maior que 2 ^ 31. Array.Copy tem uma sobrecarga com um
long
parâmetro de tamanho. BlockCopy não tem isso.fonte
Para refletir sobre esse argumento, se alguém não tomar cuidado com a forma como cria esse benchmark, pode ser facilmente enganado. Eu escrevi um teste muito simples para ilustrar isso. No meu teste abaixo, se eu trocar a ordem dos meus testes entre iniciar o Buffer.BlockCopy primeiro ou Array.Copy, o que for primeiro é quase sempre o mais lento (embora seja próximo). Isso significa que, por várias razões em que não vou simplesmente executar os testes várias vezes, um após o outro não fornecerá resultados precisos.
Eu tentei manter o teste como está com 1000000 tentativas cada uma para uma matriz de 1000000 duplas seqüenciais. No entanto, desconsiderei os primeiros 900000 ciclos e calculei a média do restante. Nesse caso, o buffer é superior.
https://github.com/chivandikwa/Random-Benchmarks
fonte
Só quero adicionar meu caso de teste, que mostra novamente que o BlockCopy não possui o benefício 'DESEMPENHO' sobre o Array.Copy. Eles parecem ter o mesmo desempenho no modo de liberação na minha máquina (ambos levam cerca de 66ms para copiar 50 milhões de números inteiros). No modo de depuração, o BlockCopy é apenas um pouco mais rápido.
fonte