在多维数组和单个数组之间存储数据的最有效方法是啥?
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]
并且通常具有相似的性能特征。
然而,确切的性能将取决于内存访问模式,以及这如何影响缓存一致性(至少对于大量数据而言)。您可能会发现嵌套循环在x
、y
然后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 & 0xF
、y = index & 0xF0
和z = index & 0xF00
@downvoter:想让我们知道答案有什么问题吗?会很有用的。
单维数组只有在您按顺序访问时才会更快,不是吗?如果您根据已知坐标进行查找,则两者的性能应该相同。
@Luaan 你读过我发布的关于缺少一些编译器优化的链接吗?
是的,没错。提到的缺失优化是将部分计算提升到内部循环之外,这意味着很多不必要的乘法。因此,如果您尝试访问数组中的一个特定位置,则两者的执行方式应该相同。他们不这样做的唯一情况是当你在一个“行”中循环“列”时 - 行偏移量是一遍又一遍地计算......至少我是这样理解分析的。【参考方案3】:
我会投票支持单维数组,它应该工作得更快。基本上,您可以编写一些测试,执行最常见的任务并测量花费的时间。 此外,如果您有 2^n 数组大小,则使用左移操作而不是乘法来访问元素位置要快得多。
【讨论】:
以上是关于在多维数组和单个数组之间存储数据的最有效方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章