在锯齿状数组上手动索引?
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]
(第一次取消引用)
- 计算元素
j
在 a[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]
我的程序遇到了性能瓶颈,我需要在一个紧密循环中访问数组中的元素数百万次。
我环顾四周,普遍的共识似乎是,即使多维数组 应该 更快,但它们的底层实现效率低下,所以只需使用交错数组即可。我对其进行了概要分析,果然,锯齿状数组的速度提高了 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]
(第一次取消引用) - 计算元素
j
在a[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]