¿Alguna forma más rápida de copiar arreglos en C #?

Tengo tres arreglos que deben combinarse en una matriz tridimensional. El siguiente código muestra el rendimiento lento en el Explorador de rendimiento. ¿Hay una solución más rápida?

for (int i = 0; i < sortedIndex.Length; i++) { if (i < num_in_left) { // add instance to the left child leftnode[i, 0] = sortedIndex[i]; leftnode[i, 1] = sortedInstances[i]; leftnode[i, 2] = sortedLabels[i]; } else { // add instance to the right child rightnode[i-num_in_left, 0] = sortedIndex[i]; rightnode[i-num_in_left, 1] = sortedInstances[i]; rightnode[i-num_in_left, 2] = sortedLabels[i]; } } 

Actualizar:

En realidad estoy tratando de hacer lo siguiente:

 //given three 1d arrays double[] sortedIndex, sortedInstances, sortedLabels; // copy them over to a 3d array (forget about the rightnode for now) double[] leftnode = new double[sortedIndex.Length, 3]; // some magic happens here so that leftnode = {sortedIndex, sortedInstances, sortedLabels}; 

Utilice Buffer.BlockCopy . Todo su propósito es realizar rápido (ver Buffer ):

Esta clase proporciona un mejor rendimiento para manipular tipos primitivos que métodos similares en la clase System.Array.

Es cierto que no he hecho ningún punto de referencia, pero esa es la documentación. También funciona en matrices multidimensionales; solo asegúrese de especificar siempre cuántos bytes copiar, no cuántos elementos, y también que está trabajando en una matriz primitiva.

Además, no he probado esto, pero es posible que pueda exprimir un poco más el rendimiento del sistema si vincula un delegado a System.Buffer.memcpyimpl y lo llama directamente. La firma es:

 internal static unsafe void memcpyimpl(byte* src, byte* dest, int len) 

Requiere punteros, pero creo que está optimizado para la mayor velocidad posible, por lo que no creo que haya forma de llegar más rápido que eso, incluso si tuviera el ensamblaje a mano.


Actualización :

Debido a las solicitudes (y para satisfacer mi curiosidad), probé esto:

 using System; using System.Diagnostics; using System.Reflection; unsafe delegate void MemCpyImpl(byte* src, byte* dest, int len); static class Temp { //There really should be a generic CreateDelegate() method... -___- static MemCpyImpl memcpyimpl = (MemCpyImpl)Delegate.CreateDelegate( typeof(MemCpyImpl), typeof(Buffer).GetMethod("memcpyimpl", BindingFlags.Static | BindingFlags.NonPublic)); const int COUNT = 32, SIZE = 32 << 20; //Use different buffers to help avoid CPU cache effects static byte[] aSource = new byte[SIZE], aTarget = new byte[SIZE], bSource = new byte[SIZE], bTarget = new byte[SIZE], cSource = new byte[SIZE], cTarget = new byte[SIZE]; static unsafe void TestUnsafe() { Stopwatch sw = Stopwatch.StartNew(); fixed (byte* pSrc = aSource) fixed (byte* pDest = aTarget) for (int i = 0; i < COUNT; i++) memcpyimpl(pSrc, pDest, SIZE); sw.Stop(); Console.WriteLine("Buffer.memcpyimpl: {0:N0} ticks", sw.ElapsedTicks); } static void TestBlockCopy() { Stopwatch sw = Stopwatch.StartNew(); sw.Start(); for (int i = 0; i < COUNT; i++) Buffer.BlockCopy(bSource, 0, bTarget, 0, SIZE); sw.Stop(); Console.WriteLine("Buffer.BlockCopy: {0:N0} ticks", sw.ElapsedTicks); } static void TestArrayCopy() { Stopwatch sw = Stopwatch.StartNew(); sw.Start(); for (int i = 0; i < COUNT; i++) Array.Copy(cSource, 0, cTarget, 0, SIZE); sw.Stop(); Console.WriteLine("Array.Copy: {0:N0} ticks", sw.ElapsedTicks); } static void Main(string[] args) { for (int i = 0; i < 10; i++) { TestArrayCopy(); TestBlockCopy(); TestUnsafe(); Console.WriteLine(); } } } 

Los resultados:

 Buffer.BlockCopy: 469,151 ticks Array.Copy: 469,972 ticks Buffer.memcpyimpl: 496,541 ticks Buffer.BlockCopy: 421,011 ticks Array.Copy: 430,694 ticks Buffer.memcpyimpl: 410,933 ticks Buffer.BlockCopy: 425,112 ticks Array.Copy: 420,839 ticks Buffer.memcpyimpl: 411,520 ticks Buffer.BlockCopy: 424,329 ticks Array.Copy: 420,288 ticks Buffer.memcpyimpl: 405,598 ticks Buffer.BlockCopy: 422,410 ticks Array.Copy: 427,826 ticks Buffer.memcpyimpl: 414,394 ticks 

Ahora cambia el orden:

 Array.Copy: 419,750 ticks Buffer.memcpyimpl: 408,919 ticks Buffer.BlockCopy: 419,774 ticks Array.Copy: 430,529 ticks Buffer.memcpyimpl: 412,148 ticks Buffer.BlockCopy: 424,900 ticks Array.Copy: 424,706 ticks Buffer.memcpyimpl: 427,861 ticks Buffer.BlockCopy: 421,929 ticks Array.Copy: 420,556 ticks Buffer.memcpyimpl: 421,541 ticks Buffer.BlockCopy: 436,430 ticks Array.Copy: 435,297 ticks Buffer.memcpyimpl: 432,505 ticks Buffer.BlockCopy: 441,493 ticks 

Ahora cambie el orden nuevamente:

 Buffer.memcpyimpl: 430,874 ticks Buffer.BlockCopy: 429,730 ticks Array.Copy: 432,746 ticks Buffer.memcpyimpl: 415,943 ticks Buffer.BlockCopy: 423,809 ticks Array.Copy: 428,703 ticks Buffer.memcpyimpl: 421,270 ticks Buffer.BlockCopy: 428,262 ticks Array.Copy: 434,940 ticks Buffer.memcpyimpl: 423,506 ticks Buffer.BlockCopy: 427,220 ticks Array.Copy: 431,606 ticks Buffer.memcpyimpl: 422,900 ticks Buffer.BlockCopy: 439,280 ticks Array.Copy: 432,649 ticks 

o, en otras palabras: son muy competitivos; como regla general, memcpyimpl es el más rápido, pero no necesariamente vale la pena preocuparse.

Puedes usar Array.Copy .

EDITAR

Array.Copy funciona para arreglos multidimensionales: vea este tema .

Para las matrices de tipo primitivo (como el double ) puede copiar rápidamente, incluso para una matriz multidimensional con punteros.

En el siguiente código inicializo una matriz 2D A[10,10] con los valores 1 a 100. Luego copio estos valores en una matriz 1D B[100]

 unsafe class Program { static void Main(string[] args) { double[,] A = new double[10, 10]; for(int i = 0; i < 10; i++) { for(int j = 0; j < 10; j++) { A[i, j] = 10 * i + j + 1; } } // A has { { 1 ,2 .. 10}, { 11, 12 .. 20}, .. { .. 99, 100} } double[] B = new double[10 * 10]; if (A.Length == B.Length) { fixed (double* pA = A, pB = B) { for(int i = 0; i < B.Length; i++) { pB[i] = pA[i]; } } // B has {1, 2, 3, 4 .. 100} } } } 

Que tan rapido es Mis pruebas han demostrado que es mucho más rápido que la copia nativa de C # y Buffer.BlockCopy() . Pruébalo para tu caso y háznoslo saber.

Editar 1 comparé la copia con cuatro métodos. 1) Dos bucles nesteds, 2) Un bucle en serie, 3) Punteros, 4) BlockCopy. Medí el número de copias por tick para varias matrices de tamaño.

 N = 10x 10 (cpy/tck) Nested = 50, Serial = 33, Pointer = 100, Buffer = 16 N = 20x 20 (cpy/tck) Nested = 133, Serial = 40, Pointer = 400, Buffer = 400 N = 50x 50 (cpy/tck) Nested = 104, Serial = 40, Pointer = 2500, Buffer = 2500 N = 100x 100 (cpy/tck) Nested = 61, Serial = 41, Pointer = 10000, Buffer = 3333 N = 200x 200 (cpy/tck) Nested = 84, Serial = 41, Pointer = 40000, Buffer = 2666 N = 500x 500 (cpy/tck) Nested = 69, Serial = 41, Pointer = 125000, Buffer = 2840 N = 1000x1000 (cpy/tck) Nested = 33, Serial = 45, Pointer = 142857, Buffer = 1890 N = 2000x2000 (cpy/tck) Nested = 30, Serial = 43, Pointer = 266666, Buffer = 1826 N = 5000x5000 (cpy/tck) Nested = 21, Serial = 42, Pointer = 735294, Buffer = 1712 

Aquí está claro quién es el ganador. La copia del puntero es órdenes de magnitudes mejor que cualquier otro método.

Editar 2 Aparentemente estaba aprovechando injustamente una optimización de comstackdor / JIT porque cuando moví los bucles detrás de los delegates para igualar el campo de juego, los números cambiaron drásticamente.

 N = 10x 10 (cpy/tck) Nested = 0, Serial = 0, Pointer = 0, Buffer = 0 N = 20x 20 (cpy/tck) Nested = 80, Serial = 14, Pointer = 100, Buffer = 133 N = 50x 50 (cpy/tck) Nested =147, Serial = 15, Pointer = 277, Buffer = 2500 N = 100x 100 (cpy/tck) Nested = 98, Serial = 15, Pointer = 285, Buffer = 3333 N = 200x 200 (cpy/tck) Nested =106, Serial = 15, Pointer = 272, Buffer = 3076 N = 500x 500 (cpy/tck) Nested =106, Serial = 15, Pointer = 276, Buffer = 3125 N = 1000x1000 (cpy/tck) Nested =101, Serial = 11, Pointer = 199, Buffer = 1396 N = 2000x2000 (cpy/tck) Nested =105, Serial = 9, Pointer = 186, Buffer = 1804 N = 5000x5000 (cpy/tck) Nested =102, Serial = 8, Pointer = 170, Buffer = 1673 

La copia en búfer es superior aquí (gracias a @Mehrdad) con copia de puntero en segundo lugar. La pregunta ahora es ¿por qué la copia del puntero no es tan rápida como los métodos del buffer?