Fast mode (Pillow-AllSIMD)¶
Fast mode lets operations use kernels that are faster and whose results may differ from Pillow’s by small, documented amounts. It is on by default: the operations listed below use their fast kernels. The other operations, and every operation in exact mode (fast mode off), compute with Pillow’s arithmetic, or an integer form of it that rounds differently by one level at most in a few places (resize with 16-bit coefficients, RGB to L with 15-bit ones). Fast mode was off by default until October 2026.
This is a Pillow-AllSIMD feature: neither Pillow nor Pillow-SIMD has a fast
mode. Check for it with features.check_feature("fast").
Turning it off¶
Set the PILLOW_ALLSIMD_FAST environment variable before
PIL.Image is imported (PILLOW_SIMD_FAST, its former name, is
read when PILLOW_ALLSIMD_FAST isn’t set). 0, false, off,
no and an empty value turn fast mode off; 1, true, on and
yes turn it on. Any other value gives a warning and leaves the setting
unchanged:
PILLOW_ALLSIMD_FAST=0 python3 app.py
Or call set_fast(), or use the
fast_mode context manager for one block:
from PIL import Image, ImageFilter, features
if features.check_feature("fast"):
Image.set_fast(False)
with Image.fast_mode(False):
blurred = im.filter(ImageFilter.GaussianBlur(2))
The setting is process-wide: it applies to every thread, including while a
fast_mode() block runs. Each operation reads it once, when the operation
starts, so a call never mixes exact and fast kernels.
What it guarantees¶
Exact when off. With fast mode off, the operations run the exact kernels, the same as before fast mode existed.
Deterministic. A fast kernel’s result depends only on its input and arguments, and is the same on every build and architecture of a given Pillow-AllSIMD version. (Large downscales use the exact resize and reduce kernels in another order, so they are as reproducible as those.)
Bounded. For each fast kernel, the largest difference from the exact result and its error against a float reference of the operation are documented below and checked by the tests.
Same shape. Output mode, size and edge handling are those of the exact operation; only pixel values differ.
Not stable across versions. A later release may change a fast kernel’s results when the kernel improves. The exact path follows Pillow.
Turn fast mode off if you compare pixels with reference images made by Pillow, or cache or deduplicate images by their content across versions.
Operations with a fast kernel¶
Speed-ups are for 2560x1600 RGB images on one core of an Intel Core Ultra 9 285H (AVX2) and an NVIDIA Jetson Orin (NEON).
Operation |
Fast kernel |
Speed-up (x86-64, arm64) |
Difference from the exact result |
|---|---|---|---|
|
Pillow’s three box passes as one kernel with 8-bit weights, in one pass over the image; the bands near the edges are the exact result |
radius 0.5: 4.0x, 4.4x; 1: 3.3x, 3.2x; 2: 2.5x, 2.6x; 4: 1.5x, 2.3x |
at most 3 levels, 0.1 to 0.7 on average; closer to a true Gaussian than the exact result |
|
JPEG’s formulas with 14-bit coefficients and one rounding, in place of tables that floor each term |
to YCbCr: 1.5x, 2.2x; to RGB: 1.3x, 2.6x |
at most 1 level; against the float formulas 0.3% and 0.06% of values off by one, against 47% and 32% for the exact result |
|
the same formulas, truncated exactly: one division in 32-bit floats per channel (arm64: for the hue, with the saturation from a table of its 65,536 values) |
2.9x, 3.3x |
hue 1 above the exact result for 0.3% of colours; saturation and value the same |
|
positions in fixed point (1/256 pixel), integer filters with one rounding |
rotate bilinear: 2.7x, 2.6x; bicubic: 2.9x, 3.4x; perspective: 2.1x, 2.3x; L rotate bilinear: 2.8x, 1.9x |
at most 2 levels, 1 on photos (RGBA: more where alpha is low, from the premultiplication), but for the few pixels whose source position is on the image’s edge, which may fall inside in one result and outside in the other; against float interpolation 0.6% of values off by one, against 41% for the exact result |
|
integer interpolation with alpha in 2^-15 units, rounded |
1.7x, 4.1x |
at most 1 level; the float result rounded, but within 0.004 of a half; the exact result is truncated |
|
the two passes fused through a ring of rows; the horizontal sums kept to 2^-6 levels instead of whole ones; the vertical pass in 16-bit products |
upscale to 5478x3424: bilinear 1.17x, 1.43x; bicubic 1.28x, 1.47x; RGBA bilinear 1.17x, 1.31x; downscale to 2048x1280 bicubic: 0.96x, 1.20x |
at most 1 level (RGBA and LA: 2, premultiplied); against a float resize 0.4% to 1.5% of values off by one, against 8% to 25% for the exact result |
|
|
1/8 Lanczos: 1.9x, 2.5x; 1/16: 2.0x, 4.5x |
at most 2 levels at photo sizes; more for small results (32x32: 4, 9 at the edges) |
Other operations, and other arguments of these, use the exact kernels in fast mode too.
Functions¶
- PIL.Image.set_fast(enabled: bool = True) None[source]
Lets operations use faster kernels whose results may differ from Pillow’s by small, documented amounts, or goes back to the exact kernels, which follow Pillow’s arithmetic. On by default; the
PILLOW_ALLSIMD_FASTenvironment variable (orPILLOW_SIMD_FAST, its former name) sets it whenPIL.Imageis imported, soPILLOW_ALLSIMD_FAST=0gives the exact kernels. The setting is process-wide, and each operation reads it once when it starts. The operations with a fast kernel, and how far their results may differ, are listed in Fast mode (Pillow-AllSIMD).Check for the feature with
features.check_feature("fast").- Parameters:
enabled –
Truefor the fast kernels,Falsefor the exact ones.
- PIL.Image.get_fast() bool[source]
Whether operations may use the fast kernels; see
set_fast().
- class PIL.Image.fast_mode(enabled: bool = True)[source]
Context manager that calls
set_fast()on entry and restores the previous setting on exit, for example to use the exact kernels in one place:with Image.fast_mode(False): im = im.filter(ImageFilter.GaussianBlur(2))
The setting is process-wide: it applies to every thread while the block runs.
- Parameters:
enabled – The setting inside the block.