文档

性能基准

GetWebP CLI 在各种工作负载下的性能表现及其原因。

性能基准

GetWebP CLI 在各种工作负载下的性能表现及其原因。

另请参阅: README | 命令 | 入门


架构概述#

理解 GetWebP 的性能特征需要理解其处理管道。

WASM 编解码器管道#

GetWebP 使用 jSquash WASM 编解码器,编译自支持 Google Squoosh 的相同 C/C++ 库:

编解码器来源用途
@jsquash/jpegMozJPEGJPEG 解码
@jsquash/pngSquoosh PNG (Rust via wasm-bindgen)PNG 解码
@jsquash/webplibwebpWebP 解码和编码
bmp-js纯 JavaScriptBMP 解码

每个图像经过三阶段管道:

读取文件 (I/O) --> 解码为 RGBA (WASM) --> 编码为 WebP (WASM) --> 写入文件 (I/O)

关键含义: WASM 编解码器在主线程中运行(不是本机 C)。这意味着:

  • 解码 + 编码是 CPU 绑定的,运行速度大约是等效操作的本机 cwebp 的 60-80%。
  • WASM 初始化是一次性成本。 四个 WASM 模块(PNG 解码器、JPEG 解码器、WebP 解码器、WebP 编码器)在首次使用时从嵌入式二进制 blob 编译,然后为会话缓存。这增加了约 100-300ms 到首次调用。
  • 每个图像的内存开销等于原始 RGBA 位图(宽度 x 高度 x 4 字节)加上编码的 WebP 缓冲区。一张 4000x3000 的照片需要约 48 MB 的临时内存。

并发模型#

GetWebP 使用使用 p-limit 的异步并发,而不是操作系统级线程或工作者线程。

                    +--> [解码 + 编码文件 1] --+
主线程 ------+--> [解码 + 编码文件 2] --+--> 结果
   (事件循环)    +--> [解码 + 编码文件 N] --+
  • Pro 计划默认为 os.cpus().length - 1 个并发任务(上限为 32)。可通过 --concurrency 配置。
  • 免费计划强制串行(一次 1 个任务),文件之间有 3 秒延迟。

因为 WASM 执行在解码/编码期间阻止 JavaScript 事件循环,真正的并行性是有限的。然而,I/O 操作(文件读取和写入)与其他任务中的 CPU 工作重叠,产生可衡量的吞吐量增益,大约是 CPU 核心数。

注意: 代码库中存在 worker_threads 实现但未激活。jSquash WASM 模块目前无法可靠地在 Node.js 工作者线程内初始化。如果这在上游解决,线程级并行性将解锁近线性扩展。


基准方法#

测试环境#

重要: 下面的所有数字都是模板。实际结果取决于您的硬件、操作系统、磁盘速度和图像内容。将 [benchmark pending] 值替换为您自己的测量。

参数
CPU[benchmark pending] (例如 Apple M2 Pro,10 核)
RAM[benchmark pending] (例如 16 GB)
磁盘[benchmark pending] (例如 NVMe SSD)
操作系统[benchmark pending] (例如 macOS 14.4)
运行时Bun 编译的二进制 (v1.0.1)
质量默认 (-q 80),除非另有说明

测试数据集#

类别数量平均大小分辨率范围
小 JPEG50~200 KB800x600 -- 1200x900
大 JPEG50~3 MB4000x3000 -- 6000x4000
PNG (照片)50~5 MB3000x2000 -- 4000x3000
PNG (图形/截图)50~800 KB1920x1080
BMP20~10 MB3000x2000
WebP (重新编码)20~400 KB2000x1500

测量方法#

# 单个文件
time getwebp convert photo.jpg -o /tmp/out
 
# 批量(墙钟时间)
time getwebp convert ./dataset -o /tmp/out --concurrency 4

所有时间都是墙钟时间。每个测试运行 3 次;报告中位数。


单文件基准#

在默认质量 (-q 80) 时转换单个图像的时间。

JPEG#

输入大小分辨率时间输出大小节省
200 KB1200x900[benchmark pending] (~0.3s)[pending][pending] (~25--40%)
1 MB2400x1800[benchmark pending] (~0.6s)[pending][pending] (~30--45%)
3 MB4000x3000[benchmark pending] (~1.2s)[pending][pending] (~30--50%)
8 MB6000x4000[benchmark pending] (~2.5s)[pending][pending] (~35--55%)

