我正在研究一种池化算法,该算法接收一个已分配的对象并将其分配回来以供日后使用。但是,回收对象的问题之一是:
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);
上面的代码将采用现有的分配并保存它,这样我就不必分配任何额外的ram以防我有另一个someObject。但是,以下内容会产生问题:
someObject obj = pool.alloc(); //gives me the above allocation
obj.otherVariable = "bar";
obj.dump();
结果,我将得到以下结果:
someVariable = foo
otherVariable = bar
上述方法会产生问题。如果我由于某种原因(或其他人)有一个算法不使用对象内部的某些变量,旧的值可能会导致不必要的行为。我已经戳了一下,看看是否有某种方法我可以再次调用默认构造函数(坏主意)和C#(谢天谢地)似乎不允许你这样做。但是,我想知道是否有某种方法可以使用反射来做到这一点?清除对象中的变量是否会破坏避免malloc(new)的目的?换句话说,如果我花时间清除变量,性能增益会变得极小吗?我正在努力自学,所以任何批评和建议都非常感谢!
答案 0 :(得分:1)
这是查看汇集的一种方式:
只有在重复使用一些复杂的设置工作时,才能通过检索已存在的对象来避免池化。例如,对象池的最着名的应用程序之一是“数据库连接”的形式。
池为数据库连接(DBConns)工作,因为1)DBConn可以通过连接字符串简单地标识; 2)DBConn需要大量的工作和时间来建立。通过匹配连接字符串来完成普通标识 - 如果两个连接字符串相同,则它们建立的连接也应该相同。此外,一旦有了连接字符串,查找服务器地址,打开套接字,验证和建立连接可能需要数百毫秒。这意味着池对数据库连接很有效,因为当释放连接时,可以在下次为同一连接字符串请求连接时重用它。
.NET运行时环境非常擅长快速分配对象并释放它们,因此您不会遇到内存问题。如果您担心的是内存使用或分配速度,请不要;你不能通过自己清理内存来击败编译器的性能。但是,如果您的对象有一些复杂,冗长的设置,可以通过从池中检索现有对象来避免,您可以找到一些好处。
汇集的另一个好例子是视频游戏中的粒子系统。您需要创建数百个粒子,经历生命周期并被销毁,只有在旧粒子死后才能创建新粒子。典型的粒子系统将X数量的对象创建为数组,并具有Reset()函数,该函数将死对象作为原始位置中新创建的粒子返回生命。这样做的原因是因为粒子可以被简单地识别,并且设置工作(给图形系统提供纹理,放置位置等)。
您的应用程序是否具有“普通匹配识别”功能和可以避免的冗长设置过程?如果没有,只需每次都分配新对象 - 我会打赌你不会看到任何性能下降。
编辑:从效果角度来看,让我们来看看这个:
重置变量= O(N)赋值语句;使用反射可以显着增加
实例化一个新对象=一个malloc调用,一个构造函数调用;复杂性级别取决于构造函数代码和内存碎片。
使用反射来重置变量可以起作用,但您必须事先知道分配的O(N)比malloc和构造函数快。对于数据库连接,我知道满足条件;但是你的游泳池是真的吗?
编辑:根据您的评论,您可能确实找到了合适的汇总案例。如果是这种情况,我建议理想的方法是为您正在汇集的任何类创建一个Reset()
函数。尝试创建定义函数IPoolable
的{{1}}接口。然后,对于您汇集的每个类,定义Reset()
函数,以便将所有关键变量清零。因为它编译,所以不会产生反射开销,并且您可以维护动态代码无法实现的特定于对象的优化。
对于池,将池类定义为Reset()
;然后,无论何时检索到先前已回收的对象,您都可以在将其发送回调用者之前调用MyPool<IPoolable>
。