Skip to content

Performance

Node.js uses a libuv-managed thread pool when processing asynchronous calls to native modules such as sharp.

The maximum number of images that sharp can process in parallel is controlled by libuv’s UV_THREADPOOL_SIZE environment variable, which defaults to 4.

When using more than 4 physical CPU cores, set this environment variable before the Node.js process starts to increase the thread pool size.

Terminal window
export UV_THREADPOOL_SIZE="$(lscpu -p | egrep -v "^#" | sort -u -t, -k 2,4 | wc -l)"

libvips uses a glib-managed thread pool to avoid the overhead of spawning new threads.

The default number of threads used to concurrently process each image is the same as the number of CPU cores, except when using glibc-based Linux without jemalloc, where the default is 1 to help reduce memory fragmentation.

Use sharp.concurrency() to manage the number of threads per image.

To reduce memory fragmentation when using the default Linux glibc memory allocator, set the MALLOC_ARENA_MAX environment variable before the Node.js process starts to reduce the number of memory pools.

Terminal window
export MALLOC_ARENA_MAX="2"

A test to benchmark the performance of this module relative to alternatives.

Greater libvips performance can be expected with caching enabled (default) and using 8+ core machines, especially those with larger L1/L2 CPU caches.

The I/O limits of the relevant (de)compression library will generally determine maximum throughput.

  • jimp v1.6.0 - Image processing in pure JavaScript.
  • imagemagick v0.1.3 - Supports filesystem only and “has been unmaintained for a long time”.
  • gm v1.25.1 - Fully featured wrapper around GraphicsMagick’s gm command line utility, but “has been sunset”.
  • sharp v0.34.3 / libvips v8.17.0 - Caching within libvips disabled to ensure a fair comparison.
  • AWS EC2 us-west-2 c7a.xlarge (4x AMD EPYC 9R14)
  • Ubuntu 25.04
  • Node.js 24.3.0
  • AWS EC2 us-west-2 c8g.xlarge (4x ARM Graviton4)
  • Ubuntu 25.04
  • Node.js 24.3.0

Decompress a 2725x2225 JPEG image, resize to 720x588 using Lanczos 3 resampling (where available), then compress to JPEG at a “quality” setting of 80.

Note: jimp does not support Lanczos 3, bicubic resampling used instead.

PackageI/OOps/secSpeed-up
jimpbuffer2.401.0
jimpfile2.601.1
imagemagickfile9.704.0
gmbuffer11.604.8
gmfile11.724.9
sharpstream59.4024.8
sharpfile62.6726.1
sharpbuffer64.4226.8
PackageI/OOps/secSpeed-up
jimpbuffer2.241.0
jimpfile2.471.1
imagemagickfile10.424.7
gmbuffer12.805.7
gmfile12.885.7
sharpstream45.5820.3
sharpfile47.9921.4
sharpbuffer49.2022.0

Decompress a 2048x1536 RGBA PNG image, premultiply the alpha channel, resize to 720x540 using Lanczos 3 resampling (where available), unpremultiply then compress as PNG with a “default” zlib compression level of 6 and without adaptive filtering.

Note: jimp does not support premultiply/unpremultiply.

PackageI/OOps/secSpeed-up
imagemagickfile6.061.0
gmfile8.441.4
jimpbuffer10.981.8
sharpfile28.264.7
sharpbuffer28.704.7
PackageI/OOps/secSpeed-up
imagemagickfile7.091.0
gmfile8.931.3
jimpbuffer10.281.5
sharpfile23.813.4
sharpbuffer24.193.4

Requires Docker.

Terminal window
git clone https://github.com/lovell/sharp.git
cd sharp/test/bench
./run-with-docker.sh