libvips demand-driven execution model
libvips is not a traditional image library that reads in rows and processes them one at a time. It is a demand-driven execution engine — operations you apply to an image are only performed together when someone actually requests pixels. This design is both the source of libvips’ speed and the cause of several common misunderstandings.
You built a pipeline, but nothing happened
Section titled “You built a pipeline, but nothing happened”This code:
image, err := vips.NewImageFromFile("input.jpg")if err != nil { return err}defer image.Close()
if err := image.Resize(0.5, vips.KernelLanczos3); err != nil { return err}if err := image.GaussianBlur(3.0); err != nil { return err}The loader normally reads enough data to identify the format and metadata, while subsequent calls build a processing graph: input.jpg → Load → Resize(0.5) → GaussianBlur(3.0) → (waiting for demand). The complete result has not been evaluated and stored. An export or write requests the required regions from downstream to upstream.
Where demand comes from
Section titled “Where demand comes from”These actions trigger real evaluation:
| Trigger | Explanation |
|---|---|
ExportJpeg() / ExportWebp() etc. |
Encoder reads rows from the result one at a time |
SaveToWriter() / TranscodeStream() |
Write target requests pixels in chunks |
| Another image reads from this one (composite, join) | Downstream operation triggers upstream evaluation |
Line cache
Section titled “Line cache”libvips does not cache the whole image in memory at once. It keeps a line buffer cache — the small set of rows needed to satisfy the current region request.
This means:
- If you read top-to-bottom sequentially, libvips only needs a few rows in memory.
- If you need random access (e.g., 90° rotation needs the last row first), libvips may need to cache the whole input — or fail with
out of order read.
Sequential vs random access
Section titled “Sequential vs random access”libvips supports two access modes:
| Mode | Behaviour | Peak RAM |
|---|---|---|
| Random (default) | Any region may be read in any order | Cached regions of the input |
| Sequential | Top-to-bottom, single-pass only | Dominated by image width, operation windows, and codec buffering |
Sequential mode must be enabled explicitly (access="sequential" or [access=sequential]), and it restricts what operations you can do — rotation, vertical flip, and other non-linear reads will fail.
Why operation order affects memory
Section titled “Why operation order affects memory”// Downsample first when the product permits blurring at lower resolution.if err := image.Resize(0.1, vips.KernelLanczos3); err != nil { return err}if err := image.GaussianBlur(5.0); err != nil { return err}
// If the product requires a full-resolution blur, the order cannot be// exchanged and the operation will cost more.Downsampling first means every subsequent operation works on fewer pixels. This principle applies to any image library, but is especially important in libvips because the operation graph determines the actual computation at evaluation time.
Common misconceptions
Section titled “Common misconceptions”- Myth 1: “vips.Startup() loads an image.” No — it only initialises the libvips runtime.
- Myth 2: “The image is processed before Close().” Processing happens on demand during export; Close just releases the image reference.
- Myth 3: “Every operation creates a complete intermediate image.” libvips normally evaluates chained operations by region; materialization depends on access patterns.
- Myth 4: “Demand-driven means slow.” The opposite — demand-driven means your program only computes pixels it actually needs.
Risk notes
Section titled “Risk notes”- Out of Order Read: Random-access operations on sequential images will fail.
- Cache Miss Penalty: Random access on large images is significantly slower than sequential.
- Binding default differences: govips defaults to random; sharp configures
sequentialRead; pyvips configuresaccess.
Learn about sequential and random access in detail.
Sources
Section titled “Sources”- libvips How libvips works
- govips v2.19.0 README Streaming I/O section
- libvips source
conversion/sequential.c