在多维数组和单个数组之间存储数据的最有效方法是啥?

Posted

技术标签:

【中文标题】在多维数组和单个数组之间存储数据的最有效方法是啥?【英文标题】:What is the most efficient way of storing data between a multi-dimension array, and a single array?在多维数组和单个数组之间存储数据的最有效方法是什么? 【发布时间】:2016-04-25 11:55:29 【问题描述】:

基本上我不确定如何存储 3D 数据结构以实现最快的访问,因为我不确定多维数组的幕后情况。

注意:数组每次都是一个常数且已知大小,每个元素都是 16 位。

选项一是拥有一个多维数组data[16, 16, 16],只需通过data[x, y, z] 访问选项二是拥有一个单维数组data[16 * 16 * 16],并通过data[x + (y * 16) + (z * 16 * 16)] 访问。

因为每个元素应该只有 16 位长,而且我怀疑多维数组会在内部存储大量对其他数组的引用,每个元素至少 32 位,这会浪费很多内存.但是,我担心它可能比每次运行选项二中指定的方程更快,速度是这个项目的关键。

那么,谁能告诉我,与内存消耗的差异相比,速度差异有多大?

【问题讨论】:

Multi-dimensional array vs. One-dimensional的可能重复 我认为这可能会对你有所帮助Why are multi-dimensional arrays in .NET slower than normal arrays? 一维可能更快:github.com/dotnet/coreclr/issues/4059#issuecomment-208491798, but if you want to know which horse is faster you should race them。 benjamin-james-drury,我会稍微改变一下问题,以强调每个元素都是 16 位的事实,因为它使问题与类似问题不同,并且对答案产生了有趣的看法 我之所以不提及现有问题是因为我的具体案例,但我了解到我的想法是不正确的,所以也许我应该默认他们。无论如何,感谢所有提供意见的人,我很感激,并且确实会赛马看看哪个更快。 【参考方案1】:

C# 将多维数组存储为单个内存块,因此它们编译成几乎相同的东西。 (一个区别是要检查三组边界)。

arr[x,y,z] 几乎等同于 arr[x + y*ny +z*nz*ny] 并且通常具有相似的性能特征。

然而,确切的性能将取决于内存访问模式,以及这如何影响缓存一致性(至少对于大量数据而言)。您可能会发现嵌套循环在xy 然后z 上可能比以不同顺序执行循环更快或更慢,如果将当前使用的数据保存在处理器缓存中做得更好的话。

这高度依赖于确切的算法,因此不可能给出对所有算法都正确的答案。

与 C 或 C++ 相比,任何速度降低的另一个原因是边界检查,在一维数组的情况下仍然需要它。然而,这些通常但不总是会被自动删除。

https://blogs.msdn.microsoft.com/clrcodegeneration/2009/08/13/array-bounds-check-elimination-in-the-clr/

同样,确切的算法将影响优化器是否能够移除边界检查。

你的行动应该如下:

arr[x,y,z] 编写一个简单的算法版本。 如果它足够快你可以停下来。 否则分析算法以检查它实际上是数组访问问题,分析内存访问模式等等。

【讨论】:

这不是真的 - 请参阅我以前的链接(例如github.com/dotnet/coreclr/issues/4059#issuecomment-208491798) @MatthewWatson 正如我所说,“确切的算法将影响优化器是否能够删除边界检查” @MatthewWatson 很公平。已编辑。 谢谢,我会尝试两种方式,看看哪种方式更快。我原以为两者之间会有更明显的区别。【参考方案2】:

我认为值得指出的是,如果你的数组维度真的都是 16,那么你可以更有效地从 (x, y, z) 计算数组的索引:

int index = x | y << 4 | z << 8;

反过来:

int x = index & 0xf;
int y = (index >> 4) & 0xf;
int z = (index >> 8) & 0xf;

如果是这种情况,那么我建议使用一维数组,因为它几乎肯定会更快。

请注意,JIT 编译器完全有可能无论如何都会执行此优化(假设乘法是根据您的 OP 硬编码的),但值得明确地进行。

我说一维数组会更快的原因是因为the latest compiler is lacking some of the optimisations for multi-dimensional array access, as discussed in this thread。

也就是说,您应该仔细计时,看看什么是最快的。

正如 Eric Lippert 所说:"If you want to know which horse is faster, race your horses"。

【讨论】:

反过来x = index &amp; 0xFy = index &amp; 0xF0z = index &amp; 0xF00 @downvoter:想让我们知道答案有什么问题吗?会很有用的。 单维数组只有在您按顺序访问时才会更快,不是吗?如果您根据已知坐标进行查找,则两者的性能应该相同。 @Luaan 你读过我发布的关于缺少一些编译器优化的链接吗? 是的,没错。提到的缺失优化是将部分计算提升到内部循环之外,这意味着很多不必要的乘法。因此,如果您尝试访问数组中的一个特定位置,则两者的执行方式应该相同。他们不这样做的唯一情况是当你在一个“行”中循环“列”时 - 行偏移量是一遍又一遍地计算......至少我是这样理解分析的。【参考方案3】:

我会投票支持单维数组,它应该工作得更快。基本上,您可以编写一些测试,执行最常见的任务并测量花费的时间。 此外,如果您有 2^n 数组大小,则使用左移操作而不是乘法来访问元素位置要快得多。

【讨论】:

以上是关于在多维数组和单个数组之间存储数据的最有效方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

在 MATLAB 中序列化多维数组以进行数据库插入的最简单方法?

Perl:展平多维数组的最简单方法是啥?

从多维数组中删除数组的最有效方法[重复]

创建初始重复数据的二维字符串数组的最有效方法是啥?

使用 Perl 检查数据数组中重复项的最有效方法是啥?

创建零填充 JavaScript 数组的最有效方法是啥?