当前位置: 首页 > 后端技术 > Python

POND:高效的Python通用对象池库

时间:2023-03-26 12:09:55 Python

PondPond是一个高性能的Python对象池库,内存占用更小,命中率更高。更多详细信息,请参阅我们的用户指南或我的博客(https://qin.news)。英语|中文版GitHub地址:https://github.com/t-baby/pondpond/Pond是Python中高效的通用对象池,具有性能好、内存占用小、命中率高等特点。根据近似统计的频率自动回收的能力可以自动调整每个对象池中空闲对象的数量。因为Python目前还没有一个测试用例完整、代码注释完整、文档完整的比较好的对象池库。同时,目前主流的对象池库并没有相对智能的自动回收机制。Pond可能是第一个社区发布的Python对象池库,测试用例完整,覆盖率超过90%,代码注释完整,文档完整。Pond受到ApacheCommonsPool、NettyRecycler、HikariCP和Caffeine的启发,并结合了许多优点。其次,Pond采用近似计数的方式统计每个内存空间非常小的对象池的使用频率,并自动回收。当流量比较随机和平均时,默认策略和权重可以减少48.85%的内存占用,借用命中率为100%。当流量更符合2/8定律时,默认的策略和权重可以减少45.7%的内存占用,借位命中率为100%。设计概述Pond主要由FactoryDict、Counter、PooledObjectTree和一个独立的回收线程组成。FactoryDict使用Pond来实现对象工厂PooledObjectFactory。PooledObjectFactory提供对象创建、初始化、销毁、验证等操作,由Pond调用。所以为了让对象池能够支持存储完全不同的对象,Pond使用了一个字典来记录每个工厂类的名称和它实现的工厂类的实例化对象。每个PooledObjectFactory都应该有创建对象、销毁对象、验证对象是否还可用、重置对象四个功能。比较特别的是,Pond支持对象的自动重置,因为在某些场景下,可能会出现对象需要先赋值才能转移,转移之后再回收的情况。为了避免污染,建议在此类场景下实现此功能。CounterCounter中存储了一个近似计数器。PooledObjectTreePooleedObjectTree是一个字典,每个key对应一个先进先出的队列,这些队列是线程安全的。每个队列包含多个PooleedObjects。PooledObejct保存了创建时间、上次借出时间和实际需要的对象。线程安全的池塘借用和回收是线程安全的。Python的队列模块提供了适合多线程编程的先进先出(FIFO)数据结构。它可用于在生产者线程和消费者线程之间安全地传递消息或其他数据。锁定由调用者处理,因此多个线程可以安全且轻松地使用同一个Queue实例。Pond的借用和回收都是操作队列,所以基本上可以认为是线程安全的。借出机制在使用Pond借出对象时,会先检查你要借出的对象类型是否已经存在于PooledObjectTree中。如果存在,就会检查这个对象的对象池是否为空。如果它是空的,它将创建一个新的。.如果对象池中存在冗余对象,则使用队列弹出对象并验证对象是否可用。如果不可用,会自动调用Factory清理销毁该对象,同时在Python中清理其GC计数,使其更快的被GC回收,一直拿下一个直到它是可用的。如果对象可用,则直接返回。当然无论是从对象池中取出对象,还是创建新的对象,都会使用计数器增加一个计数。回收机制在回收一个对象时,会判断目标对象池是否存在。如果存在,它将检查对象池是否已满。如果满了,会自动销毁要返回的对象。然后会检查这个对象是否被借出时间过长,如果超过了配置的最长时间,也会被清理掉。自动回收每隔一段时间自动回收一次,默认300秒。自动清理不常使用的对象池对象。使用说明您可以先安装Pond的库并在您的项目中引用它。pipinstallpondpondfrompondimportPond,PooledObjectFactory,PooledObject首先需要声明一个工厂类是你要放入的对象的类型,比如下面的例子,我们要池化的对象是Dog,那么我们先声明一个PooledDogFactory类,并实现PooledObjectFactory。类狗:名称:strvalidate_result:bool=TrueclassPooledDogFactory(PooledObjectFactory):defcreatInstantce(self)->PooledObject:dog=Dog()dog.name=“puppy”返回PooledObject(dog)defdestroy(self,pooled_object:PooledObject):delpooled_objectdefreset(self,pooled_object:PooledObject)->PooledObject:pooled_object.keeped_object.name="puppy"returnpooled_objectdefvalidate(self,pooled_object:PooledObject)->bool:returnvalidresult.keeped_object那么你需要创建aPond对象:pond=Pond(borrowed_timeout=2,time_between_eviction_runs=-1,thread_daemon=True,eviction_weight=0.8)Pond可以传入一些参数给它,代表:borrowed_timeout:单位是秒,借出对象的最长周期,超过deadline的对象返回时会自动销毁,不会放入对象池。time_between_eviction_runs:单位为秒,自动回收的时间间隔。thread_daemon:守护线程,如果为True,自动回收线程会在主线程关闭时关闭。eviction_weight:自动回收时的权重。该权重将乘以最大使用频率。对象池中使用频率小于该值的对象将进入清理步骤。实例化工厂类:factory=PooledDogFactory(pooled_maxsize=10,least_one=False)所有继承PooledObjectFactory的都会有自己的构造函数,可以传递pooled_maxsize和least_one两个参数。pooled_maxsize:这个工厂类生成的对象池中可以放置的最大对象数。least_one:如果为True,进入自动清理时,这个工厂类生成的对象的对象池会保留至少一个对象。向Pond注册此工厂对象。默认情况下,工厂的类名会作为PooledObjectTree的key:pond.register(factory)当然你也可以自定义它的名字,名称会作为PooledObjectTree的key:pond。register(factory,name="PuppyFactory")注册成功后,Pond会自动开始根据factory中设置的pooled_maxsize创建对象,直到对象池被填满。借还对象:pooled_object:PooledObject=pond.borrow(factory)dog:Dog=pooled_object.use()pond.recycle(pooled_object,factory)当然可以用名字借还:pooled_object:PooledObject=pond.borrow(name="PuppyFactory")dog:Dog=pooled_object.use()pond.recycle(pooled_object,name="PuppyFactory")完全清理一个对象池:pond.clear(factory)按名称清理一个对象池:pond.clear(name="PuppyFactory")一般情况下只需要使用上面的方法,对象的生成和回收都是全自动的。技术原理详细的技术原理可以参考:https://qin.news/pond/GitHub地址:https://github.com/t-baby/pondpond/Reference@software{Pond,author={AndyQin},title={{Pond:一个用于Python的高性能对象池库}},year={2022},url={https://github.com/T-baby/pondpond},}