PNG#

输入大小分辨率类型时间输出大小节省
300 KB1920x1080截图[benchmark pending] (~0.4s)[pending][pending] (~70--85%)
2 MB3000x2000照片[benchmark pending] (~1.0s)[pending][pending] (~60--75%)
5 MB4000x3000照片[benchmark pending] (~2.0s)[pending][pending] (~65--80%)
15 MB6000x4000照片 (alpha)[benchmark pending] (~4.0s)[pending][pending] (~55--70%)

PNG 到 WebP 的转换通常产生最高的节省,因为 PNG 是无损的,而质量为 80 的 WebP 应用有损压缩。

BMP#

输入大小分辨率时间输出大小节省
5 MB1920x1080[benchmark pending] (~0.5s)[pending][pending] (~95%+)
36 MB4000x3000[benchmark pending] (~2.5s)[pending][pending] (~97%+)

BMP 文件是未压缩的位图。转换为 WebP 产生戏剧性的文件大小缩减。请注意,BMP 解码使用纯 JavaScript 库 (bmp-js) 而不是 WASM,对于非常大的文件来说更慢但足以满足典型用途。

WebP (重新编码)#

输入大小分辨率时间输出大小节省
200 KB2000x1500[benchmark pending] (~0.4s)[pending][pending] (varies)

WebP 到 WebP 重新编码对于调整质量很有用。节省取决于输入和输出之间的质量差距。


批量吞吐量#

并发扩展 (Pro)#

50 个 JPEG 文件,平均 3 MB 每个,在 8 核机器上:

--concurrency墙钟时间吞吐量 (文件/秒)相对串行的加速
1[benchmark pending] (~60s)[pending] (~0.8)1.0x
2[benchmark pending] (~35s)[pending] (~1.4)~1.7x
4[benchmark pending] (~20s)[pending] (~2.5)~3.0x
7 (8 核上的默认值)[benchmark pending] (~14s)[pending] (~3.6)~4.3x
8[benchmark pending] (~13s)[pending] (~3.8)~4.6x
16[benchmark pending] (~12s)[pending] (~4.0)~5.0x
32[benchmark pending] (~12s)[pending] (~4.0)~5.0x

为什么扩展是次线性的: GetWebP 在单个进程内使用异步并发,而不是操作系统线程。WASM 编解码器执行在每次解码/编码调用期间阻止事件循环。并发收益来自 I/O(文件读取/写入)与其他任务中 CPU 工作的重叠。超过 CPU 核心数,额外的并发增加调度开销,不会改善吞吐量。

推荐设置: 保留为默认值(CPU 核心数 - 1)。将 --concurrency 设置高于您的核心数会带来收益递减。

大型批量 (1,000 文件)#

混合数据集:600 JPEG + 300 PNG + 100 BMP,各种大小,8 核机器上的默认并发:

指标
总文件数1,000
墙钟时间[benchmark pending] (~4--6 min)
每个文件的平均时间[benchmark pending] (~0.3s)
总输入大小[benchmark pending] (~2 GB)
总输出大小[benchmark pending] (~800 MB)
整体节省[benchmark pending] (~60%)
峰值内存[benchmark pending] (~500 MB)

计划对比#

在 8 核机器上处理 50 个 JPEG 文件(平均 3 MB 每个):

指标免费Pro
文件限制每次运行 20 个无限制
处理模式串行 + 3 秒延迟并行 (7 个并发)
10 个文件的时间[pending] (~42s)[pending] (~3s)
50 个文件的时间N/A (上限为 20)[pending] (~14s)
有效吞吐量~0.24 文件/秒~3.6 文件/秒
加速--~15x

免费计划时间分解 (10 文件)#

文件  1: ~1.2s  (转换)
         3.0s   (强制延迟)
文件  2: ~1.2s
         3.0s
...
文件 10: ~1.2s
─────────────────────────
总计:   ~12s 转换 + ~27s 延迟 = ~39s

免费计划上的 3 秒文件间延迟是主要瓶颈,而不是转换本身。升级到 Pro 完全移除此延迟并启用并行处理。


质量 vs 文件大小 vs 速度#

所有测量都在单个 4000x3000 JPEG (~3 MB) 上:

