图片压缩实测:十个样本,在浏览器里跑出来的数字

十个样本,每个测五次,同一台机器、同一个浏览器。数据来自网站实际发布的 worker 路径,也包括那些不太好看的结果。

由 tinyimg 发布。压测代码、样本生成脚本和结果文件都在仓库里,方法和数字都可以复核。

测了什么,怎么测的?

十个用例来自七个文件,每个测五次:基准测试测量压缩耗时和输出体积。12 MP 照片分别编码为 JPEG、WebP 和 AVIF,2 MP 截图分别编码为 PNG 和 WebP。

测试代码走的是线上实际路径。它直接 import src/lib/pool.js 和 src/lib/worker.js —— 也就是应用处理 JPEG、PNG、WebP、AVIF、JXL 时用的同一个 worker 池和同一个 worker。MotionPhoto 则 import src/lib/motion-photo/pipeline.js,这正是应用处理该格式时走的路径;MotionPhoto 在生产环境同样不经过 worker 池。没有只给基准测试用的压缩器,也没有第二套实现可以悄悄跑偏。

每个用例先跑一次预热,再测五次。用例串行执行,避免争抢四个 worker。表中时间是五次采样的中位数。p95 用最近秩计算,在五个样本下就是最慢的一次 —— 不做插值,也不假装五个样本能刻画长尾。

浏览器是 headless Chrome 155,每次运行使用全新的临时 profile,禁用扩展和后台网络。页面由 localhost 提供,计时从 worker 池和 WASM 模块就绪之后开始。运行时刻意不开启跨源隔离:那会让 wasm-feature-detect 报告支持线程,从而把 oxipng 切到多线程构建,而生产环境用的不是那条路径。

机器是 Apple M4,10 核,16 GB 内存,macOS arm64。浏览器报告 10 个硬件线程;应用的 worker 池上限是四个。

测试了哪些文件?

七个文件,从 0.5 MP 的 JPEG 到 24 MP 的照片,另有一个 12 MP 的 MotionPhoto。

文件像素输入代表什么
photo-12mp.jpg4000×30004.92 MB细节丰富的照片
shot-2mp.png1920×10801.36 MB带锐利边缘和类文字色条的界面截图
flat-1mp.png1000×1000118.1 KB平坦、低细节的图形
alpha-4mp.png2000×20008.26 MB带真实 Alpha 通道的 RGBA 图像
large-24mp.jpg6000×40009.66 MB24 MP 照片
tiny-0.5mp.jpg800×600219.6 KB小图;单次调用开销在这里更明显
motion-12mp.jpg4000×3000 静帧 + 6 秒 1280×720 视频9.98 MB用仓库自带写入器合成的 MotionPhoto

所有样本都由 ffmpeg 从内置素材生成。每个文件的 SHA-256 记录在 bench/fixtures/manifest.json,浏览器加载前会校验;样本被改动会直接失败,而不是悄悄换掉基线。

结果如何?

十个用例的压缩比从 1.5% 到 69.3%,中位数从 42.0 ms 到 1,983.2 ms。比率是输出 ÷ 输入,越低越小。中位数和 p95 是整个用例的墙钟时间,包含 worker 往返。ms/MP 是中位数除以百万像素数。

表格和两张图读取同一个文件 bench/published.json(运行 id、日期、环境);如有出入,以文件为准。

