共计 3653 个字符,预计需要花费 10 分钟才能阅读完成。
Pond
Pond 是一个用于 Python 的高性能对象池库,它的内存占用更小、命中率更高。更多细节能够参阅咱们的用户指南或我的博客(https://qin.news)。
English | 中文版
GitHub 地址:
https://github.com/t-baby/pondpond/
Pond 是一个 Python 中高效的通用对象池,具备性能好、内存占用小、命中率高的特点。基于近似统计的依据频率主动回收的能力,可能主动调整每个对象池的闲暇对象数量。
因为目前 Python 目前没有比拟好的、测试用例齐备、代码正文齐备、文档欠缺的对象池化库,同时目前的支流对象池库也没有比拟智能的主动回收机制。Pond 可能是 Python 中第一个社区公开的测试用例残缺,覆盖率 90% 以上、代码正文齐备、文档欠缺的对象池化库。
Pond 灵感来自于 Apache Commons Pool、Netty Recycler、HikariCP、Caffeine,汇合了多家的长处。其次 Pond 通过应用近似计数的形式以极小的内存空间统计每个对象池的应用频率,并且主动回收。
流量较为随机均匀的状况下,默认策略和权重能够升高 48.85% 内存占用,借取命中率 100%。
流量较为合乎 2/8 定律的状况下,默认策略和权重能够升高 45.7% 内存占用,借取命中率 100%。
设计概述
Pond 次要由 FactoryDict、Counter、PooledObjectTree 三局部以及一个独自的回收线程形成。
FactoryDict
应用 Pond 须要实现对象工厂 PooledObjectFactory,PooledObjectFactory 提供对象的创立、初始化、销毁、验证等操作,由 Pond 调用。所以为了让对象池反对寄存齐全不同的对象,Pond 应用了一个字典来记录每个工厂类的名称和本人实现的工厂类的实例化对象。
每个 PooledObjectFactory 应该具备创建对象、销毁对象、验证对象是否还可用、重置对象四个性能。
比拟特地的是 Pond 反对主动重置对象,因为某些场景下可能会存在对象中要先赋值进行传递,传递完又被回收的状况,为了防止净化倡议这种场景下无比实现这个性能。
Counter
Counter 中保留了一个近似计数器。
PooledObjectTree
PooleedObjectTree 是个字典,每个 key 对应着一个先进先出的队列,这些队列都是线程平安的。每个队列中保留着多个 PooleedObject。PooledObejct 保留了创立工夫、最初借出的工夫以及理论须要的对象。
线程平安
Pond 的借用和回收都是线程平安的。Python 的 queue 模块提供了一个实用于多线程编程的先进先出(FIFO)数据结构。它能够用来平安地在生产者和消费者线程之间传递音讯或其余数据。锁是调用者来解决的,所有多个线程可能平安且容易的应用同样的 Queue 实例工作。而 Pond 的借用和回收都是在操作 queue,所以根本能够认为是线程平安的。
借出机制
在应用 Pond 借出一个对象时,会先查看想要借出的对象的品种是否曾经在 PooledObjectTree 存在,如果存在会查看这个对象的对象池是否为空,如果为空会创立一个新的。
如果对象池中有多余的对象,会利用 queue 弹出一个对象并验证这个对象是否可用。如果不可用会主动调用所属的 Factory 清理销毁该对象,同时清理它在 Python 中的 GC 计数,让它更快被 GC 回收,一直拿取下一个直至有可用的。
如果这个对象可用,则会间接返回。当然无论是从对象池中取出对象还是新创建了一个对象,都会利用 Counter 减少一个计数。
回收机制
回收一个对象时会判断指标对象池存不存在,如果存在会查看对象池是否曾经满了,满了的话会主动销毁要偿还的这个对象。
而后会查看这个对象是否曾经被借出太长时间,如果超过了配置的最长工夫同样会被清理掉。
主动回收
主动回收时每隔一段时间,默认是 300 s,就会执行一次。主动清理不常常应用的对象池中的对象。
应用阐明
你能够先装置 Pond 的库并且在你的我的项目中援用。
pip install pondpond
from pond import Pond, PooledObjectFactory, PooledObject
首先你须要申明一个你想要放入的类型的对象的工厂类,比方上面的例子咱们心愿池化的对象是 Dog,所以咱们先申明一个 PooledDogFactory 类,并且实现 PooledObjectFactory。
class Dog:
name: str
validate_result:bool = True
class PooledDogFactory(PooledObjectFactory):
def creatInstantce(self) -> PooledObject:
dog = Dog()
dog.name = "puppy"
return PooledObject(dog)
def destroy(self, pooled_object: PooledObject):
del pooled_object
def reset(self, pooled_object: PooledObject) -> PooledObject:
pooled_object.keeped_object.name = "puppy"
return pooled_object
def validate(self, pooled_object: PooledObject) -> bool:
return pooled_object.keeped_object.validate_result
接着你须要创立 Pond 的对象:
pond = Pond(borrowed_timeout=2,
time_between_eviction_runs=-1,
thread_daemon=True,
eviction_weight=0.8)
Pond 能够传递一些参数进去,别离代表:
borrowed_timeout
:单位为秒,借出对象的最长期限,超过期限的对象偿还时会主动销毁不会放入对象池。
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 注册这个工厂对象,默认会应用 factory 的类名作为 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/
援用
@software{Pond,
author = {Andy Qin},
title = {{Pond: A high performance object-pooling library for Python}},
year = {2022},
url = {https://github.com/T-baby/pondpond},
}