结构内存破解重叠对象引用 - 是否可能?

Wil*_*ood 14 .net c# performance memory-management

我猜这个问题的答案是"这是不可能的,切换到C++".但我还以为我会把它扔出去.

我正在处理一个巨大的二叉树.我有一个结构数组来表示我在迭代树时用来帮助处理内存的分支节点.

为了节省一点内存,从而改善缓存局部性,我正在考虑重叠叶节点的对象引用.该对象引用将指向所有叶数据.基本上,这样的事情:

[StructLayout(LayoutKind.Explicit)]
struct BranchData
{
    [FieldOffset(0)] // 1 byte
    internal byte SplitIndex;
    [FieldOffset(1)] // 4 bytes
    internal float SplitValue;
    [FieldOffset(5)] // 4 bytes
    internal int LowIndex;
    [FieldOffset(9)] // 4 bytes
    internal int HighIndex;
    [FieldOffset(0)] // 8 bytes (We're working with x64 here)
    internal LeafData Node;
}
Run Code Online (Sandbox Code Playgroud)

上面给出了以下运行时错误

无法从程序集"WindowsFormsApplication1,Version = 1.0.0.0,Culture = neutral,PublicKeyToken = null"加载类型"BranchData",因为它包含偏移0处的对象字段,该字段未正确对齐或由非对象字段重叠.

我可以使用一个单独的数组来存储叶子数据,并使用索引指向该数组,但后来我有2个内存查找(对于什么当然是远处的内存区域).一个用于叶子数组中的位置以获取引用,另一个用于获取叶子数据.如果我可以实现这种重叠,我摆脱了其中一个查找.

我能够固定对象并使用不安全的代码来解决这个问题.速度是这里的关键因素.

Han*_*ant 16

此限制在托管代码中非常重要.问题是您的Node成员是对象引用.运行时的指针.它与其他字段重叠.

垃圾收集器需要能够找回指针.必须要知道堆上的LeafData对象有实时引用.并且在压缩堆时移动LeafData对象时更新该指针.

问题是:收集器无法判断你的联盟是否存储了指针.如果没有,则存在其他成员的值看起来像GC的有效对象引用的风险.这非常非常糟糕.

存储不安全的LeafData*在技术上是可行的,但这需要固定LeafData对象.当树很大时,这就无法工作了,当任何东西都不能移动时,GC就会崩溃.将LeafData数据存储在非托管内存中是在兔子洞的下方,你将开始编写C++代码.你可以做的唯一另一件事就是将LeafData存储在节点本身中,作为一个结构,你很可能不满意.

请注意,您应该避免使用这些未对齐的字段,当字段跨越L1缓存行边界时,您会受到猛烈的攻击.将SplitIndex放在HighIndex 之后,这样就不会发生了.