在锯齿状数组上手动索引?

Manual indexing over jagged arrays?

我的程序遇到了性能瓶颈,我需要在一个紧密循环中访问数组中的元素数百万次。

我环顾四周,普遍的共识似乎是,即使多维数组 应该 更快,但它们的底层实现效率低下,所以只需使用交错数组即可。我对其进行了概要分析,果然,锯齿状数组的速度提高了 50%。很好

但是,我也尝试过只做手动索引(例如,通过做这样的事情来模拟多维数组的行为:object value = array[i * 24 + j]; (where 24 is an array size) 并通过具有乘法的一维数组访问它来模拟多维数组。

令人惊讶的是,这也比锯齿状数组的访问速度快了大约 15%(我只关心)。这让我感到难过,因为一方面,手动重新创建多维数组比 C# 的内置实现快得多,这似乎很愚蠢;第二,与仅使用 jagged/multidimensional 数组进行索引相比,获取索引所涉及的数学更难看。

有什么办法可以在不使用我自己的手动索引的情况下恢复速度优势?当然可以设置或检查某种优化来模拟这种行为?为什么数组的 C# 实现如此低效?

Surprisingly, this was also faster than a jagged array by around 15% for accesses

这应该不足为奇,因为索引锯齿状数组需要额外的取消引用。当您写 a[i][j] 时,计算机必须执行以下操作:

  • 计算嵌套数组 i 在交错数组 a
  • 中的位置
  • 获取嵌套数组的位置a[i](第一次取消引用)
  • 计算元素 ja[i]
  • 中的位置
  • 获取 a[i] 位置 j 处的值(第二次取消引用)

当您将二维数组折叠成一个向量时,计算机只会执行一次取消引用:

  • 计算目标元素的位置
  • 从数组中获取值(唯一的解引用)

本质上,您是在用取消引用换取乘法;乘法更便宜。

此外,您可以获得内存中元素的连续性 - 这是锯齿状数组无法保证的。这对于对缓存命中敏感的代码变得很重要。

Is there anything I can do to recoup the speed benefits without having to use my own manual indexing?

使用您的索引方案是一种可行的方法。您可以通过制作一个 class,比如 Matrix2D 公开一个采用两个索引并生成值的 operator [] 来向代码的查看者隐藏它。这样计算偏移量的代码将对程序的读者隐藏起来,因为 a[i * 24 + j] 部分看起来像 a[i, j]