计算数组中的索引比让编译器更有效吗?

时间:2018-03-05 13:41:30

标签: r performance indexing

我正在尝试将神经网络函数推广到任意多个层,因此我需要多个矩阵来保存每层中每个神经元的权重。我最初明确地在R中声明矩阵对象来保存每个图层的权重。我没有在每层中使用一个矩阵,而是考虑了一种方法(不是说它是原始的),将所有权重存储在一个数组中,并定义了一个“索引函数”来将权重映射到数组中的相应索引。

我将函数定义如下:

其中 i -th图层中 j -th神经元的 k 重量,< em> L(r)是层 r 中神经元的数量。写完这些定义之后,我意识到stackoverflow不允许像mathoverflow这样的乳胶,这是不幸的。 现在的问题是:以这种方式计算权重指数是否更有效,或者效率更低? 在查看了一般如何为数组计算索引之后,如果我只是在每个持有权重的层中保留一个矩阵,那么这基本上就是在编译时所做的事情,所以看起来我可能只是让我的代码过于复杂和难以了解时间效率是否没有差异。

4 个答案:

答案 0 :(得分:3)

TL; DR 使用矩阵更容易理解并利用优化的CPU指令。

在计算机科学的说法中,算法的效率(可扩展性)是关于使用Big O cost的。可以给出时间和空间复杂度的分数 使用Big O表示法可以比较两种方法:

阵列方法

时间复杂度
数组索引访问是O(1)时间,无论数组有多大,在给定索引的情况下访问元素都很容易。

由于您已经创建了一个计算k-th权重索引的函数,这会增加一些小的复杂性,但可能会在O(1)时间内运行,因为它是一个数学表达式,因此可以忽略不计。

空间复杂性O(N)其中N是所有图层的权重数。

矩阵方法

时间复杂度
矩阵本质上是一个带有O(1)访问

的二维数组

空间复杂性
O(N + M),其中N是神经元数量,M是权重数量。

从概念上讲,我们可以看到这两种方法具有相同的时间和空间复杂度得分 然而,还有其他权衡因素(并且作为一个好的SO-er必须通知你那些)

当涉及使用数组与矩阵方法中的数据时,数组方法效率较低,因为它绕过了MISD operations的机会。正如@liborm所暗示的那样,矢量化(MISD)操作由较低级别的系统库(如LAPACK/BLAS)处理,这些操作用于某些矩阵操作的“批处理”CPU指令(与发送数据相比,在CPU上传输和计算数据的开销成本更低)每次都有新的指示)

  

我不是每层都有一个矩阵,而是想办法将所有权重存储在一个数组中

很难理解为什么要为后者选择,因为它需要您创建一个定制的索引功能。也许更好地考虑你所有的重量在一个长阵列的位置?但是我认为维持数组映射所需的心理负载高于专用于层的多个矩阵。

类似哈希表的矩阵结构将更容易推理

layers <- list(layer1 = [[...]], layer2 = [[...]], layerN = [[...]])

进一步阅读

http://www.noamross.net/blog/2014/4/16/vectorization-in-r--why.html

答案 1 :(得分:2)

每种方法都需要考虑很多因素。我不熟悉R,但我假设矩阵的缓冲区在内存中表示为一维数组。 (即使它们在底层C实现中被编写为二维数组,编译器也将其作为一维数组存储在内存中)

内存操作的总体概述是:

  1. 案例:每层有几个矩阵<​​/ p>

    • 矩阵分配:allocationmatrix
    • 访问索引:accesingindices
  2. 案例:所有图层的一个矩阵+指数计算

    • 矩阵成本的分配:onematrixallocation
    • 访问每个指数成本:accesingindicies
    • 功能费用:functioncost
  3. 我们可以清楚地看到第二种情况,即使有额外的函数调用成本,也可以更好地扩展。

    话虽如此,通常具有静态分配的阵列以及所有层的所有权重,应该更快。

    在大多数情况下,计算机的瓶颈是内存带宽,抵消这种情况的最佳方法是尽量减少内存访问次数。

    考虑到这一点,第二种方法可能更快的另一个更原始的原因:缓存。

    这是一个很好的explanation of the performance difference in accesing a two-dimensional array in a loop by Good Ol' Bob Martin

    TL; DR:缓存利用principle of locality,因此,内存访问在空间上彼此接近(就像在单个阵列中一样,并以Bob Martin's answer中所述的缓存友好方式访问它们)提供比在空间上分离它们更好的性能(将它们放在几个不同的数组中)。

    PS:我还建议对这两种方法进行基准测试并进行比较,因为这些关于缓存的细微差别与机器有关。可能是数据集/ NN小到足以完全适合RAM甚至缓存?在一个非常强大的服务器中。

答案 2 :(得分:1)

我确定你想使用某种原生数组对象,因此你可以获得BLAS / LAPACK实现提供的加速比(如果你在Windows上,请参阅例如英特尔MKL讨论here)。 NN评估中的大部分时间将用于矩阵乘法(如SGEMM),这就是像英特尔MKL这样的BLAS实现可以快一个数量级。

即使您的单阵列多层网络的手动编码索引超快,您也无法将其与优化的乘法例程一起使用,这会使整个网络显着变慢。使用本机数组对象并在它们之上创建多层抽象。

但实际上如果你想要速度和可用性(以及真正构建一些NN模型),你应该考虑使用类似R interface to TensorFlow的东西。作为奖励,你可以免费在GPU上运行。

答案 3 :(得分:0)

很好的谜题..如果你要求计算在运行时需要编译的索引。只是想了解你如何让编译器计算它?如果您以后需要随时播放信息,那么我建议使用Hasmap类型的机制。我是为了类似的需要而做的。