Object pooling is pre-allocating a number of objects to use later. It is done by creating a group of objects (pool) initially and when you need an object, you take one from the pool rather than creating it. When you are done with the object you send it back to the pool. It effectively means reusing of objects. This allows bypassing the object creation overhead and reducing the work of garbage collector. Thus, managing the memory yourself. This is, of course, should be used with care because if you really want to manage the memory, why use a high-level language like Python?
Despite the fact that many programs don't need object pooling, this technique can speed things up, especially in situations explained below.
When to use Object Pooling?
There are two main reasons to use object pooling:
- You need to create and destroy many objects quickly (i.e. limited lifetime of objects)
- Creating many objects that takes time to be initiated (i.e. class constructor does many things)
It is an optimization so it shouldn't be applied until there is a proved bottleneck. It will increase the overall complexity of your program and probably present additional bugs. Also, when you create many objects at start, your program may allocate too much memory throughout its lifetime. As a result, you should carefully analyze the trade-off here.
Sample Implementation
The harbor example is from Paul Graham's book ANSI Common Lisp. Here are two classes, one is using object pooling while the other is not.
class Ship():
def __init__(self):
self.name = None
self.flag = None
self.tons = None
class HarborWithPooling():
def __init__(self, count):
self.pool = [Ship() for i in range(count)]
self.harbor = {}
def enter(self, name, flag, tons):
ship = self.pool.pop()
ship.name = name
ship.flag = flag
ship.tons = tons
self.harbor[name] = ship
def find_ship(self, name):
return self.harbor[name]
def leave(self, name):
ship = self.harbor[name]
self.pool.append(ship)
del self.harbor[name]
class HarborWithoutPooling():
def __init__(self, count):
self.harbor = {}
def enter(self, name, flag, tons):
ship = Ship()
ship.name = name
ship.flag = flag
ship.tons = tons
self.harbor[name] = ship
def find_ship(self, name):
return self.harbor[name]
def leave(self, name):
ship = self.harbor[name]
del self.harbor[name]
Benchmark
Its execution time is measured while enter and leave methods are called 100,000 times in both classes. The test machine has an Intel Core 2 Duo 1.80GHz CPU and it is running Linux. Tested using Python 2.5.4. If you have time to test it on another version or OS, please let me know. You can download the complete source code here.
| Enter | Leave | Total | |
| With Object Pooling | 459 ms | 246 ms | 705 ms |
| Without Object Pooling | 952 ms | 224 ms | 1176 ms |
| Speed Up | %107 | -%9 | %66 |
There is about %66 overall speed increase when using object pooling.
Conclusion
Using object pooling in Python increases total object creation and destruction rate more than two-fold. When used appropriately, it will help developing fast Python programs. Particularly, the programs that needs to be fast such as games.
Further Reading
- Wikipedia, Pool
- Brandon Corfman, Object pooling in Python
- Paul Graham, ANSI Common Lisp, 1996, pp. 226-227
- Brian Goetz, Java theory and practice: Urban performance legends, revisited
Pool image from: Stuck In Customs

