PNG Benchmark¶
Apple-to-apple performance comparison between zerodep png (pure Python) and Pillow (C extensions).
Test Environment
- CPU: x86_64 Linux
- Python: 3.12
- Tool: pytest-benchmark 5.2.3 (mean values reported)
- Reference: Pillow 12.2.0
- Last Updated: 2026-04-27
Implementations¶
| Implementation | File/Package | Description |
|---|---|---|
| zerodep | png.py |
Pure-Python PNG/BMP codec using stdlib zlib |
| Pillow | (reference) | Full-featured image library with C extensions |
Test Images¶
| Label | Description |
|---|---|
| Small | 64x64 RGBA (16 KB pixels) |
| Medium | 256x256 RGBA (256 KB pixels) |
| Large | 1024x1024 RGBA (4 MB pixels) |
Key Takeaways¶
- Pillow is significantly faster -- this is expected since Pillow uses compiled C extensions (libpng, zlib-ng) while zerodep is pure Python. The core bottleneck is the per-byte row filter loop.
- PNG decode is mostly zlib -- the
zlib.decompresscall (C code) dominates; the pure-Python unfilter loop adds overhead proportional to image size. - PNG encode is filter-bound -- the minimum-sum heuristic tries all 5 filters per row, making encode ~5x more work than decode per pixel.
- BMP is optimized -- BMP codec uses
bytearrayslice assignment for BGR↔RGB channel swap, which runs at C level. This makes BMP encode/decode only ~4-6x slower than Pillow (vs ~20x for PNG). - zerodep targets different use cases -- the tradeoff is zero dependencies and a single-file drop-in:
- Generating QR codes, charts, or thumbnails
- Reading image metadata or pixel data
- Matrix data compression via PNG row filters
- Environments where C extensions are unavailable
Run It Yourself¶
pip install pytest pytest-benchmark Pillow numpy
pytest png/test_png_benchmark.py --benchmark-only -v
Latest CI Results¶
Updated automatically on each release via Benchmark CI.