循环有效访问2个结构数组

时间:2019-04-10 03:27:39

标签: c# unity3d cpu-cache

我有以下2个结构数组和一个容器类:

[Serializable]
public struct Pointer {

    public byte State;

}

[Serializable]
public struct Data {

    public uint Hash;
    public byte SomeIndex;
    public byte SomeMoreIndex;
    public byte SomeFurtherIndex;

}

[Serializable]
public class Grid {

    public Pointer[] Cells;
    public Data[] CellData;

}

我打算将它们循环如下:

int index = 0;
for (var i = 0; i < Cells.Length; i++) {
    if (Cells[i] != 0) {
        // access CellData[index], and do more work
        index++;
    }
}

我知道CPU高速缓存未命中如何从根本上影响性能,因此我尝试按顺序访问这两个阵列。但是我的问题是:

  • 由于我们交错访问2个数组:它是否会使顺序内存访问的性能优势无效?
  • 如果没有,CPU缓存如何在这种情况下工作?
  • 如果在循环中读取CellData[index]之后,我使用其Hash访问Dictionary<Hash, ItemClass>,会进一步使循环本身的性能复杂化吗?
  • 我选择将1个结构拆分为2个以节省一些内存(我本可以使用byte[]而不是Pointer[]),因为网格可能很大并且可能稀疏,这是否公平?权衡?

1 个答案:

答案 0 :(得分:0)

如果重复速度足够快(即“更多工作”不会破坏缓存),则同一64B行中的元素仍将具有缓存优势。

如果数组位于不同的页面上,则跨行的元素仍应享有硬件预取的好处。

使用Hash字段将创建数据依赖关系,并且当然会受到惩罚。这是一个常见的A[B[i]]问题,有一些学术上的预取者可以解决(例如IMP),但据我所知,在商用CPU中什么也没有。如果现有的“顺序”硬件预取能够在实际运行之前运行得足够长,从而可以预取哈希数据足够的迭代次数,则可以缓解其中的大部分情况,在这种情况下,代价将减少为两个背对背的L1访问(或任何高速缓存)级别会实现该预取器-通常L1应该有一个。 请注意,对性能的影响不是直接的,因为不同的迭代是独立的,但是一旦使未处理的缓冲区饱和,内存延迟将转化为内存带宽限制。