正如我所看到的,构建暂存数组有几种不同的解决方案:
>每次调用该方法时,使用’new’从堆中获取内存.这是我在开始做的,但是它对垃圾收集器施加了很大的压力,每秒钟的一秒钟或两秒钟的ms峰值真的很烦人.
>调用方法时,将scratch数组作为参数传递.问题是这迫使用户对其进行管理,包括适当地对其进行调整,这是一个巨大的痛苦.并且它使得使用或多或少的临时内存困难,因为它改变了API.
>在不安全的上下文中使用stackalloc从程序堆栈中分配临时内存.这将工作正常,除了我需要编译/不安全,并不断地洒在我的代码中的不安全的块,我想避免.
>程序启动时,先分配私有数组一次.这是很好的,除了我不一定知道我需要的数组的大小,直到我可以看一些输入数据.它变得非常混乱,因为您不能将这些私有变量的范围限制为一个单一的方法,所以他们不断污染命名空间.而且随着需要临时内存的方法数量的增加,它的扩展性会降低,因为我正在分配大量的内存,这仅仅是使用了一小部分时间.
>创建某种中央池,并从池中分配临时存储器数组.这样做的主要问题是我没有看到从中央池分配动态大小的数组的简单方法.我可以使用起始偏移量和长度,并且所有的临时内存本质上共享一个大的数组,但是我有很多现有的代码假定double [].而且我必须小心使这样一个池线程安全.
…
有没有人有类似问题的经验?从经验中提供的任何建议/经验教训?
解决方法
在编译器中,数组大小往往很小,因此频繁重复.在你的情况下,如果你有大阵列,那么我会做的是跟随汤姆的建议:简化管理问题,浪费一些空间.当您向池中请求一个大小为x的数组时,将x乘以最接近的幂数,并分配一个该大小的数组,或者从池中获取一个数组.调用者得到一个有点太大的数组,但是它们可以被写入来处理它.在池中搜索适当大小的数组不应太难.或者你可以维护一堆池,一个池的大小为1024,一个为2048,等等.
编写一个线程安全池不是太难,或者你可以使池线程静态,每个线程有一个池.
令人难以置信的是在游戏池中恢复记忆.有几种方法来处理这个问题.首先,如果他们不想牺牲收集压力,您可以简单地要求池内存的用户调用“返回池”方法.
另一种方法是在数组周围编写一个外观封装,使其实现IDisposable,以便您可以使用“using”(*),并在该对象上重新将其重新置于池中. (确保让终结者回到“我需要定稿”位.)复活的终结者让我紧张;我个人喜欢以前的做法,这是我们在罗斯林做的.
(*)是的,这违反了“使用”应该表明非托管资源正在返回到操作系统的原则.本质上,我们通过自己的管理来将托管内存视为非托管资源,所以不是那么糟糕.