#213: Tune the Garbage Collector
The Garbage Collector (GC) in Python frees the memory from no longer needed objects. This usually works without any problem in the background and prevents us from memory leaks. However, for certain tasks we may pay a performance penalty that we can prevent by tuning the GC. Let us explore the options we have.
The defaults for garbage collection
As a rule of thumb (and not an exact value) we can expect the GC to run once for every 700 allocated objects. That is the first value we get back when we check the thresholds:
Depending on the Python version and the implementation you use, these values may differ. There is a lot of work put into optimizing garbage collection and that makes the GC runs non-deterministic – it may or may not run even when it has reached the threshold. We can trigger a GC run with this command:
Creating lots of objects
The more objects we create, the more work the GC has. Especially when we turn large JSON files into object graphs, we may create many thousands of objects that the GC has to check again and again while we load the data.
We use this little code snipped to simulate the creation of 10 million objects and measure how long it takes with the default GC behaviour:
If I run this code on my machine, it takes around 9 seconds to create all the objects. The GC runs, even if all objects are still in use.
Tune the GC
We can tune the GC by setting a different value for the thresholds. If we use 50000 objects instead of the 700, we can do it like this:
With this setting the allocation of 10 million objects completes in 6 seconds.
Turn off the GC
As long as we can guarantee that we do not create cycles between our objects, we can disable the GC for our load operation and turn it back on when we are done:
If I run the code to allocate the 10 million objects once more, it only takes 4 seconds.
Comparison
The fewer the GC runs, the faster our “load” operation can be. I repeated the examples from above with different numbers of objects and got these values:
| GC? | 1m | 10m | 100m |
|---|---|---|---|
| No change | 0.8s | 9s | 75s |
| Tuned | 0.5s | 6s | 58s |
| Off | 0.4s | 4s | 46s |
The more objects we have, the more time we spend on garbage collection.
Conclusion
The best way to improve the performance of your Python application is not to allocate that many objects in the first place. But sometimes we must do it and for such cases tuning the GC may be a quick way to improve the runtime. Just make sure that you do not produce cycles in your objects, otherwise the performance gain quickly turns into a debugging nightmare that cost much more time that you could save by turning the GC off.