c# - 在循环中有效地访问 2 个结构数组
问题描述
我有以下 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
来访问 aDictionary<Hash, ItemClass>
,它是否会使循环本身的性能进一步复杂化? - 我选择将 1 个结构拆分为 2 个以节省一些内存(我本可以使用
byte[]
而不是Pointer[]
),因为网格可能很大并且可能很稀疏,这是一个公平的权衡吗?
解决方案
如果重复速度足够快(即“更多工作”不会破坏缓存),同一 64B 行中的元素仍将具有缓存优势。
如果数组位于不同的页面上,跨行的元素仍应享受硬件预取的好处。
使用 Hash 字段会产生数据依赖性,当然会受到惩罚。这是一个常见A[B[i]]
问题,并且有一些学术预取器解决了它(例如,IMP),但据我所知,在商业 CPU 中没有任何问题。如果现有的“顺序”硬件预取在实际使用之前运行得足够远以预取哈希数据足够多的迭代,则应该可以减轻大部分问题,在这种情况下,惩罚将减少为两个背靠背的 L1 访问(或任何缓存级别实现该预取器 - 通常 L1 应该有一个)。请注意,对性能的影响不是直接的,因为不同的迭代是独立的,但是一旦您的未处理缓冲区饱和,内存延迟将转化为内存 BW 限制。
推荐阅读
- python - 无法解决 Python ImportError: cannot import name 'FirstNews'
- javascript - Fancybox 画廊打破 swiper
- jenkins - Jenkins:Jenkins 在哪里存储分配的用户(ldap 用户)的配置文件?
- c# - 如何创建一个包含 n 个字符串属性和一个数据对象的对象列表,但对象类型及其属性可以在 C# 中动态更改
- spring - 我无法在 Spring 中通过 Thymeleaf 以 HTML 格式显示图像
- animation - Flutter TabBarView的动画持续时间
- arduino - 中断运行时Arduino不会休眠
- git - 为什么 First Commit (Root Commit) 的时间戳大于 Second Commit?
- java - 版本 8 及更高版本的 OpenJDK 安全更新
- json - ORM: Sequelize: 除了一些 json 数据,我如何返回?