质量 (-q)输出大小节省编码时间视觉质量
50[pending] (~300 KB)[pending] (~90%)[pending] (更快)明显的伪影
60[pending] (~400 KB)[pending] (~87%)[pending]轻微伪影
75[pending] (~600 KB)[pending] (~80%)[pending]
80 (默认)[pending] (~700 KB)[pending] (~77%)[pending]非常好
90[pending] (~1.2 MB)[pending] (~60%)[pending]优秀
95[pending] (~1.8 MB)[pending] (~40%)[pending]近无损
100[pending] (~2.5 MB)[pending] (~17%)[pending] (更慢)无损 WebP

建议: 质量 75-80 为网络传输提供最佳的文件大小和视觉保真度平衡。对于摄影作品集或打印质量资产使用 90+。


与其他工具的对比#

所有测试都在相同的 50 文件 JPEG 数据集上(平均 3 MB,4000x3000),为公平比较,单线程:

工具类型时间 (50 文件,串行)平均节省注意
GetWebP CLIWASM (jSquash 的 libwebp)[benchmark pending] (~60s)[pending] (~35%)跨平台二进制,无依赖
cwebp本机 C (libwebp)[benchmark pending] (~40s)[pending] (~35%)Google 的参考编码器
sharp本机 C (libvips + libwebp)[benchmark pending] (~25s)[pending] (~35%)需要 Node.js + 本机插件
Squoosh CLIWASM (相同编解码器)[benchmark pending] (~65s)[pending] (~35%)已弃用,类似 WASM 方法
ImageMagick本机 C[benchmark pending] (~80s)[pending] (~30%)通用,不是 WebP 优化的

分析#

GetWebP vs cwebp: 两者都使用 libwebp 编码,因此在相同质量设置下的输出质量和文件大小几乎相同。性能差距来自 WASM 开销(比本机慢 1.3-1.6 倍)。GetWebP 的优势是零依赖的跨平台分发和具有并发的批处理。

GetWebP vs sharp: Sharp 直接链接到本机 libvips,使其成为最快的选项。但是,它需要 Node.js 和平台特定的本机二进制编译。GetWebP 作为自包含的 Bun 编译的二进制文件提供。

GetWebP vs Squoosh CLI: 两者都使用相同的 jSquash/Squoosh WASM 编解码器。性能相当。Squoosh CLI 已弃用;GetWebP 得到积极维护。

GetWebP vs ImageMagick: ImageMagick 的 WebP 编码器通常不如 libwebp 优化。GetWebP 在等效的视觉质量下产生更小的文件。

何时选择 GetWebP#

  • CI/CD 管道: 单个二进制文件,无运行时依赖,JSON 输出,用于脚本的退出代码。
  • 跨平台团队: macOS (ARM + Intel)、Linux 和 Windows 上相同的二进制文件。
  • 批量作业: 内置并发、递归扫描、--skip-existing 用于增量构建。
  • 无本机编译: 避免 sharp 需要的 node-gyp / 平台特定插件问题。

并行吞吐量对比#

当考虑并发时,差距缩小。8 核机器上的 50 个 JPEG 文件:

工具模式墙钟时间注意
GetWebP CLI--concurrency 7[benchmark pending] (~14s)内置批处理
cwebp + xargsxargs -P 7[benchmark pending] (~10s)需要 shell 脚本
sharp (自定义脚本)工作者池[benchmark pending] (~7s)需要 Node.js + 自定义代码

GetWebP 的内置并发使其与需要手动并行化包装器的本机工具竞争。

何时本机工具可能更好#

  • 最大吞吐量: 如果处理数百万图像,每毫秒都很重要,本机 cwebp 或 sharp 将每个图像更快。
  • 高级转换: 如果您还需要调整大小、裁剪或超越 WebP 的格式转换,sharp 或 ImageMagick 提供更广泛的功能集。

内存使用#

GetWebP 的内存消耗由解码/编码期间的原始像素缓冲区主导:

因素内存影响
WASM 模块初始化~20--40 MB(一次性,4 个编解码器)
每个图像解码缓冲区width * height * 4 字节 (RGBA)
每个图像编码缓冲区输出 WebP 大小(更小得多)
并发图像concurrency * per-image memory

示例:8 核机器,默认并发 (7)#

处理 4000x3000 图像:

WASM init:          ~30 MB
7 个并发图像: 7 * 48 MB = ~336 MB
开销 (缓冲区、GC): ~50 MB
────────────────────────────
峰值估计:       ~416 MB

