【问题标题】:Cloning jagged array is slower than multidimensional array克隆锯齿状数组比多维数组慢
【发布时间】:2018-11-04 15:45:19
【问题描述】:

我正在尝试克隆一个大小约为 100x100 的多维数组,我发现的每个资源似乎都表明使用锯齿状数组应该更有效(至少在查找中,因为多维数组使用函数调用) .

但我的问题是我对这些数组进行了很多克隆,而且克隆锯齿状数组(在循环中分配新数组)似乎比仅执行 multArray.Clone() 慢得多。

示例代码:

class Program
    {
        static void Main(string[] args)
        {
            int matSize = 100;
            int iterations = 100000;
            Random r = new Random();
            Stopwatch sw = new Stopwatch();
            double[][] jaggedMat = new double[matSize][];
            double[,] multiMat = new double[matSize, matSize];
            for(int i = 0; i < matSize; i++)
            {
                jaggedMat[i] = new double[matSize];
                for (int j = 0; j < matSize; j++)
                {
                    double v = r.NextDouble();
                    jaggedMat[i][j] = v;
                    multiMat[i, j] = v;
                }
            }
            Console.WriteLine($"Cloning jagged matrix old school.");
            sw.Start();
            for (int i=0; i<iterations; i++)
            {
                double[][] copy = new double[matSize][];
                for(int j = 0; j <matSize; j++)
                {
                    copy[j] = new double[matSize];
                    for (int k = 0; k < matSize; k++)
                    {
                        copy[j][k] = jaggedMat[j][k];
                    }
                }
            }
            sw.Stop();
            Console.WriteLine($"Cloning took {sw.ElapsedMilliseconds}ms");
            Console.WriteLine($"Cloning using LINQ");
            sw.Reset();
            sw.Start();
            for (int i = 0; i < iterations; i++)
            {
                var clone = jaggedMat.Select(element => element.ToArray()).ToArray();
            }
            sw.Stop();
            Console.WriteLine($"Cloning took {sw.ElapsedMilliseconds}ms");
            Console.WriteLine($"Cloning multidimensional array.");
            sw.Reset();
            sw.Start();
            for(int i = 0; i < iterations; i++)
            {
                var clone = multiMat.Clone() as double[,];
            }
            sw.Stop();
            Console.WriteLine($"Cloning took {sw.ElapsedMilliseconds}ms");
            Console.ReadKey();
        }
    }

这会在我的计算机上产生以下输出:

克隆锯齿状矩阵老派。 克隆耗时 4913ms 使用 LINQ 进行克隆 克隆耗时 2283ms 克隆多维数组。 克隆耗时 712ms

如您所见,在相同大小的矩阵上执行 .Clone() 比实际分配新的锯齿状数组快约 3 倍。

有人知道是否有更快的方法来克隆锯齿状数组吗?

【问题讨论】:

  • 我希望您在发布模式下运行测试,并且没有调试器(CTRL+F5 或直接在命令行中)...而且我经常在测试开始时添加Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High; , 以提高程序在运行期间的优先级
  • 我会说克隆锯齿状数组应该更慢。请注意,您可以半克隆锯齿状数组(克隆每一行):copy[j] = jagged[j].Clone as double[]
  • 其实我没有,从命令行运行测试产生了类似的结果:克隆锯齿状矩阵老派。克隆需要 3801 毫秒 使用 LINQ 克隆需要 1600 毫秒 克隆多维数组。克隆耗时 484 毫秒
  • 在 Core i7 上使用 Benchmark.NET,10,000 次迭代得到以下结果:OldSchool:140.38 ms,Linq:151.74 ms,多:47.5 毫秒
  • 您基本上发现 Array.Clone() 在 CLR 内部进行了微优化,它可以使用原始 memcpy 来完成,而无需进行边界检查。你永远无法击败它。这也适用于锯齿状数组,但它的惊人速度可以帮助您发现 Clone() 的问题,它不是深拷贝。您通常希望避免索引 copy[j] 和 jaggedMat[j],它们是循环不变的。然而,抖动优化器可以自己解决这个问题。

标签: c# .net multidimensional-array jagged-arrays


【解决方案1】:

我没有测量性能。但是你可以用更少的代码更简单地解决这个问题。我的解决方案对整个数组进行序列化和反序列化以获取它的深层副本。

using System.Runtime.Serialization.Formatters.Binary;
BinaryFormatter formatter = new BinaryFormatter();
using(var ms = new MemoryStream())
{
   var array = new int[100, 100];
   array [0, 1] = 57; // simple test data to validate the output.
   formatter.Serialize(ms, array );
   ms.Position = 0; // rewind the stream to deserialize it.
   var copied = formatter.Deserialize(ms);
}

【讨论】:

  • 我会尝试,但请注意,您将多维数组称为“jaggedArray”,而实际上它不是。
  • 是的,这是真的。我的解决方案适用于任何可序列化的对象。
  • 如果有人感兴趣,您的方法会产生与遍历数组类似的结果。请注意,我序列化了一个锯齿状数组而不是一个多维数组:Cloning jagged with memorystream。克隆耗时 4839 毫秒 使用 LINQ 克隆耗时 2127 毫秒 克隆多维数组。克隆耗时 536 毫秒
【解决方案2】:

这样做:

double[][] copy = new double[matSize][];
for (int j = 0; j < matSize; j++)
{
    copy[j] = jaggedMat[j].Clone() as double[];
}

它只会使锯齿状数组比多维数组慢 2 倍(在我的机器上 2450 毫秒对 1360 毫秒)。简单地说,创建 100 个对象(锯齿状数组的线)是有代价的。 GC 会恨你一点 :-) 如果 GC 运行,所有这些对象都必须被分配然后释放。这会使锯齿状阵列的克隆速度变慢。我会说成本如此之慢很有趣(似乎创建一个数组与填充它的成本相同,因为多维克隆是纯填充,而锯齿状数组克隆是一半复制一半创建数组)

【讨论】:

  • 感谢您的回答,但如果它仍然比克隆多维数组慢 2 倍,它并没有真正帮助我。
  • @taracus 您的测试是 3.5 倍...我已经降低了它 :-) 有时您需要在速度和代码可维护性之间做出妥协。 .NET 几乎不支持多维数组(没有 LINQ,...)。我不认为你可以加快速度。此时的问题是您要分配 100 个对象。这是有代价的。
  • 我可以理解分配100个对象是有代价的,但是为什么多维分配不付出相同的代价?
  • @taracus 因为只有一个对象。计算您必须使用的new 的数量来创建锯齿状数组与创建多维数组。每个new 都是一个分配
猜你喜欢
  • 1970-01-01
  • 2015-11-13
  • 1970-01-01
  • 2020-01-27
  • 1970-01-01
  • 1970-01-01
  • 2013-02-24
  • 2018-07-26
  • 1970-01-01
相关资源
最近更新 更多