将数据保存在缓存中的技术,地点?

时间:2012-03-22 11:52:01

标签: java c++ c performance caching

对于超快速代码,我们必须保持引用的位置 - 在CPU缓存中保留尽可能多的密切使用的数据:

http://en.wikipedia.org/wiki/Locality_of_reference

实现这一目标的技术是什么?人们能举例吗?

我对Java和C / C ++示例感兴趣。有趣的是知道人们用来阻止大量缓存交换的方式。

问候

4 个答案:

答案 0 :(得分:9)

这可能过于通用而无法得到明确答案。与Java相比,C或C ++中的方法会有很大不同(语言布置对象的方式不同)。

基本的是,保持将在闭环中一起访问的数据。如果您的循环在类型T上运行,并且它具有成员m1 ... mN,但在关键路径中仅使用m1 ... m4,请考虑将T分解为包含m1 ... m4和包含m4的T2的T1。 ..mN。您可能希望向T1添加一个引用T2的指针。尽量避免使用与缓存边界不对齐的对象(非常依赖于平台)。

使用连续的容器(C中的普通旧数组,C ++中的vector)并尝试管理迭代上升或下降,但不要随机跳过容器。链接列表是局部性的杀手,列表中的两个连续节点可能处于完全不同的随机位置。

Java中的对象容器(和泛型)也是一种杀手,而在Vector中,引用是连续的,实际的对象不是(有一个额外的间接级别)。在Java中有很多额外的变量(如果你new两个对象一个接着一个,对象可能最终会在几乎连续的内存位置,即使会有一些额外的信息(通常是两个或三个指针之间的对象管理数据.GC将移动对象,但希望不会比运行之前更糟糕。

如果您专注于Java,请创建紧凑的数据结构,如果您有一个具有位置的对象,并且要在紧密循环中访问,请考虑保持xy对象内部的原始类型,而不是创建Point并保持对它的引用。需要更新引用类型,这意味着不同的分配,额外的间接和更少的局部性。

答案 1 :(得分:3)

两种常用技术包括:

  • 极简主义(数据大小和/或代码大小/路径)
  • 使用缓存遗忘技术

极简主义的例子:在光线追踪(三维图形渲染范例)中,使用8字节Kd树来存储静态场景数据是一种常见的方法。遍历算法仅适用于几行代码。然后,Kd树的编译方式通常是通过在树顶部放置大的空节点来最小化遍历步骤的数量(Havran的“Surface Area Heuristics”)。

错误预测通常具有50%的概率,但成本较低,因为真正的许多节点适合缓存行(考虑到每个KiB有128个节点!),并且两个子节点中的一个始终是记忆中的直接邻居。

缓存遗忘技术的示例: Morton array indexing,也称为Z阶曲线索引。如果您通常以不可预测的方向访问附近的数组元素,那么这种索引可能是首选。这可能对大型图像或体素数据有价值,在这些数据中你可能有32或甚至64个字节的大像素,然后是数百万个(典型的紧凑型相机测量是百万像素,对吗?)甚至数千亿用于科学模拟。

然而,这两种技术都有一个共同点:保持最常访问的东西在附近,事情可以越来越少,跨越主内存的整个L1缓存范围到硬盘,然后同一房间,隔壁房间,同一国家,世界各地,其他行星的其他电脑。

答案 2 :(得分:1)

我想到的一些随机技巧,以及我最近使用过的一些技巧:

重新思考你的算法。例如,您有一个具有形状的图像和查找该形状角的处理算法。您可以对其进行预处理,将所有形状的像素坐标保存在列表中,然后对列表进行操作,而不是直接对图像数据进行操作。你可以避免随意跳过图像

缩小数据类型。常规int将占用4个字节,如果您设法使用例如uint16_t您将缓存2倍以上的内容

有时您可以使用位图,我用它来处理二进制图像。我每位存储像素,所以我可以在一个缓存行中容纳8 * 32像素。 真的提升了性能

表单Java,您可以使用JNI(这并不困难)并在C中实现您的关键代码来控制内存

答案 3 :(得分:0)

在Java世界中,JIT将努力实现这一目标,并试图再次猜测这可能会适得其反。 This SO question更全面地解决了Java特定问题。