Generate image picker thumbnails with libvips in parallel (#6686)

Thumbnails were generated one ImageMagick process at a time. Queue the
missing ones and drain them across every core with vipsthumbnail, which
decodes and encodes faster and lets the per-process startup overlap.

Cold cache for a 92 wallpaper directory drops from 14.2s to 1.3s, and
the bundled theme previews from 2.0s to 0.26s.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
David Heinemeier Hansson
2026-08-10 22:20:00 +02:00
committed by GitHub
co-authored by Claude Opus 5
parent c53190be07
commit e4604fbcfb
3 changed files with 35 additions and 4 deletions
+31 -4
View File
@@ -74,8 +74,9 @@ fi
selection_file=$(mktemp) selection_file=$(mktemp)
done_file=$(mktemp) done_file=$(mktemp)
pending_file=$(mktemp)
rm -f "$done_file" rm -f "$done_file"
trap 'rm -f "$selection_file" "$done_file"' EXIT trap 'rm -f "$selection_file" "$done_file" "$pending_file"' EXIT
image_dirs_env="" image_dirs_env=""
for dir in "${image_dirs[@]}"; do for dir in "${image_dirs[@]}"; do
@@ -144,7 +145,8 @@ generate_thumbnail() {
local tmp="$thumbnail.$$.jpg" local tmp="$thumbnail.$$.jpg"
if mkdir "$lock" 2>/dev/null; then if mkdir "$lock" 2>/dev/null; then
if magick "${image}[0]" -auto-orient -resize '1536x864^' -gravity center -extent '1536x864' -strip -quality 82 "$tmp"; then # Callers fan out one generator per image, so keep each vips single-threaded.
if VIPS_CONCURRENCY=1 vipsthumbnail "$image" --size 1536x864 --smartcrop=centre --path "$tmp[Q=82,strip]"; then
mv -f "$tmp" "$thumbnail" mv -f "$tmp" "$thumbnail"
else else
rm -f "$tmp" "$thumbnail" rm -f "$tmp" "$thumbnail"
@@ -186,10 +188,19 @@ thumbnail_for() {
return return
fi fi
generate_thumbnail "$image" "$thumbnail" printf '%s\0%s\0' "$image" "$thumbnail" >>"$pending_file"
fi fi
[[ -f $thumbnail ]] && printf '%s' "$thumbnail" printf '%s' "$thumbnail"
}
# Generate every queued thumbnail at once; each vips run is single-threaded.
drain_pending_thumbnails() {
[[ -s $pending_file ]] || return 0
export -f generate_thumbnail
xargs -a "$pending_file" -0 -n 2 -P "$(nproc)" \
bash -c 'generate_thumbnail "$1" "$2"' _ >/dev/null 2>&1 || true
} }
if [[ $rows_cache_hit != true && -f $rows_cache_file && -f $rows_signature_file ]] && cmp -s "$rows_signature_file" <(printf '%s' "$rows_signature"); then if [[ $rows_cache_hit != true && -f $rows_cache_file && -f $rows_signature_file ]] && cmp -s "$rows_signature_file" <(printf '%s' "$rows_signature"); then
@@ -210,6 +221,22 @@ elif [[ $rows_cache_hit != true ]]; then
fi fi
done done
drain_pending_thumbnails
if [[ -s $pending_file ]]; then
pruned=""
while IFS=$'\t' read -r row_image row_thumbnail; do
[[ -e $row_thumbnail ]] || continue
if [[ -z $pruned ]]; then
pruned="$row_image"$'\t'"$row_thumbnail"
else
pruned+=$'\n'"$row_image"$'\t'"$row_thumbnail"
fi
done <<<"$rows"
rows="$pruned"
fi
if [[ $rows_cacheable == true ]]; then if [[ $rows_cacheable == true ]]; then
printf '%s' "$rows" >"$rows_cache_file" printf '%s' "$rows" >"$rows_cache_file"
printf '%s' "$rows_signature" >"$rows_signature_file" printf '%s' "$rows_signature" >"$rows_signature_file"
+1
View File
@@ -69,6 +69,7 @@ lazydocker
lazygit lazygit
less less
libsecret libsecret
libvips
libyaml libyaml
libreoffice-fresh libreoffice-fresh
llvm llvm
+3
View File
@@ -0,0 +1,3 @@
echo "Generate image picker thumbnails with libvips"
omarchy-pkg-add libvips