0

我正在研究一种池算法,该算法采用分配的对象并将其回收分配以供以后使用。但是,回收对象的问题之一是:

 someObject obj = pool.alloc(); //gives me a new object if no previous allocations, If an allocation has been recycled, returns a previous allocation
 obj.someVariable = "foo";
 pool.recycle(obj);

上面的代码将采用现有的分配并保存它,这样我就不必分配任何额外的内存,以防万一我有另一个 someObject。但是,以下会产生一个问题:

 someObject obj = pool.alloc(); //gives me the above allocation
 obj.otherVariable = "bar";
 obj.dump();

结果,我将得到以下结果:

  someVariable = foo
  otherVariable = bar

上述方法产生了一个问题。如果我出于某种原因(或其他人)有一个不使用对象内部某些变量的算法,那么旧值可能会导致不必要的行为。我已经戳了一下,看看是否有某种方法可以再次调用默认构造函数(坏主意),而 C#(谢天谢地)似乎不允许你这样做。但是,我想知道是否有某种方法可以使用反射来做到这一点?此外,清除对象中的变量是否会破坏避免 malloc(新)的目的?换句话说,如果我花时间清除变量,性能增益会变得最小吗?我正在尝试自学汇集,因此非常感谢任何批评和建议!

4

1 回答 1

1

这是查看池化的一种方式:

只有当您重用一些复杂的设置工作时,池化才是真正需要的,您可以通过检索已经存在的对象来避免这些工作。例如,对象池最著名的应用之一是“数据库连接”的形式。

池化适用于数据库连接 (DBConns),因为 1) DBConn 可以通过连接字符串轻松识别;2) 建立一个 DBConn 需要大量的工作和时间。简单的识别是通过匹配连接字符串来完成的——如果两个连接字符串相同,那么它们建立的连接也应该相同。此外,一旦有了连接字符串,查找服务器地址、打开套接字、进行身份验证和建立连接可能需要数百毫秒。这意味着池对于数据库连接很有效,因为当一个连接被释放时,它可以在下一次为相同的连接字符串请求连接时被重用。

.NET 运行时环境非常擅长快速分配对象并释放它们,因此您不会遇到内存问题。如果您只担心内存使用或分配速度,请不要;您无法通过自行清零内存来击败编译器的性能。但是,如果您的对象有一些复杂、冗长的设置,可以通过从池中检索现有对象来避免,您会发现一些好处。

池化的另一个很好的例子是电子游戏中的粒子系统。您必须创建数百个粒子,经历一个生命周期并被销毁,只有在旧粒子死亡后才能创建新粒子。一个典型的粒子系统将创建 X 个对象作为一个数组,并具有一个 Reset() 函数,该函数将一个死对象恢复为原始位置中新创建的粒子。这再次起作用的原因是因为可以轻松识别粒子,并且设置工作(为图形系统提供纹理,放置位置等)。

您的应用程序是否同时具有“微不足道的匹配识别”功能和可以避免的冗长设置过程?如果没有,只需每次都分配新的对象 - 我敢打赌你不会看到任何性能下降。

编辑:从性能的角度来看,让我们在这里看看:

重置变量 = O(N) 赋值语句;使用反射可以显着增加

实例化一个新对象 = 一次 malloc 调用,一次构造函数调用;复杂程度取决于构造函数代码和内存碎片。

使用反射来重置变量可以工作,但您必须提前知道分配的 O(N) 比您的 malloc 和构造函数更快。对于数据库连接,我知道条件满足;但这对您的游泳池来说是真的吗?

编辑:从您下面的评论中,您可能确实找到了适合合并的情况。如果是这种情况,我建议理想的方法是为Reset()您正在池化的任何类创建一个函数。尝试创建一个IPoolable定义函数的接口Reset()。然后,对于您汇集的每个类,定义Reset()函数,以便将所有关键变量清零。因为它可以编译,所以不会产生反射开销,并且您可以维护动态代码无法实现的特定于对象的优化。

对于池,将池类定义为MyPool<IPoolable>;然后,每当检索到以前回收的对象时,您都可以Reset() 在将其返回给调用者之前调用它。

于 2012-10-08T18:50:44.810 回答