在托管代码中,如何实现良好的引用局部性?

时间:2009-10-05 08:58:37

标签: c# java python optimization memory-management

由于RAM似乎是the new disk,并且因为该语句也意味着现在认为对内存的访问速度与磁盘访问的方式类似,我确实希望最大化内存中的引用位置以获得高性能应用。例如,在排序索引中,我希望相邻值接近(不像在哈希表中),我也希望索引指向的数据也接近。

在C中,我可以使用专门的内存管理器来创建数据结构,就像(非常复杂的)Judy array的开发人员一样。通过直接控制指针,它们甚至可以在指针值本身中编码附加信息。在Python,Java或C#中工作时,我故意将这种类型的解决方案中的一个(或多个)抽象级别委托给JIT编译器并优化运行时,为我做低级别的巧妙技巧

尽管如此,我猜,即使在这种高级抽象中,也有一些东西可以在语义上被认为“更接近”,因此可能实际上更接近于低级别。例如,我想知道以下(我在括号中的猜测):

  • 我可以期待一个数组成为相邻的内存块(是)吗?
  • 同一个实例中的两个整数是否比同一个类的不同实例中的两个更接近(可能)?
  • 对象是否占据内存中的一个连续区域(否)?
  • 只有两个int字段的对象数组和一个包含两个int[]字段的对象之间有什么区别? (这个例子可能是Java特定的)

我开始在Java环境中对这些问题感到疑惑,但我的想法变得更加普遍,所以我建议不要将其视为Java问题。

6 个答案:

答案 0 :(得分:9)

  • 在.NET中,数组的元素肯定是连续的。在Java中,我希望它们可以在大多数实现中使用,但似乎无法保证。
  • 我认为假设一个实例用于字段的内存在一个块中是合理的......但是不要忘记其中一些字段可能是对其他对象的引用。

对于Java数组部分,Sun's JNI documentation包含此注释,隐藏在关于字符串的讨论中:

  

例如,Java虚拟机可能无法连续存储数组。

对于你的上一个问题,如果你有两个int[],那么每个阵列都将是一个连续的内存块,但它们在内存中可能相距甚远。如果你有一个带有两个int字段的对象数组,那么每个对象可能相距很远,但每个对象中的两个整数将靠近在一起。可能更重要的是,由于每个对象的开销,你最终会使用“大量对象”解决方案获得 lot 更多的内存。在.NET中,你可以使用带有两个整数的自定义 struct ,并且有一个数组 - 这样可以将所有数据保存在一个大块中。

我相信在Java和.NET中,如果在单个线程中快速连续分配大量小对象,那么这些对象可能具有良好的引用局部性。当GC压缩堆时,如果堆为

,这可能会改善 - 或者可能会变得更糟
A B C D E

被压缩到

A D E B

(收集C的地方) - 突然之间可能已经“接近”的A和B相隔很远。我不知道这是否真的发生在任何垃圾收集器中(周围有负载!)但它是可能的。

基本上在托管环境中,您通常不像在非托管环境中那样控制引用的位置 - 您必须相信托管环境足以管理它,并且您将拥有通过编码到更高级别的平台节省了足够的时间,让您花时间在其他地方进行优化。

答案 1 :(得分:3)

首先,你的头衔暗示C#。如果我没弄错的话,“托管代码”是微软创造的一个术语。

Java原始数组保证是连续的内存块。如果你有

int[] array = new int[4];

你可以从JNI(本地C)得到一个int *p指向实际的数组。我认为这也适用于Array *类容器(ArrayList,ArrayBlockingQueue等)。

我认为JVM的早期实现将对象视为连续结构,但是对于较新的JVM,不能假设这种情况。 (JNI抽象了这个)。

同一个对象中的两个整数会像你说的那样“更接近”,但它们可能不是。即使使用相同的JVM,这可能会有所不同。

具有两个int字段的对象是一个对象,我认为任何JVM都不保证成员将“关闭”。具有两个元素的int数组很可能由8字节长的数组支持。

答案 2 :(得分:2)

关于数组,这里是CLI(公共语言基础结构)规范的摘录:

  

应布置数组元素   在row-major中的数组对象中   顺序(即,相关的元素   最右边的数组维度   应从最低指数到最高指数连续布局)。该   为每个分配的实际存储空间   数组元素可以包括   特定于平台的填充。 (尺寸   返回此存储的数量(以字节为单位)   通过sizeof指令   应用于该数组的类型   元件。

答案 3 :(得分:2)

好问题!我想我会在C ++中编写扩展,以更谨慎的方式处理内存,只是暴露足够的接口以允许应用程序的其余部分操作对象。如果我关注性能,我可能会使用C ++扩展。

答案 4 :(得分:2)

我认为没有人谈论过Python,所以我会去看看

  

我可以期待一个数组成为相邻的内存块(是)吗?

在python数组中更像是C中的指针数组。因此指针将是相邻的,但实际的对象不太可能。

  

同一个实例中的两个整数是否比同一个类的不同实例中的两个更接近(可能)?

可能与上述原因不同。该实例仅保存指向实际整数的对象的指针。 Python没有native int(比如Java),只有盒装Int(用Java说)。

  

对象是否占据内存中的一个连续区域(不)?

可能不是。但是,如果您使用__slots__优化,那么它的某些部分将是连续的!

  

只有两个int字段的对象数组和一个具有两个int []字段的对象之间有什么区别?   (这个例子可能是Java特定的)

在python中,就内存局部性而言,它们几乎都是一样的!一个将生成一个指向对象的指针数组,这些指针又包含两个指向int的指针,另一个指针将生成两个指向整数的指针数组。

答案 5 :(得分:-3)

如果您需要优化到该级别,那么我怀疑基于VM的语言不适合您;)