代码之家  ›  专栏  ›  技术社区  ›  Morten Christiansen

在内存中固定指针数组

  •  1
  • Morten Christiansen  · 技术社区  · 16 年前

    作为一个业余项目,我目前正在研究C中的光线跟踪器。我试图通过实现C++实现中的一些技巧来达到一个不错的渲染速度,并且遇到了麻烦。

    光线跟踪器渲染的场景中的对象存储在kdtree结构中,树的节点依次存储在数组中。我遇到的优化问题是,试图将尽可能多的树节点放入缓存线。这样做的一种方法是,节点只包含指向左子节点的指针。然后隐式地,右子项紧跟在数组中左子项的后面。

    节点是结构,在树构造期间,静态内存管理器类会将它们成功地放入数组中。当我开始横穿这棵树时,起初,它似乎工作得很好。然后在渲染的早期(每次大约在同一个位置),根节点的左子指针突然指向一个空指针。我得出的结论是,当数组位于堆上时,垃圾收集器移动了结构。

    我尝试过几种方法来将地址固定在内存中,但它们中没有一种能像我需要的那样在整个应用程序生命周期中持续。“fixed”关键字只在单个方法调用和声明“fixed”数组时才有帮助,只能在节点没有的简单类型上进行。有没有一种好的方法可以做到这一点,或者我是不是太落后于C不适用的东西。

    顺便说一下,变为C++,也许是一个高性能程序的更好选择,不是一个选项。

    5 回复  |  直到 9 年前
        1
  •  4
  •   Luke Halliwell    16 年前

    首先,如果您正常使用c,由于垃圾收集器正在移动数据,所以您不能突然获得空引用,因为垃圾收集器也会更新所有引用,因此您不必担心它会移动数据。

    你可以把事情固定在记忆中,但这可能会导致比它解决的问题更多的问题。首先,它防止垃圾收集器正确压缩内存,并可能以这种方式影响性能。

    我想从你的文章中说的一件事是,使用结构可能不会像你希望的那样有助于性能。C无法内联任何涉及结构的方法调用,即使它们在最新的运行时测试版中修复了这一问题,结构通常也不能很好地执行。

    就我个人而言,我认为这样的C++技巧通常不太适合C语言。您可能需要学会放手一点;还有其他更微妙的方法来提高性能;)

        2
  •  2
  •   Barry Kelly    16 年前

    静态内存管理器实际上在做什么?除非它在做不安全的事情(p/invoke,不安全的代码),否则您看到的行为是程序中的一个bug,而不是由于clr的行为。

    第二,对于结构之间的链接,“指针”是什么意思?你的字面意思是不安全的kdtree*指针吗?不要那样做。相反,使用数组中的索引。因为我希望单个树的所有节点都存储在同一个数组中,所以您不需要对该数组进行单独的引用。只要一个索引就行了。

    最后,如果您确实必须使用kdtree*指针,那么静态内存管理器应该使用marshal.allochglobal或其他非托管内存源来分配一个大的块;它应该将这个大的块视为kdtree数组(即索引kdtree*c样式)。 它应该通过碰撞一个“空闲”指针,从这个数组中子分配节点。

    当然,如果必须调整这个数组的大小,那么就需要更新所有指针。

    这里的基本经验是不安全指针和托管内存 混合“fixed”块之外的内容,当然这些块具有堆栈帧关联性(即,当函数返回时,固定行为消失)。有一种方法可以使用gchandle.alloc(your array,gchandleType.pinned)固定任意对象,如数组,但几乎可以肯定,您不想沿着该路径移动。

    如果你更详细地描述你在做什么,你会得到更合理的答案。

        3
  •  1
  •   Andrew    16 年前

    如果你 真的? 要做到这一点,您可以使用gchandle.alloc方法来指定指针应该固定,而不会像fixed语句那样在作用域的末尾自动释放。

    但是,正如其他人所说,这样做对垃圾收集器造成了不适当的压力。仅仅创建一个结构来保存一对节点,然后管理一个节点数组而不是一个节点数组怎么样?

    如果您真的想完全非托管地访问一块内存,最好是直接从非托管堆分配内存,而不是永久地固定托管堆的一部分(这会阻止堆能够正确地压缩自己)。一种快速而简单的方法是使用marshal.allochGlobal方法。

        4
  •  0
  •   DrPizza    16 年前

    是否确实禁止存储数组引用和索引对?

        5
  •  0
  •   Chris Forrence Shreya Gupta    9 年前

    静态内存管理器实际上在做什么?除非它在做不安全的事情(p/invoke,不安全的代码),否则您看到的行为是程序中的一个bug,而不是由于clr的行为。

    事实上,我说的是不安全的指针。我想要的是 Marshal.AllocHGlobal 但其生存期超过了单个方法调用。在反思中,仅仅使用一个索引是正确的解决方案,因为我可能已经陷入了模仿C++代码中。

    我想从你的文章中说的一件事是,使用结构可能不会像你希望的那样有助于性能。C无法内联涉及结构的任何方法调用,即使它们在最近的运行时测试版中修复了这一问题,结构通常也不能很好地执行。

    我仔细研究了一下,发现它已经在.net 3.5sp1中修复了;我假设这就是您所说的运行时测试版。事实上,我现在明白了这个变化导致了我的渲染速度翻倍。现在,structs在x86系统上的性能得到了极大的提高(x64预先具有更好的struct性能)。