Give the slowest AVIF encode at the limit across quality
check / check (push) Canceled after 0s

The AVIF times given before, 25 seconds with an alpha channel and 15
without, came from quality 85 and 100 only. Measured again with the
runtime image's libvips on one thread at qualities 1 to 100, 4096x4096,
8192x2048 and 2048x8192 random pixels take up to about 34 seconds with
an alpha channel and 22 without, the slowest at quality 90 to 99. That
is under 40, so the limit stays at 16,777,216 pixels. The doc comments,
README.md and TODO.md now give those times. WebP and JPEG XL, measured
the same way, stay within the times already given, which now say they
hold at any quality. The TODO.md entry moves to the top of Completed
Steps.

Model: opus-5-5
This commit is contained in:
2026-10-15 22:41:49 +00:00
parent 0d4f94221f
commit 21c4838c4b
3 changed files with 32 additions and 28 deletions
+5 -4
View File
@@ -397,11 +397,12 @@ takes up to about 1 second as JPEG, 15 as PNG and 16 as GIF, the slowest of 8 or
16 bits per sample, with or without an alpha channel. The WebP and JPEG XL 16 bits per sample, with or without an alpha channel. The WebP and JPEG XL
encoders run below libvips' default effort, and AVIF at the lowest effort govips encoders run below libvips' default effort, and AVIF at the lowest effort govips
can set, so that no encode takes more than 40 seconds of the default can set, so that no encode takes more than 40 seconds of the default
`downstream_timeout` of 60: at quality 85 or 100 and 8 or 16 bits per sample, `downstream_timeout` of 60: at any quality and 8 or 16 bits per sample,
8192x8192 random pixels with an alpha channel take up to about 23 seconds as 8192x8192 random pixels with an alpha channel take up to about 23 seconds as
WebP and 27 as JPEG XL, and 4096x4096 or 8192x2048 up to about 25 as AVIF, which WebP and 27 as JPEG XL, and 4096x4096, 8192x2048 or 2048x8192 up to about 34 as
would take about 99 at 8192x8192. Without an alpha channel they take up to about AVIF, which at quality 85 would take about 96 at 8192x8192. Without an alpha
16, 13 and 15 seconds. Decoding such a source adds up to about 11 seconds. channel they take up to about 16, 13 and 22 seconds. Decoding such a source adds
up to about 11 seconds.
The login form (`POST /`) is limited to 5 attempts per minute per client The login form (`POST /`) is limited to 5 attempts per minute per client
address, counting an IPv6 client by its /64; an attempt over the limit is address, counting an IPv6 client by its /64; an attempt over the limit is
+12 -11
View File
@@ -30,6 +30,18 @@ P2: security: per-origin rate limiting
# Completed Steps # Completed Steps
- 2026-10-15 a WebP, AVIF or JPEG XL encode at the limits takes at most about 34
seconds (closes #272): with one libvips thread, 8192x8192 random pixels with
an alpha channel took about 61 seconds to encode as WebP, 96 as AVIF and 81 as
JPEG XL, so with the default `downstream_timeout` of 60 seconds such an image
was never delivered, yet its encode held a processing slot to the end. WebP is
now saved at effort 2 and JPEG XL at effort 4, which take up to about 23 and
27 seconds. AVIF was already at the lowest effort govips can set, so an AVIF
output of more than 16,777,216 pixels is refused with 400 before anything is
decoded; 4096x4096, 8192x2048 or 2048x8192 takes up to about 34 seconds. Each
time is the slowest at any quality. Resizing with `fit=cover` already decodes
the input, so the output's dimensions are worked out before resizing.
`README.md` gives the limit and the new times.
- 2026-10-15 the README says how a source URL with a query is written into an - 2026-10-15 the README says how a source URL with a query is written into an
image URL, and the image route accepts it (closes #255): the query follows the image URL, and the image route accepts it (closes #255): the query follows the
source path after `%3F`. pixa now reads the image URL's path decoded once, so source path after `%3F`. pixa now reads the image URL's path decoded once, so
@@ -76,17 +88,6 @@ P2: security: per-origin rate limiting
directive is removed, and gosec's new path check is marked as not applying to directive is removed, and gosec's new path check is marked as not applying to
the fixed config file locations. The agent guidance moves unchanged from the fixed config file locations. The agent guidance moves unchanged from
`CLAUDE.md` to a root `AGENTS.md`, where `REPO_POLICIES.md` keeps it. `CLAUDE.md` to a root `AGENTS.md`, where `REPO_POLICIES.md` keeps it.
- 2026-10-15 a WebP, AVIF or JPEG XL encode at the limits takes at most about 27
seconds (closes #272): with one libvips thread, 8192x8192 random pixels with
an alpha channel took about 61 seconds to encode as WebP, 99 as AVIF and 81 as
JPEG XL, so with the default `downstream_timeout` of 60 seconds such an image
was never delivered, yet its encode held a processing slot to the end. WebP is
now saved at effort 2 and JPEG XL at effort 4, which take up to about 23 and
27 seconds. AVIF was already at the lowest effort govips can set, so an AVIF
output of more than 16,777,216 pixels is refused with 400 before anything is
decoded; 4096x4096 or 8192x2048 takes up to about 25 seconds. Resizing with
`fit=cover` already decodes the input, so the output's dimensions are worked
out before resizing. `README.md` gives the limit and the new times.
- 2026-10-15 the request metrics record any method that is not a standard one as - 2026-10-15 the request metrics record any method that is not a standard one as
`other` (closes #274): their `method` label was each request's method as sent, `other` (closes #274): their `method` label was each request's method as sent,
and Go's HTTP server accepts any method name, so a client sending made-up and Go's HTTP server accepts any method name, so a client sending made-up
+15 -13
View File
@@ -145,9 +145,10 @@ var ErrOutputTooLarge = errors.New("output image dimensions exceed maximum")
// MaxAVIFOutputPixels is the most pixels an AVIF output may have, so that an // MaxAVIFOutputPixels is the most pixels an AVIF output may have, so that an
// encode takes at most 40 seconds of the default downstream_timeout of 60 // encode takes at most 40 seconds of the default downstream_timeout of 60
// seconds; AVIF is already at the lowest effort govips can set (avifEffort). // seconds; AVIF is already at the lowest effort govips can set (avifEffort).
// With one thread, as pixad runs libvips, saving 4096x4096 or 8192x2048 // With one thread, as pixad runs libvips, saving 4096x4096, 8192x2048 or
// random pixels with an alpha channel takes up to about 25 seconds, and // 2048x8192 random pixels with an alpha channel takes up to about 34
// 5120x4096 took 39. // seconds, the slowest at any quality and at 8 or 16 bits per sample, and
// 5120x4096 at 8 bits already takes about 36.
const MaxAVIFOutputPixels = 4096 * 4096 const MaxAVIFOutputPixels = 4096 * 4096
// ErrAVIFOutputTooLarge is returned, before anything is decoded, when an AVIF // ErrAVIFOutputTooLarge is returned, before anything is decoded, when an AVIF
@@ -680,8 +681,8 @@ func exportGIF(img *vips.ImageRef) ([]byte, error) {
// default of 4 so that an encode takes at most 40 seconds of the default // default of 4 so that an encode takes at most 40 seconds of the default
// downstream_timeout of 60 seconds. With one thread, as pixad runs libvips, // downstream_timeout of 60 seconds. With one thread, as pixad runs libvips,
// saving an 8192x8192 image of random pixels with an alpha channel takes up // saving an 8192x8192 image of random pixels with an alpha channel takes up
// to about 23 seconds at 2, and up to about 16 without one; with an alpha // to about 23 seconds at 2, and up to about 16 without one, at any quality;
// channel, 3 takes about 50 seconds and 4 about 61. // with an alpha channel, 3 takes about 50 seconds and 4 about 61.
const webpEffort = 2 const webpEffort = 2
// exportWebP encodes img as lossy WebP at quality and webpEffort, without // exportWebP encodes img as lossy WebP at quality and webpEffort, without
@@ -697,12 +698,13 @@ func exportWebP(img *vips.ImageRef, quality int) ([]byte, error) {
} }
// avifEffort is the AVIF effort, from 0 (fastest) to 9; 1 is the lowest // avifEffort is the AVIF effort, from 0 (fastest) to 9; 1 is the lowest
// govips can set. With one thread, as pixad runs libvips, 1 takes about 58 // govips can set. With one thread, as pixad runs libvips, 1 takes about 52
// seconds to save an 8192x8192 RGB image of random pixels, and about 99 // seconds to save an 8192x8192 RGB image of random pixels at quality 85,
// with an alpha channel, against the default downstream_timeout of 60 // and about 96 with an alpha channel, against the default
// seconds, so an AVIF output is held to MaxAVIFOutputPixels. On an image of // downstream_timeout of 60 seconds, so an AVIF output is held to
// milder noise, which 1 saves in about 12 seconds, 2 takes nearly a minute // MaxAVIFOutputPixels. On an image of milder noise, which 1 saves in about
// and libvips' default, 4, takes minutes. // 12 seconds, 2 takes nearly a minute and libvips' default, 4, takes
// minutes.
const avifEffort = 1 const avifEffort = 1
// avifBitdepth is the AVIF bit depth, 8 bits per sample for every image. // avifBitdepth is the AVIF bit depth, 8 bits per sample for every image.
@@ -733,8 +735,8 @@ const jxlResolution = 72 / 25.4
// libvips' default of 7 so that an encode takes at most 40 seconds of the // libvips' default of 7 so that an encode takes at most 40 seconds of the
// default downstream_timeout of 60 seconds. With one thread, as pixad runs // default downstream_timeout of 60 seconds. With one thread, as pixad runs
// libvips, saving an 8192x8192 image of random pixels with an alpha channel // libvips, saving an 8192x8192 image of random pixels with an alpha channel
// takes up to about 27 seconds at 4, and up to about 13 without one; with an // takes up to about 27 seconds at 4, and up to about 13 without one, at any
// alpha channel, 5 takes about 43 seconds and 7 about 81. // quality; with an alpha channel, 5 takes about 43 seconds and 7 about 81.
const jxlEffort = 4 const jxlEffort = 4
// exportJXL encodes img as JPEG XL at quality and jxlEffort, without // exportJXL encodes img as JPEG XL at quality and jxlEffort, without