各用例压缩比水平条形图。压缩比为输出体积除以输入体积,越低越好。最低为 shot-2mp-webp 的 1.5%,最高为 motion-12mp 的 69.3%。各用例压缩比0255075photo-12mp-jpgphoto-12mp-webpphoto-12mp-avifshot-2mp-pngshot-2mp-webpflat-1mp-pngalpha-4mp-aviflarge-24mp-jpgtiny-0.5mp-webpmotion-12mp8.8%6.5%2.4%43.2%1.5%51.7%4.4%8.6%9.3%69.3%输出 ÷ 输入(%)越低越好
最小:shot-2mp-webp,为原始体积的 1.5%。最大:motion-12mp,69.3%。
每百万像素编码耗时水平条形图,显示每次编码的中位耗时(按百万像素计),用例顺序与压缩比图相同。最低为 large-24mp-jpg 的 51.2 毫秒/百万像素,最高为 flat-1mp-png 的 516.2 毫秒/百万像素。每百万像素编码耗时0200400600photo-12mp-jpgphoto-12mp-webpphoto-12mp-avifshot-2mp-pngshot-2mp-webpflat-1mp-pngalpha-4mp-aviflarge-24mp-jpgtiny-0.5mp-webpmotion-12mp56.483.1165.3296.273.1516.2387.951.287.5111.2毫秒/百万像素(5 次中位数)用例顺序与压缩比图相同
每百万像素最快:large-24mp-jpg,51.2 毫秒/百万像素。最慢:flat-1mp-png,516.2 毫秒/百万像素。
用例格式MP输入输出比率中位数p95ms/MP
photo-12mp-jpgJPEG → JPEG124.92 MB444.4 KB8.8%677.0733.456.4
photo-12mp-webpJPEG → WebP124.92 MB325.2 KB6.5%997.51021.783.1
photo-12mp-avifJPEG → AVIF124.92 MB121.8 KB2.4%1983.22026.8165.3
shot-2mp-pngPNG → PNG2.071.36 MB600.6 KB43.2%613.1618.1296.2
shot-2mp-webpPNG → WebP2.071.36 MB21.0 KB1.5%151.3156.173.1
flat-1mp-pngPNG → PNG1118.1 KB61.0 KB51.7%516.2562.9516.2
alpha-4mp-avifPNG → AVIF48.26 MB370.1 KB4.4%1551.71626.3387.9
large-24mp-jpgJPEG → JPEG249.66 MB846.1 KB8.6%1228.11231.051.2
tiny-0.5mp-webpJPEG → WebP0.48219.6 KB20.4 KB9.3%42.043.487.5
motion-12mpMotionPhoto129.98 MB6.92 MB69.3%1334.01377.3111.2

这些数字说明什么?

在 12 MP 照片上,AVIF 比 JPEG 小 3.65 倍、慢 2.93 倍;WebP 在两者中间,PNG 则是无损的异类,达到 296.2 ms/MP。

AVIF 比 JPEG 小多少?

约为 JPEG 的三分之一:2.4% 对 8.8%,即 121.8 KB 对 444.4 KB。WebP 是 325.2 KB。AVIF 比 JPEG 小 3.65 倍,比 WebP 小 2.67 倍。它也是最慢的:中位数 1,983.2 ms,是 JPEG 的 2.93 倍,是 WebP 的 1.99 倍。按百万像素算,AVIF 是 165.3 ms,JPEG 是 56.4 ms,WebP 是 83.1 ms。

一句话概括这笔交易:用略多于 JPEG 四分之一的体积,换三倍的编码时间。几百张的批量处理里,这个时间很实在;只压一张首图时,可能无所谓。

WebP 比 JPEG 小多少、慢多少?

小 26.8%,慢 1.47 倍:同一张 12 MP 源图上 325.2 KB 对 444.4 KB,997.5 ms 对 677.0 ms。和 AVIF 比,它的体积大 2.67 倍,速度快 1.99 倍。它两头都不占第一,两个方向都停在中间。

JPEG 有多快?

12 MP 照片 677.0 ms(56.4 ms/MP),24 MP 照片 1,228.1 ms(51.2 ms/MP),是这里三种编码器中最快的。每百万像素成本基本持平,这正是快速块编码器该有的样子。它的输出比两种现代格式都大,但比速度它是基准,而在这台机器上没有谁赢过它。

PNG 为什么每百万像素最慢?

2.07 MP 截图上是 296.2 ms/MP,是 WebP 的 73.1 ms/MP 的 4.05 倍,因为它必须为每个像素搜索滤波器和 DEFLATE 策略。这张截图用 PNG 编码耗时 613.1 ms,编码成 WebP 只用 151.3 ms。平坦的 1 MP PNG 更差:516.2 ms,因为优化器的固定成本只摊在一个百万像素上。

PNG 是无损的。每个像素都要保留,oxipng 要花时间搜索滤波器和 DEFLATE 策略,寻找更小的码流;搜索是它的价值,也是它的成本。有损编码器达到质量目标就可以停;无损优化器必须确认没有东西可以再删,而组合数量随图像增大。

体积差距同样直白:同一张截图 PNG 是 600.6 KB,WebP 是 21.0 KB。这两个输出并不等价 —— 一个无损,一个有损 —— 但它们解释了为什么 PNG 适合你还要继续编辑的图表,不适合你要发出去的照片。

oxipng level 越高,PNG 就越小吗?

不一定:level 3 在三个截图样本上小 9.6–13.6%,但在 8.29 MP 的照片型 PNG 上只少四个字节,耗时却是 8.2 倍。PNG level 用 bench/png-levels.mjs 单独测过,样本相同,每个 level 一次预热加五次测量。在三个截图样本上(2.07、8.29、15.73 MP),level 3 的耗时是 level 1 的 7.8–8.5 倍,文件小 9.6–13.6%。在 8.29 MP 的照片型 PNG 上,它只比 level 1 少四个字节 —— 15,474,918 对 15,474,922 —— 耗时却是 8.2 倍。在 RGBA 图像上省了 2.0%,耗时 7.1 倍。图像本身已经是照片型时,多出来的搜索几乎买不到东西。

