C# 在多维数组和单个数组之间存储数据的最有效方式是什么?

C# 在多维数组和单个数组之间存储数据的最有效方式是什么?,c#,arrays,multidimensional-array,unity3d,jagged-arrays,C#,Arrays,Multidimensional Array,Unity3d,Jagged Arrays,基本上,我不确定如何存储3D数据结构以实现尽可能快的访问,因为我不确定在多维阵列的引擎盖下发生了什么 注意:数组每次都是一个恒定的已知大小,每个元素正好是16位 选项一是使用多维数组data[16,16,16],只需通过data[x,y,z]访问即可。选项二是使用一维数组data[16*16*16]并通过data[x+(y*16)+(z*16*16)]访问 由于每个元素应该只有16位长,我怀疑多维数组会在内部以每个元素至少32位的速度存储大量对其他数组的引用,这是一个很大的内存浪费。然而,我担心

基本上,我不确定如何存储3D数据结构以实现尽可能快的访问,因为我不确定在多维阵列的引擎盖下发生了什么

注意:数组每次都是一个恒定的已知大小,每个元素正好是16位

选项一是使用多维数组
data[16,16,16]
,只需通过
data[x,y,z]
访问即可。选项二是使用一维数组
data[16*16*16]
并通过
data[x+(y*16)+(z*16*16)]
访问

由于每个元素应该只有16位长,我怀疑多维数组会在内部以每个元素至少32位的速度存储大量对其他数组的引用,这是一个很大的内存浪费。然而,我担心它可能比每次运行选项2中指定的等式要快,而速度是这个项目的关键


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

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

我认为值得指出的是,如果您的数组大小实际上都是16,则可以更高效地从(x,y,z)计算数组的索引:

int index = x | y << 4 | z << 8;
如果是这种情况,那么我建议使用一维数组,因为它几乎肯定会更快

请注意,JIT编译器完全有可能执行这种优化(假设乘法是按照您的OP硬编码的),但这是值得显式执行的

我说一维数组会更快的原因是

这就是说,您应该仔细计时,看看什么是真正最快的

正如Eric Lippert所说:.

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

也就是说,
arr[x,y,z]
几乎等同于
arr[x+y*ny+z*nz*ny]
,通常具有类似的性能特征

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

这在很大程度上取决于精确的算法,因此不可能给出对所有算法都正确的答案

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

同样,精确算法将影响优化程序是否能够删除边界检查

你的行动方针应如下:

  • arr[x,y,z]
    编写算法的原始版本
  • 如果速度够快,你可以停下来
  • 另外,对算法进行概要分析,以检查问题实际上是数组访问,分析内存访问模式等等

我认为这可能有助于你提高一维速度:,,benjamin james drury,我想稍微修改一下问题,以强调每个元素都是16位的事实,因为它使问题不同于类似的问题,我之所以不提及现有问题,是因为我有一个具体的案例,但我知道我的想法是不正确的,所以也许我应该默认它们。尽管如此,还是要感谢每一位提供意见的人,我对此表示感谢,并将与马赛跑,看看哪一匹更快。相反的是,
x=index&0xF
y=index&0xF0
,和
z=index&0xF00
downvoter:请告诉我们答案有什么问题?这将非常有用。不过,只有按顺序访问一维数组时,它才会更快,不是吗?如果你是根据已知的坐标进行查找,这两个函数应该执行相同的操作。@Luaan你读过我发布的关于缺少一些编译器优化的链接吗?是的,没错。提到的缺失优化是将计算的一部分提升到内部循环之外,这意味着大量不必要的乘法。因此,如果您试图访问阵列中的一个特定位置,那么这两个位置应该执行相同的操作。唯一没有的情况是当你在一行中的“列”上循环时-行偏移量被反复计算。。。至少我是这样理解分析的。这不完全是真的——请看我之前的链接(例如:@MatthewWatson),正如我所说,“精确的算法将影响优化者是否能够删除边界检查”@MatthewWatson公平。编辑。谢谢,我会尝试两种方法,看看哪种方法更快。我原以为两者之间会有更明显的区别。
int x = index & 0xf;
int y = (index >> 4) & 0xf;
int z = (index >> 8) & 0xf;