首页 > 解决方案 > 高效增长的对象池

问题描述

是否有已建立的模块或良好实践可以有效地处理 Python 3 中的大型对象池?

我所说的“对象池”是指某个类能够:

这是一个基本示例:

class Value:
    __slots__ = ('a','b')
    def __init__(self,a=None,b=None):
        self.a = a
        self.b = b

class BasicPool:
    def __init__(self):
        self.data = []
    def __getitem__(self,k):
        return self.data[k]
    def fetch(self):
        v = Value()
        self.data.append(v)
        return v

class BlockPool:
    def __init__(self,bsize=100):
        self.bsize = bsize
        self.next = bsize
        self.data = []
    def __getitem__(self,k):
        b,k = divmod(k,self.bsize)
        return self.data[b][k]
    def fetch(self):
        self.next += 1
        if self.next >= self.bsize:
            self.data.append([ Value() for _ in range(self.bsize) ])
            self.next = 0
        return self.data[-1][self.next]

BasicPool没有做任何聪明的事情:每当请求一个新实例时,它都会被实例化并附加到底层的list. 另一方面,BlockPool增长了一个预先分配的实例块列表。但令人惊讶的是,预分配在实践中似乎没有好处:

from timeit import default_timer as timer

def benchmark(P):
    N = int(1e6)
    start = timer()
    for _ in range(N): P.fetch()
    print( timer() - start )

print( 'Basic pool:' )
for _ in range(5): benchmark(BasicPool())

#     Basic pool:
#     1.2352294209995307
#     0.5003506309985823
#     0.48115064000012353
#     0.48508202800076106
#     1.1760561199989752

print( 'Block pool:' )
for _ in range(5): benchmark(BlockPool())

#     Block pool:
#     0.7272855400005938
#     1.4875716509995982
#     0.726611527003115
#     0.7369502859983186
#     1.4867010340021807

如您所见,BasicPool总是比BlockPool(我也不知道这些大变化的原因)。对象池必须是 Python 中相当普遍的需求;真的是使用内置的最好方法list.append吗?是否有更智能的容器可用于进一步提高运行时性能,或者这是否由实例化时间主导?

标签: pythonperformance

解决方案


基于 a 的数组的几何增长的重点list是将重新分配开销减少到一个常数因子。该常数可以很容易地小于手动制作块的常数(主要是因为后者的缓慢、解释性操作self.nextself.data。(渐近地说,当然,成本仍然是BlockPool.fetchappend此外,您的基准不包括破坏块的额外成本,也不包括读取时的两步索引。

所以list肯定和它一样好(无需编写自己的 C 代码)。您可以通过继承而不是包含一个来改进BasicPool一点,完全消除字典查找和解释包装器。listfetch__getitem__


推荐阅读