所以线上 worker 现在默认用 level 2,超过 4 MP 降到 level 1。在 16 MP 时,level 2 比 level 1 大 1.0%,速度还慢约一倍,所以 level 更高并不自动等于文件更小。这个改动的代价在平坦、低细节的 PNG 上看得见:1 MP 平坦样本在 level 2 下是 62,501 B,level 3 是 48,138 B —— 大 29.8% —— 但编码只要 437.9 ms,而不是 2,028.3 ms,快 4.6 倍。截图付出的代价小一些:2.07 MP 时 level 2 比 level 3 大 7.2%,快 4.1 倍。网站选择了更快的默认值,接受这部分体积。

MotionPhoto 会压缩视频吗?

不会:只重新编码静帧,视频按字节原样复制,所以合成的 12 MP 文件小了 30.7%,而其中一半根本没动。一个 MotionPhoto 就是一个文件:一张 JPEG 静帧,附上一段视频,有些机型还带 gain map。线上路径会解码并重新编码静帧,然后围绕它重建容器。视频不解码、不重编码,也不作为输出目标提供。gain map 存在时同样原样复制,并重新指向它在 MPF 中的绝对偏移,HDR 显示才不会坏。

这意味着整文件比率主要取决于视频占多大比例。在合成的 12 MP 样本里,静帧占输入的 5,156,418 B / 10,463,084 B(49.3%),视频占 5,306,666 B(50.7%),没有 gain map。静帧处理后是 1,944,569 B,小了 62.3%;整个文件从 9.98 MB 变成 6.92 MB,小了 30.7%。静帧自身省得很多;整文件省得有限,因为有一半根本没动。

真实样本验证用的是一台 OnePlus 13 的 MotionPhoto:静帧 7,140,978 B(44.7%),视频 7,968,544 B(49.9%),gain map 848,042 B(5.3%),合计 15,957,564 B。走完只压静帧的路径后:静帧 1,862,140 B(小 73.9%),视频和 gain map 字节完全一致,合计 10,712,466 B —— 15.96 MB 变成 10.71 MB,小 32.9%。重建后的文件在手机上被识别为 MotionPhoto,并能正常播放。gain map 这条路径是在真实硬件上验证的,不是从合成样本推断的。

有哪些局限?

六点说明,从合成样本和单台机器开始。

如何复现这些数字?

五条命令,从 npm ci 到 npm run bench。

npm ci
npm run build
npm run bench:fixtures
npm run bench:build
npm run bench

运行会把 JSON 写进 bench/results/,并把上面的表格打印到 stdout。PNG level 对比用 npm run bench:png-levels。浏览器加载前会用 bench/fixtures/manifest.json 校验样本;文件被改动会直接失败,而不是悄悄生成新基线。

常见问题

哪种格式压缩得最小?

AVIF:在 12 MP 照片上是 121.8 KB,占输入的 2.4%,而 WebP 是 6.5%,JPEG 是 8.8%。

哪种格式编码最快?

JPEG:12 MP 照片用 677.0 ms,WebP 是 997.5 ms,AVIF 是 1,983.2 ms。

PNG 为什么每百万像素更慢?

因为它是无损的:oxipng 要为每个像素搜索滤波器和 DEFLATE 策略。2.07 MP 截图用 PNG 是 296.2 ms/MP,用 WebP 是 73.1 ms/MP,少 4.05 倍。

MotionPhoto 会压缩视频吗?

不会。视频按字节原样复制,只重新编码静帧;合成的 12 MP 样本里静帧小了 62.3%,整个文件小了 30.7%,从 9.98 MB 变成 6.92 MB。

level 2 和 level 3 对 PNG 输出体积有什么影响?

level 3 更小但慢得多。1 MP 平坦 PNG 在 level 2 下是 62,501 B,level 3 是 48,138 B,大 29.8%,但编码只要 437.9 ms,而不是 2,028.3 ms。

每个用例测几次?

一次预热加五次测量;表格报告这五次的中位数,p95 是最慢的一次。

关于本文

由 tinyimg 发布。压测代码、样本生成器和结果文件都在仓库里,这次运行的原始 JSON 写在 bench/results/。署名是项目,不是某个人:这里的依据不是头衔,而是方法写下来了,数字可以复现。

← 返回 tinyimg

en · zh-CN · ja · ko · es · fr · ru · ar