c# - 构造内存 hack 以重叠对象引用 - 这可能吗?

标签 c# .net 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;
}

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

Could not load type 'BranchData' from assembly 'WindowsFormsApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' because it contains an object field at offset 0 that is incorrectly aligned or overlapped by a non-object field.

我可以使用一个单独的数组来存储叶数据,并使用索引指向该数组,但是我有 2 次内存查找(对于那些肯定是遥远的内存区域)。一个用于叶数组中的位置以获取引用,一个用于获取叶数据。如果我能实现这种重叠,我就可以摆脱其中一个查找。

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

最佳答案

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

垃圾收集器需要能够找到那个指针。两者都需要知道堆上存在对 LeafData 对象的实时引用。并在压缩堆时移动 LeafData 对象时更新该指针。

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

存储不安全的 LeafData* 在技术上是可行的,但这需要固定 LeafData 对象。当树很大时,那是行不通的,当什么都不能移动时,GC 就会崩溃。将 LeafData 数据存储在非托管内存中是更难的事情,到那时你就可以开始编写 C++ 代码了。您唯一可以做的另一件事是将 LeafData 存储在节点本身中,作为一个结构,您不太可能对适合度感到满意。

请注意,您应该避免这些未对齐的字段,当字段跨越 L1 缓存行边界时,您会被猛烈撞击。将 SplitIndex 放在 HighIndex 之后,这样就不会发生这种情况。

关于c# - 构造内存 hack 以重叠对象引用 - 这可能吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17771902/

相关文章:

java - 图像文件处理性能中的 SWT 与 AWT?

c# - ValueTask 有 ContinueWith 吗?

c# - IFrame : Page is post back or refreshing on iframe src

python - 在 Python 中矢量化 Haversine 距离计算

具有多个 union 和 group by 的 MySQL 查询

.net - margin 中的属性顺序

c# - 通过 Lazy<T> 或任何 lambda 表达式访问非静态成员

c# - 需要 AcquireCredentialsHandle win32 api 函数的 C# 签名

c# - 如何通过代码隐藏将AutoGeneratedColumn header 包装在TextBlock中?

c# - RESTSharp 抛出时未捕获异常