Array.Copy y Buffer.BlockCopy hacen lo mismo, pero BlockCopy
tienen como objetivo la copia rápida de matrices primitivas a nivel de bytes, mientras que Copy
es la implementación de propósito general. Mi pregunta es: ¿bajo qué circunstancias debe usar BlockCopy
? ¿Debería usarlo en cualquier momento cuando esté copiando matrices de tipos primitivos, o solo debe usarlo si está codificando para el rendimiento? ¿Hay algo inherentemente peligroso sobre el uso de Buffer.BlockCopy
más Array.Copy
?
124
Marshal.Copy
:-). Bueno, utilíceloArray.Copy
para tipos de referencia, tipos de valores complejos y, si el tipo no cambia,Buffer.BlockCopy
para "conversión" entre tipos de valores, conjuntos de bytes y magia de bytes. F.ex. la combinación conStructLayout
es bastante poderosa si sabes lo que estás haciendo. En cuanto al rendimiento, parece que una llamada no administrada amemcpy
/cpblk
es la más rápida para eso; consulte code4k.blogspot.nl/2010/10/… .byte[]
. No hubo diferencia en la versión de lanzamiento. AArray.Copy
veces, a vecesBuffer.BlockCopy
(ligeramente) más rápido.Array.Copy
es más bien una versión especializada; por ejemplo, solo puede copiar las mismas matrices de rango.Respuestas:
Dado que los parámetros
Buffer.BlockCopy
están basados en bytes en lugar de estar basados en índices, es más probable que arruine su código que si lo usaArray.Copy
, por lo que solo lo usaríaBuffer.BlockCopy
en una sección crítica de rendimiento de mi código.fuente
UInt16
son dos bytes por elemento. Si pasa esta matriz a BlockCopy junto con el número de elementos en la matriz, por supuesto, solo se copiará la mitad de la matriz. Para que esto funcione correctamente, deberá pasar el número de elementos multiplicado por el tamaño de cada elemento (2) como parámetro de longitud. msdn.microsoft.com/en-us/library/… y busqueINT_SIZE
en los ejemplos.Preludio
Me uniré a la fiesta tarde, pero con 32k vistas, vale la pena hacerlo bien. La mayor parte del código de microbenchmarking en las respuestas publicadas hasta ahora adolece de uno o más defectos técnicos severos, que incluyen no mover las asignaciones de memoria fuera de los bucles de prueba (que introduce artefactos GC severos), no probar flujos de ejecución variables versus deterministas, calentamiento JIT, y no rastrear la variabilidad intratest. Además, la mayoría de las respuestas no probaron los efectos de diferentes tamaños de memoria intermedia y tipos primitivos variables (con respecto a los sistemas de 32 bits o de 64 bits). Para abordar esta cuestión de manera más integral, la conecté a un marco de microbenchmarking personalizado que desarrollé que reduce la mayoría de las "trampas" comunes en la medida de lo posible. Las pruebas se ejecutaron en modo de lanzamiento de .NET 4.0 tanto en una máquina de 32 bits como en una máquina de 64 bits. Los resultados se promediaron en 20 ejecuciones de prueba, en las que cada ejecución tuvo 1 millón de pruebas por método. Los tipos primitivos probados fueron
byte
(1 byte),int
(4 bytes) ydouble
(8 bytes). Se ensayaron tres métodos:Array.Copy()
,Buffer.BlockCopy()
, y simple asignación per-índice en un bucle. Los datos son demasiado voluminosos para publicar aquí, así que resumiré los puntos importantes.Las comida para llevar
Array.Copy()
oBuffer.BlockCopy()
para los 3 tipos primitivos probados en máquinas de 32 bits y 64 bits. Además, la rutina de copia de bucle explícita tiene una variabilidad notablemente menor en el rendimiento en comparación con las dos alternativas. El buen rendimiento se debe casi seguramente a la localidad de referencia explotada por el almacenamiento en memoria caché de la CPU L1 / L2 / L3 junto con la ausencia de sobrecarga de llamadas a métodos.double
búferes en máquinas de 32 bits : la rutina de copia de bucle explícita es mejor que ambas alternativas para todos los tamaños de búfer probados hasta 100k. La mejora es 3-5% mejor que los otros métodos. Esto se debe a que el rendimiento deArray.Copy()
yBuffer.BlockCopy()
se degrada totalmente al pasar el ancho nativo de 32 bits. Por lo tanto, supongo que el mismo efecto se aplicaría a loslong
búferes también.byte[]
cuando la copia explícita en bucle puede ser 7 veces más lenta en tamaños de búfer grandes.Array.Copy()
y seBuffer.BlockCopy()
realizó de forma casi idéntica. En promedio,Array.Copy()
parece tener una ventaja muy leve de aproximadamente 2% o menos tiempo (pero es típico 0.2% - 0.5% mejor), aunque enBuffer.BlockCopy()
ocasiones lo superó. Por razones desconocidas,Buffer.BlockCopy()
tiene una variabilidad intratest significativamente mayor queArray.Copy()
. Este efecto no se pudo eliminar a pesar de que probé mitigaciones múltiples y no tuve una teoría operable sobre por qué.Array.Copy()
es un método "más inteligente", más general y mucho más seguro, además de ser un poco más rápido y tener menos variabilidad en promedio, debería preferirseBuffer.BlockCopy()
en casi todos los casos comunes. El único caso de uso en elBuffer.BlockCopy()
que será significativamente mejor es cuando los tipos de valores de la matriz de origen y destino son diferentes (como se señala en la respuesta de Ken Smith). Si bien este escenario no es común,Array.Copy()
puede funcionar muy mal aquí debido a la conversión continua del tipo de valor "seguro", en comparación con la conversión directa deBuffer.BlockCopy()
.Array.Copy()
es más rápida queBuffer.BlockCopy()
para la copia de matriz del mismo tipo .fuente
Array.Clear()
primero comienza a golpear un claro asignación bucle explícito de una matriz (ajuste afalse
,0
onull
). Esto es consistente con mis hallazgos similares anteriores. Estos puntos de referencia separados se descubrieron en línea aquí: 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
Sin embargo, si el tamaño de la copia> ~ 20 bytes, el bucle explícito es significativamente más lento.Otro ejemplo de cuándo tiene sentido usarlo
Buffer.BlockCopy()
es cuando se le proporciona un conjunto de primitivas (por ejemplo, cortos) y necesita convertirlo en un conjunto de bytes (por ejemplo, para la transmisión a través de una red). Utilizo este método con frecuencia cuando trato con audio de Silverlight AudioSink. Proporciona la muestra como unashort[]
matriz, pero debe convertirla en unabyte[]
matriz cuando está creando el paquete al que se envíaSocket.SendAsync()
. Podrías usarBitConverter
e iterar a través de la matriz uno por uno, pero es mucho más rápido (aproximadamente 20 veces en mis pruebas) solo para hacer esto:Y el mismo truco también funciona a la inversa:
Esto es lo más cercano que puede estar en C # seguro al
(void *)
tipo de administración de memoria que es tan común en C y C ++.fuente
MemoryMarshal.AsBytes<T>
o leMemoryMarshal.Cast<TFrom, TTo>
permite interpretar su secuencia de una primitiva como una secuencia de otra primitiva.Según mis pruebas, el rendimiento no es una razón para preferir Buffer.BlockCopy sobre Array.Copy. De mis pruebas, Array.Copy es en realidad más rápido que Buffer.BlockCopy.
Salida de ejemplo:
fuente
ArrayCopy es más inteligente que BlockCopy. Descubre cómo copiar elementos si el origen y el destino son la misma matriz.
Si llenamos una matriz int con 0,1,2,3,4 y aplicamos:
terminamos con 0,0,1,2,3 como se esperaba.
Pruebe esto con BlockCopy y obtenemos: 0,0,2,3,4. Si asigno
array[0]=-1
después de eso, se convierte en -1,0,2,3,4 como se esperaba, pero si la longitud de la matriz es par, como 6, obtenemos -1,256,2,3,4,5. Cosas peligrosas No use BlockCopy que no sea para copiar una matriz de bytes en otra.Hay otro caso en el que solo puede usar Array.Copy: si el tamaño de la matriz es mayor que 2 ^ 31. Array.Copy tiene una sobrecarga con un
long
parámetro de tamaño. BlockCopy no tiene eso.fuente
Para sopesar este argumento, si uno no tiene cuidado de cómo crean este punto de referencia, podrían ser engañados fácilmente. Escribí una prueba muy simple para ilustrar esto. En mi prueba a continuación, si cambio el orden de mis pruebas entre iniciar Buffer.BlockCopy primero o Array.Copy, el que va primero es casi siempre el más lento (aunque está cerca). Esto significa que por un montón de razones por las que no entraré, simplemente ejecutar las pruebas varias veces, especialmente una tras otra, no dará resultados precisos.
Recurrí a mantener la prueba como está con 1000000 intentos cada uno para una matriz de 1000000 dobles secuenciales. Sin embargo, en este caso, ignoro los primeros 900000 ciclos y promedio el resto. En ese caso, el Buffer es superior.
https://github.com/chivandikwa/Random-Benchmarks
fuente
Solo quiero agregar mi caso de prueba que muestra nuevamente que BlockCopy no tiene ningún beneficio de 'RENDIMIENTO' sobre Array.Copy. Parecen tener el mismo rendimiento en el modo de lanzamiento en mi máquina (ambos tardan unos 66 ms en copiar 50 millones de enteros). En modo de depuración, BlockCopy es solo un poco más rápido.
fuente