c# – 托管环境中的临时内存

我正在C#中进行流体模拟.每个循环都需要计算出离散点处的流体速度.作为该计算的一部分,我需要几十千字节的临时空间来保存一些双[]数组(数组的确切大小取决于某些输入数据).数组只需要使用它们的方法的持续时间,并且有一些不同的方法需要这样的划痕空间.

正如我所看到的,构建暂存数组有几种不同的解决方案:

>每次调用方法时,使用’new’从堆中获取内存.这是我在开始做的,但是它对垃圾收集器施加了很大的压力,每秒钟的一秒钟或两秒钟的ms峰值真的很烦人.
>调用方法时,将scratch数组作为参数传递.问题是这迫使用户对其进行管理,包括适当地对其进行调整,这是一个巨大的痛苦.并且它使得使用或多或少的临时内存困难,因为它改变了API.
>在不安全的上下文中使用stackalloc从程序堆栈中分配临时内存.这将工作正常,除了我需要编译/不安全,并不断地洒在我的代码中的不安全的块,我想避免.
>程序启动时,先分配私有数组一次.这是很好的,除了我不一定知道我需要的数组的大小,直到我可以看一些输入数据.它变得非常混乱,因为您不能将这些私有变量的范围限制为一个单一的方法,所以他们不断污染命名空间.而且随着需要临时内存的方法数量增加,它的扩展性会降低,因为我正在分配大量的内存,这仅仅是使用了一小部分时间.
>创建某种中央池,并从池中分配临时存储器数组.这样做的主要问题是我没有看到从中央池分配动态大小的数组的简单方法.我可以使用起始偏移量和长度,并且所有的临时内存本质上共享一个大的数组,但是我有很多现有的代码假定double [].而且我必须小心使这样一个池线程安全.

有没有人有类似问题的经验?从经验中提供的任何建议/经验教训?

解决方法

我同情你的情况当我在Roslyn工作时,我们非常仔细地考虑了从分配临时工作阵列的收集压力导致的潜在性能问题.我们解决解决方案是一个汇集策略.

在编译器中,数组大小往往很小,因此频繁重复.在你的情况下,如果你有大阵列,那么我会做的是跟随汤姆的建议:简化管理问题,浪费一些空间.当您向池中请求一个大小为x的数组时,将x乘以最接近的幂数,并分配一个该大小的数组,或者从池中获取一个数组.调用者得到一个有点太大的数组,但是它们可以被写入来处理它.在池中搜索适当大小的数组不应太难.或者你可以维护一堆池,一个池的大小为1024,一个为2048,等等.

编写一个线程安全池不是太难,或者你可以使池线程静态,每个线程有一个池.

令人难以置信的是在游戏池中恢复记忆.有几种方法来处理这个问题.首先,如果他们不想牺牲收集压力,您可以简单地要求池内存的用户调用“返回池”方法.

另一种方法是在数组周围编写一个外观封装,使其实现Idisposable,以便您可以使用“using”(*),并在该对象上重新将其重新置于池中. (确保让终结者回到“我需要定稿”位.)复活的终结者让我紧张;我个人喜欢以前的做法,这是我们在罗斯林做的.

(*)是的,这违反了“使用”应该表明非托管资源正在返回到操作系统的原则.本质上,我们通过自己的管理来将托管内存视为非托管资源,所以不是那么糟糕.

相关文章

在要实现单例模式的类当中添加如下代码:实例化的时候:frmC...
1、如果制作圆角窗体,窗体先继承DOTNETBAR的:public parti...
根据网上资料,自己很粗略的实现了一个winform搜索提示,但是...
近期在做DSOFramer这个控件,打算自己弄一个自定义控件来封装...
今天玩了一把WMI,查询了一下电脑的硬件信息,感觉很多代码都...
最近在研究WinWordControl这个控件,因为上级要求在系统里,...