我目前正在使用C#中的光线跟踪器作为业余爱好项目。我试图通过从c ++实现中实现一些技巧来实现一个不错的渲染速度,并且遇到了麻烦。
光线跟踪器渲染的场景中的对象存储在KdTree结构中,而树的节点又存储在数组中。我遇到问题的优化是在尝试将尽可能多的树节点放入缓存行时。这样做的一种方法是节点仅包含指向左子节点的指针。然后隐含的是,右子项直接跟在数组中的左子项之后。
节点是结构体,在树构造期间,它们通过静态内存管理器类成功地放入数组中。当我开始遍历树时,它起初似乎工作正常。然后在渲染早期的某个点(每次大约相同的位置),根节点的左子指针突然指向空指针。我得出的结论是,当数组位于堆上时,垃圾收集器已经移动了结构。
我已经尝试了几种方法来固定内存中的地址,但它们似乎都没有在我需要的整个应用程序生命周期中持续存在。 'fixed'关键字似乎仅在单个方法调用期间有所帮助,并且声明'固定'数组只能在节点不是的简单类型上完成。有没有一种很好的方法可以做到这一点,或者我只是在C#不适合的东西的道路上走得太远。
顺便说一下,改用c ++,虽然也许是高性能程序的更好选择,但不是一种选择。
答案 0 :(得分:4)
首先,如果您正常使用C#,由于垃圾收集器移动内容,您不能突然获得空引用,因为垃圾收集器也会更新所有引用,因此您无需担心它会移动周围的东西。
你可以将东西固定在内存中,但这可能会导致比解决的问题更多的问题。首先,它可以防止垃圾收集器正确压缩内存,并可能以这种方式影响性能。
我要在你的帖子中说的一件事是,使用结构可能无助于你所希望的性能。 C#无法内联任何涉及结构的方法调用,即使他们已经在最新的运行时测试版中修复了这个问题,结构通常不能很好地执行。
就个人而言,我会说像这样的C ++技巧通常不会很好地延续到C#中。你可能要学会放手一点;可以采用其他更微妙的方法来提高绩效;)
答案 1 :(得分:2)
你的静态内存管理器究竟在做什么?除非它做了一些不安全的事情(P / Invoke,不安全的代码),否则你看到的行为是程序中的一个错误,而不是由于CLR的行为。
其次,对于结构之间的链接,“指针”是什么意思?你真的是指一个不安全的KdTree *指针吗?不要那样做。相反,在数组中使用索引。由于我希望单个树的所有节点都存储在同一个数组中,因此您不需要单独引用该数组。只需一个索引即可。
最后,如果你真的必须使用KdTree *指针,那么你的静态内存管理器应该使用例如Marshal.AllocHGlobal或其他非托管内存源;它应该将这个大块视为一个KdTree数组(即索引一个KdTree * C风格)和它应该通过碰撞一个“自由”指针来从这个数组中分配节点。
如果您必须调整此数组的大小,那么您当然需要更新所有指针。
这里的基本教训是,不安全指针和托管内存在“固定”块之外进行不混合,当然这些块具有堆栈帧亲和性(即当函数返回时,固定行为消失)。有一种方法可以使用GCHandle.Alloc(yourArray,GCHandleType.Pinned)来固定任意对象,例如你的数组,但你几乎肯定不想沿着那条路走下去。
如果你更详细地描述你在做什么,你会得到更明智的答案。
答案 2 :(得分:1)
如果确实想要这样做,你可以使用GCHandle.Alloc方法指定一个指针应该被固定,而不是像在fixed语句一样在范围的末尾自动释放。 / p>
但是,正如其他人一直在说的那样,这样做会给垃圾收集者带来不必要的压力。那么只创建一个结构,该结构保存在一对节点上,然后管理一个NodePairs数组而不是一个节点数组?
如果你确实想要对一块内存进行完全非托管访问,那么最好直接从非托管堆分配内存,而不是永久固定托管堆的一部分(这可以防止堆被丢弃)能够适当地压缩自己)。一种快速而简单的方法是使用Marshal.AllocHGlobal方法。
答案 3 :(得分:0)
存储这对数组引用和索引是否真的禁止?
答案 4 :(得分:0)
你的静态内存管理器究竟在做什么?除非它做了一些不安全的事情(P / Invoke,不安全的代码),否则你看到的行为是程序中的一个错误,而不是由于CLR的行为。
我实际上是在谈论不安全的指针。我想要的是像Marshal.AllocHGlobal
这样的东西,虽然它的生命周期超过了一个方法调用。经过反思,似乎只使用索引是正确的解决方案,因为我可能已经陷入了模仿c ++代码的过程。
我要在你的帖子中说的一件事是,使用结构可能无助于你所希望的性能。 C#无法内联任何涉及结构的方法调用,即使他们已经在最新的运行时测试版中修复了它,但结构通常不能很好地执行。
我对此进行了一些调查,我发现它已在.NET 3.5SP1中得到修复;我认为这就是你所说的运行时测试版。事实上,我现在明白这种变化占我渲染速度的两倍。现在,结构体积极地在线,在X86系统上大大提高了它们的性能(X64提前具有更好的结构性能)。