猿问

有更快的方法在C#中复制数组吗?

我有三个数组,需要组合成一个三维数组。以下代码显示了Performance Explorer中的性能下降。有更快的解决方案吗?


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];

    }                    

}

更新:


我实际上正在尝试执行以下操作:


//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};


UYOU
浏览 927回答 3
3回答

蝴蝶刀刀

使用Buffer.BlockCopy。它的全部目的是快速执行(请参见Buffer):与System.Array类中的类似方法相比,此类提供了更好的操作原始类型的性能。诚然,我还没有做过任何基准测试,但这就是文档。它也适用于多维数组。只需确保始终指定要复制的字节数,而不是指定多少个元素,并且确保您正在处理原始数组。另外,我还没有对此进行测试,但是如果将委托绑定到并直接调用它,则可能会使系统的性能降低System.Buffer.memcpyimpl一点。签名是:internal static unsafe void memcpyimpl(byte* src, byte* dest, int len)它确实需要指针,但是我相信它已针对可能的最高速度进行了优化,因此,即使您手头有组装,我也认为没有任何方法可以使速度更快。更新:由于要求(并满足我的好奇心),我对此进行了测试:using System;using System.Diagnostics;using System.Reflection;unsafe delegate void MemCpyImpl(byte* src, byte* dest, int len);static class Temp{&nbsp; &nbsp; //There really should be a generic CreateDelegate<T>() method... -___-&nbsp; &nbsp; static MemCpyImpl memcpyimpl = (MemCpyImpl)Delegate.CreateDelegate(&nbsp; &nbsp; &nbsp; &nbsp; typeof(MemCpyImpl), typeof(Buffer).GetMethod("memcpyimpl",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; BindingFlags.Static | BindingFlags.NonPublic));&nbsp; &nbsp; const int COUNT = 32, SIZE = 32 << 20;&nbsp; &nbsp; //Use different buffers to help avoid CPU cache effects&nbsp; &nbsp; static byte[]&nbsp; &nbsp; &nbsp; &nbsp; aSource = new byte[SIZE], aTarget = new byte[SIZE],&nbsp; &nbsp; &nbsp; &nbsp; bSource = new byte[SIZE], bTarget = new byte[SIZE],&nbsp; &nbsp; &nbsp; &nbsp; cSource = new byte[SIZE], cTarget = new byte[SIZE];&nbsp; &nbsp; static unsafe void TestUnsafe()&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; Stopwatch sw = Stopwatch.StartNew();&nbsp; &nbsp; &nbsp; &nbsp; fixed (byte* pSrc = aSource)&nbsp; &nbsp; &nbsp; &nbsp; fixed (byte* pDest = aTarget)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; for (int i = 0; i < COUNT; i++)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; memcpyimpl(pSrc, pDest, SIZE);&nbsp; &nbsp; &nbsp; &nbsp; sw.Stop();&nbsp; &nbsp; &nbsp; &nbsp; Console.WriteLine("Buffer.memcpyimpl: {0:N0} ticks", sw.ElapsedTicks);&nbsp; &nbsp; }&nbsp; &nbsp; static void TestBlockCopy()&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; Stopwatch sw = Stopwatch.StartNew();&nbsp; &nbsp; &nbsp; &nbsp; sw.Start();&nbsp; &nbsp; &nbsp; &nbsp; for (int i = 0; i < COUNT; i++)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Buffer.BlockCopy(bSource, 0, bTarget, 0, SIZE);&nbsp; &nbsp; &nbsp; &nbsp; sw.Stop();&nbsp; &nbsp; &nbsp; &nbsp; Console.WriteLine("Buffer.BlockCopy: {0:N0} ticks",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sw.ElapsedTicks);&nbsp; &nbsp; }&nbsp; &nbsp; static void TestArrayCopy()&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; Stopwatch sw = Stopwatch.StartNew();&nbsp; &nbsp; &nbsp; &nbsp; sw.Start();&nbsp; &nbsp; &nbsp; &nbsp; for (int i = 0; i < COUNT; i++)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Array.Copy(cSource, 0, cTarget, 0, SIZE);&nbsp; &nbsp; &nbsp; &nbsp; sw.Stop();&nbsp; &nbsp; &nbsp; &nbsp; Console.WriteLine("Array.Copy: {0:N0} ticks", sw.ElapsedTicks);&nbsp; &nbsp; }&nbsp; &nbsp; static void Main(string[] args)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; for (int i = 0; i < 10; i++)&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; TestArrayCopy();&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; TestBlockCopy();&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; TestUnsafe();&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Console.WriteLine();&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }}结果:Buffer.BlockCopy: 469,151 ticksArray.Copy: 469,972 ticksBuffer.memcpyimpl: 496,541 ticksBuffer.BlockCopy: 421,011 ticksArray.Copy: 430,694 ticksBuffer.memcpyimpl: 410,933 ticksBuffer.BlockCopy: 425,112 ticksArray.Copy: 420,839 ticksBuffer.memcpyimpl: 411,520 ticksBuffer.BlockCopy: 424,329 ticksArray.Copy: 420,288 ticksBuffer.memcpyimpl: 405,598 ticksBuffer.BlockCopy: 422,410 ticksArray.Copy: 427,826 ticksBuffer.memcpyimpl: 414,394 ticks现在更改顺序:Array.Copy: 419,750 ticksBuffer.memcpyimpl: 408,919 ticksBuffer.BlockCopy: 419,774 ticksArray.Copy: 430,529 ticksBuffer.memcpyimpl: 412,148 ticksBuffer.BlockCopy: 424,900 ticksArray.Copy: 424,706 ticksBuffer.memcpyimpl: 427,861 ticksBuffer.BlockCopy: 421,929 ticksArray.Copy: 420,556 ticksBuffer.memcpyimpl: 421,541 ticksBuffer.BlockCopy: 436,430 ticksArray.Copy: 435,297 ticksBuffer.memcpyimpl: 432,505 ticksBuffer.BlockCopy: 441,493 ticks现在再次更改顺序:Buffer.memcpyimpl: 430,874 ticksBuffer.BlockCopy: 429,730 ticksArray.Copy: 432,746 ticksBuffer.memcpyimpl: 415,943 ticksBuffer.BlockCopy: 423,809 ticksArray.Copy: 428,703 ticksBuffer.memcpyimpl: 421,270 ticksBuffer.BlockCopy: 428,262 ticksArray.Copy: 434,940 ticksBuffer.memcpyimpl: 423,506 ticksBuffer.BlockCopy: 427,220 ticksArray.Copy: 431,606 ticksBuffer.memcpyimpl: 422,900 ticksBuffer.BlockCopy: 439,280 ticksArray.Copy: 432,649 ticks或者换句话说:他们很有竞争力;一般而言,memcpyimpl速度最快,但不必担心。

至尊宝的传说

对于基本类型的数组(如double),即使对于带有指针的多维数组,也可以快速复制。在下面的代码中,我初始化一个2D数组A[10,10],其值是1到100。然后将这些值复制到1D数组中。B[100]unsafe class Program{&nbsp;&nbsp; &nbsp; static void Main(string[] args)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; double[,] A = new double[10, 10];&nbsp; &nbsp; &nbsp; &nbsp; for(int i = 0; i < 10; i++)&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; for(int j = 0; j < 10; j++)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; A[i, j] = 10 * i + j + 1;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; // A has { { 1 ,2 .. 10}, { 11, 12 .. 20}, .. { .. 99, 100} }&nbsp; &nbsp; &nbsp; &nbsp; double[] B = new double[10 * 10];&nbsp; &nbsp; &nbsp; &nbsp; if (A.Length == B.Length)&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fixed (double* pA = A, pB = B)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; for(int i = 0; i < B.Length; i++)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pB[i] = pA[i];&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // B has {1, 2, 3, 4 .. 100}&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }}有多快 我的测试表明,它比本机C#复制和复制要快很多倍Buffer.BlockCopy()。您可以根据自己的情况尝试一下,并告诉我们。编辑1 我比较了复制和四种方法。1)两个嵌套循环,2)一个串行循环,3)指针,4)BlockCopy。我测量了各种尺寸阵列的每刻度的副本数。N =&nbsp; &nbsp;10x&nbsp; 10 (cpy/tck) Nested = 50,&nbsp; Serial = 33, Pointer =&nbsp; &nbsp; 100, Buffer =&nbsp; &nbsp; 16N =&nbsp; &nbsp;20x&nbsp; 20 (cpy/tck) Nested = 133, Serial = 40, Pointer =&nbsp; &nbsp; 400, Buffer =&nbsp; &nbsp;400N =&nbsp; &nbsp;50x&nbsp; 50 (cpy/tck) Nested = 104, Serial = 40, Pointer =&nbsp; &nbsp;2500, Buffer =&nbsp; 2500N =&nbsp; 100x 100 (cpy/tck) Nested = 61,&nbsp; Serial = 41, Pointer =&nbsp; 10000, Buffer =&nbsp; 3333N =&nbsp; 200x 200 (cpy/tck) Nested = 84,&nbsp; Serial = 41, Pointer =&nbsp; 40000, Buffer =&nbsp; 2666N =&nbsp; 500x 500 (cpy/tck) Nested = 69,&nbsp; Serial = 41, Pointer = 125000, Buffer =&nbsp; 2840N = 1000x1000 (cpy/tck) Nested = 33,&nbsp; Serial = 45, Pointer = 142857, Buffer =&nbsp; 1890N = 2000x2000 (cpy/tck) Nested = 30,&nbsp; Serial = 43, Pointer = 266666, Buffer =&nbsp; 1826N = 5000x5000 (cpy/tck) Nested = 21,&nbsp; Serial = 42, Pointer = 735294, Buffer =&nbsp; 1712很明显,谁是赢家。指针复制比任何其他方法好几个数量级。编辑2 显然,我不公平地利用了编译器/ JIT优化,因为当我将循环移动到代表后面以平衡竞争环境时,数字发生了巨大变化。N =&nbsp; &nbsp;10x&nbsp; 10 (cpy/tck) Nested =&nbsp; 0, Serial =&nbsp; 0, Pointer =&nbsp; &nbsp; &nbsp; 0, Buffer =&nbsp; &nbsp; &nbsp;0N =&nbsp; &nbsp;20x&nbsp; 20 (cpy/tck) Nested = 80, Serial = 14, Pointer =&nbsp; &nbsp; 100, Buffer =&nbsp; &nbsp;133N =&nbsp; &nbsp;50x&nbsp; 50 (cpy/tck) Nested =147, Serial = 15, Pointer =&nbsp; &nbsp; 277, Buffer =&nbsp; 2500N =&nbsp; 100x 100 (cpy/tck) Nested = 98, Serial = 15, Pointer =&nbsp; &nbsp; 285, Buffer =&nbsp; 3333N =&nbsp; 200x 200 (cpy/tck) Nested =106, Serial = 15, Pointer =&nbsp; &nbsp; 272, Buffer =&nbsp; 3076N =&nbsp; 500x 500 (cpy/tck) Nested =106, Serial = 15, Pointer =&nbsp; &nbsp; 276, Buffer =&nbsp; 3125N = 1000x1000 (cpy/tck) Nested =101, Serial = 11, Pointer =&nbsp; &nbsp; 199, Buffer =&nbsp; 1396N = 2000x2000 (cpy/tck) Nested =105, Serial =&nbsp; 9, Pointer =&nbsp; &nbsp; 186, Buffer =&nbsp; 1804N = 5000x5000 (cpy/tck) Nested =102, Serial =&nbsp; 8, Pointer =&nbsp; &nbsp; 170, Buffer =&nbsp; 1673缓冲的副本位于此处的顶部(感谢@Mehrdad),其次是指针副本。现在的问题是,为什么指针复制不如缓冲区方法那么快?

冉冉说

如果在.NET Core上运行,则可以考虑使用source.AsSpan().CopyTo(destination)(但请注意在Mono上)。&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Method |&nbsp; Job | Runtime |&nbsp; &nbsp; &nbsp; Mean |&nbsp; &nbsp; &nbsp;Error |&nbsp; &nbsp; StdDev | Ratio | RatioSD |---------------- |----- |-------- |----------:|----------:|----------:|------:|--------:|&nbsp; &nbsp; &nbsp; &nbsp;ArrayCopy |&nbsp; Clr |&nbsp; &nbsp; &nbsp;Clr |&nbsp; 60.08 ns | 0.8231 ns | 0.7699 ns |&nbsp; 1.00 |&nbsp; &nbsp; 0.00 |&nbsp; &nbsp; &nbsp; &nbsp; SpanCopy |&nbsp; Clr |&nbsp; &nbsp; &nbsp;Clr |&nbsp; 99.31 ns | 0.4895 ns | 0.4339 ns |&nbsp; 1.65 |&nbsp; &nbsp; 0.02 |&nbsp;BufferBlockCopy |&nbsp; Clr |&nbsp; &nbsp; &nbsp;Clr |&nbsp; 61.34 ns | 0.5963 ns | 0.5578 ns |&nbsp; 1.02 |&nbsp; &nbsp; 0.01 |&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; |&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; &nbsp;ArrayCopy | Core |&nbsp; &nbsp; Core |&nbsp; 63.33 ns | 0.6843 ns | 0.6066 ns |&nbsp; 1.00 |&nbsp; &nbsp; 0.00 |&nbsp; &nbsp; &nbsp; &nbsp; SpanCopy | Core |&nbsp; &nbsp; Core |&nbsp; 47.41 ns | 0.5399 ns | 0.5050 ns |&nbsp; 0.75 |&nbsp; &nbsp; 0.01 |&nbsp;BufferBlockCopy | Core |&nbsp; &nbsp; Core |&nbsp; 59.89 ns | 0.4713 ns | 0.3936 ns |&nbsp; 0.94 |&nbsp; &nbsp; 0.01 |&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; |&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; &nbsp;ArrayCopy | Mono |&nbsp; &nbsp; Mono | 149.82 ns | 1.6466 ns | 1.4596 ns |&nbsp; 1.00 |&nbsp; &nbsp; 0.00 |&nbsp; &nbsp; &nbsp; &nbsp; SpanCopy | Mono |&nbsp; &nbsp; Mono | 347.87 ns | 2.0589 ns | 1.9259 ns |&nbsp; 2.32 |&nbsp; &nbsp; 0.02 |&nbsp;BufferBlockCopy | Mono |&nbsp; &nbsp; Mono |&nbsp; 61.52 ns | 1.1691 ns | 1.0364 ns |&nbsp; 0.41 |&nbsp; &nbsp; 0.01 |
随时随地看视频慕课网APP
我要回答