减少内存使用#

  • 较低的并发: --concurrency 2 将相同图像的峰值内存减少到 ~130 MB。
  • 较小的图像: Web 分辨率图像 (1920x1080) 每个解码缓冲区使用 ~8 MB,并发 7 时总共 ~90 MB。
  • BMP 警告: 大 BMP 文件需要额外的全帧缓冲区用于 BGR 到 RGB 通道交换。一个 36 MB 的 BMP (4000x3000) 在解码期间分配 ~96 MB。

启动时间#

阶段持续时间注意
二进制加载[benchmark pending] (~50ms)Bun 编译的单个二进制文件
WASM 初始化[benchmark pending] (~150ms)从嵌入式 blob 编译 4 个 WASM 模块
许可证检查[benchmark pending] (~5ms 本地,~500ms 网络)JWT 在本地验证;仅在首次认证或过期时进行网络调用
文件扫描[benchmark pending] (~10ms 每 1,000 文件)fs.readdir 带排序
总冷启动~200--300ms后续运行重用操作系统文件缓存

对于单文件转换,启动开销是总时间的重要部分。对于批量作业 (100+ 文件),它可以忽略不计。


优化技巧#

为了最大吞吐量#

# 在专用构建机器上使用所有核心
getwebp convert ./images -r --concurrency 8
 
# 在增量构建中跳过已转换的文件
getwebp convert ./images -r -o ./dist --skip-existing
 
# 降低质量以加快编码(质量 < 80 稍微更快)
getwebp convert ./images -q 70

为了最小内存#

# 限制并发以减少峰值内存
getwebp convert ./images --concurrency 2
 
# 一次处理一个目录而不是递归
getwebp convert ./images/thumbs -o ./out/thumbs
getwebp convert ./images/photos -o ./out/photos

为了 CI/CD 管道#

# JSON 输出 + 退出代码用于脚本化的错误处理
getwebp convert ./assets -r -o ./dist --skip-existing --json
echo "Exit code: $?"
 
# 干运行以在转换前验证
getwebp convert ./assets -r --dry-run --json

运行您自己的基准#

为您的特定硬件生成数字:

# 准备测试数据集
mkdir -p /tmp/bench-input /tmp/bench-output
# 复制或生成测试图像到 /tmp/bench-input
 
# 单文件时间
time getwebp convert /tmp/bench-input/sample.jpg -o /tmp/bench-output
 
# 使用默认并发的批量
time getwebp convert /tmp/bench-input -o /tmp/bench-output
 
# 具有 JSON 输出以获得机器可解析的结果的批量
getwebp convert /tmp/bench-input -o /tmp/bench-output --json > results.json
 
# 并发扫描
for c in 1 2 4 8 16; do
  rm -rf /tmp/bench-output/*
  echo "concurrency=$c"
  time getwebp convert /tmp/bench-input -o /tmp/bench-output --concurrency $c
done
 
# 内存监控 (macOS)
/usr/bin/time -l getwebp convert /tmp/bench-input -o /tmp/bench-output
 
# 内存监控 (Linux)
/usr/bin/time -v getwebp convert /tmp/bench-input -o /tmp/bench-output

写入安全#

GetWebP 使用原子写入来防止损坏的输出文件:

  1. 编码输出写入到临时文件 (<name>.webp.tmp)。
  2. 临时文件通过 fs.rename 重命名为最终路径 (<name>.webp)。
  3. 在 SIGINT (Ctrl+C) 上,任何正在进行中的 .tmp 文件都会自动清理。

这意味着崩溃或中断永远不会在输出目录中留下半写入的 .webp 文件。已完成的文件是安全的。在下次运行时使用 --skip-existing 从您离开的地方恢复。


已知限制#

限制影响解决方案
单线程 WASM编码/解码阻止事件循环;扩展围绕核心数达到平台期对于极端吞吐量需求,使用本机 cwebp
无 GPU 加速WASM 编解码器仅为 CPUN/A (libwebp 按设计仅为 CPU)
BMP 解码为纯 JS对于大 BMP 比 WASM 更慢如果处理许多大 BMP,首先将 BMP 转换为 PNG
免费计划延迟每个文件 3 秒,20 文件上限升级到 Pro
内存随并发扩展大图像上的高并发可能会耗尽 RAM对于大分辨率批量减少 --concurrency