【问题标题】:2D Array vs Array of Arrays in tight loop performance C#紧密循环性能中的二维数组与数组数组 C#
【发布时间】:2016-10-09 14:53:20
【问题描述】:

我看了看,没有看到任何东西能完全回答我的问题。

我并不是最擅长创建准确的“现实生活”测试,所以我不确定这是否是问题所在。基本上我想创建一些简单的神经网络来创建Gridworld 的效果。这些神经网络的性能至关重要,我不希望隐藏层成为尽可能多的瓶颈。

我宁愿使用更多内存并更快,所以我选择使用数组而不是列表(因为列表对数组有额外的边界检查)。数组并不总是满的,但是因为 if 语句(检查元素是否为空)直到结束都是相同的,所以可以预测并且根本没有性能下降。

我的问题来自我如何存储数据以供网络处理。我认为由于 2D 数组将所有数据存储在一起,它会更好地缓存并且运行速度更快。但是从我的模拟测试来看,数组数组在这种情况下的表现要好得多。

一些代码:

    private void RunArrayOfArrayTest(float[][] testArray, Data[] data)
    {
        for (int i = 0; i < testArray.Length; i++) {
            for (int j = 0; j < testArray[i].Length; j++) {
                var inputTotal = data[i].bias;

                for (int k = 0; k < data[i].weights.Length; k++) {
                    inputTotal += testArray[i][k];
                }
            }
        }
    }

    private void Run2DArrayTest(float[,] testArray, Data[] data, int maxI, int maxJ)
    {
        for (int i = 0; i < maxI; i++) {
            for (int j = 0; j < maxJ; j++) {
                var inputTotal = data[i].bias;

                for (int k = 0; k < maxJ; k++) {
                    inputTotal += testArray[i, k];
                }
            }
        }
    }

这是定时的两个函数。每个“生物”都有自己的网络(第一个 for 循环),每个网络都有隐藏节点(第二个 for 循环),我需要找到每个输入的权重总和(第三个循环)。在我的测试中,我剥离了它,因此它并不是我在实际代码中所做的,但是发生了相同数量的循环(数据变量将有它自己的二维数组,但我不想可能扭曲结果) .从这里我试图了解哪个更快,令我惊讶的是数组的数组。

开始测试的代码:

        // Array of Array test
        Stopwatch timer = Stopwatch.StartNew();

        RunArrayOfArrayTest(arrayOfArrays, dataArrays);

        timer.Stop();
        Console.WriteLine("Array of Arrays finished in: " + timer.ElapsedTicks);

        // 2D Array test
        timer = Stopwatch.StartNew();

        Run2DArrayTest(array2D, dataArrays, NumberOfNetworks, NumberOfInputNeurons);

        timer.Stop();
        Console.WriteLine("2D Array finished in: " + timer.ElapsedTicks);

只是想展示我是如何测试它的。发布模式下的结果为我提供如下值:

Array of Arrays finished in: 8972
2D Array finished in: 16376

有人可以向我解释我做错了什么吗?为什么在这种情况下数组数组的速度要快这么多?二维数组不是都存储在一起的,这意味着它对缓存更友好吗?

请注意,我确实需要这个速度很快,因为它需要每帧汇总数十万 - 数百万个数字,就像我说的那样,我不希望这是一个问题。我知道这在未来很容易实现多线程,因为每个网络都是完全独立的,甚至每个节点也是完全独立的。

我想最后一个问题,这样的东西可以在 GPU 上运行吗?我认为 GPU 不会为拥有更多数量的输入/隐藏神经元的网络而奋斗。

【问题讨论】:

  • SO Post 中显示的类似内容
  • 干杯谢谢。我查看了一些标题相似的帖子,但它们通常不会像那样表现。 John Leidegren 指出 2D 数组应该更快,但这并不是因为实施不善。你认为如果我创建自己的数据结构,将数据存储为一维数组但允许像二维数组一样访问,它会改变吗?

标签: c# arrays caching optimization


【解决方案1】:

在 CLR 中,有两种不同类型的数组:

  • 向量,它们是从零开始的一维数组
  • 数组,可以有非零基数和多个维度

您的“数组数组”是 CLR 术语中的“向量向量”。

基本上,向量比数组快得多。数组可能会在以后的 CLR 版本中进一步优化,但我怀疑是否会得到与向量相同的爱,因为它们相对很少使用。要使 CLR 数组更快,您无能为力。正如你所说,它们对缓存更友好,但它们有这种 CLR 惩罚。

您已经可以改进您的数组数组代码,但是,每行只执行一次第一个索引操作:

private void RunArrayOfArrayTest(float[][] testArray, Data[] data)
{
    for (int i = 0; i < testArray.Length; i++) {

        // These don't change in the loop below, so extract them
        var row = testArray[i];            
        var inputTotal = data[i].bias;
        var weightLength = data[i].weights.Length;
        for (int j = 0; j < row.Length; j++) {
            for (int k = 0; k < weightLength; k++) {
                inputTotal += row[k];
            }
        }
    }
}

如果你想获得缓存友好性并且仍然使用向量,你可以有一个 single float[] 并自己执行索引......但我可能会从数组开始-of-arrays 方法。

【讨论】:

  • 感谢您的回复。我认为我只是做错了什么,而不是锯齿状数组会更快。我可能会像你说的那样考虑使用一维数组(我之前也考虑过),但我不确定最干净的方法是什么。你认为这样的表现值得付出努力吗?还是它们或多或少是一样的?还有你所说的将这些变量排除在循环之外,这真的很重要吗?我假设编译器会足够聪明,可以自己做这件事?
  • @Lolop:我不想猜测编译器会做什么,或者使用单个数组会有什么好处。我会至少尝试我在帖子中使用的方法 - 你已经有了代码来测量它,所以试一试。如果这没有您希望的那么快,那么您可以测试单数组方法……请注意,可读性会有所下降。
  • 好吧,我完全错了。仅仅按照你说的去做,运行时间几乎减少了一半。我认为我的循环运行速度几乎是最快的,仅运行空循环大约需要总执行时间的 2/3。谢谢您的帮助!开始对为什么它没有按我预期的那样工作感到非常恼火。
猜你喜欢
  • 2010-11-17
  • 1970-01-01
  • 2013-05-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多