你可能会发现在部份图片上传的时候常常会卡在 100% 的进度不动好一段时间。
事实上这其实是小羊在人工审核你的图片啊,要是看到色图就自己吃掉不给其他人看了。
其实 100% 是本地传输内容到服务器完成之后的服务器处理阶段,基本上分为两个部份 analysis(分析)+ optimization(优化)
分析基本上来讲原理很简单,就是读取 EXIF 的数据,图片的长宽之类的,照理来讲不会花太多时间;而优化是将图片生成一个更小的预览版本,会相对花比较多的时间,但是由于是异步进行的所以到也没什么问题。
身为打 OI 出生的臭写代码,一般遇事不决不会打 debugger,而是 std::cout << value << std:endl; 服务器你也打不了 debugger,启一套 perf 工具看火焰图太 overkill 了,所以直接在代码里面计时然后打印出来看看啊。
不看不知道,一看吓一跳!对于一个区区 4.9MB 的 JPEG 格式图片来说:
| 阶段/事件 | 最大耗时(ms) | 对应文件/消息 |
|---|---|---|
| analysisDuration | 37,905.43 | 文件 01KWBK...K |
| totalDuration(整体上传耗时) | 42,460.23 | 同上(包含分析+上传+存储等) |
| stageDuration(暂存到磁盘) | 3,929.12 | 文件 01KWBK...K 的 staging 阶段 |
| uploadDuration(实际上传耗时) | 1,666.72 | 文件 01KWBK...Q |
| eventDuration(事件通知耗时) | 1.87 | 文件 01KWBK...Q 的 complete 事件 |
想着 @a123lsw 看着 100% 的上传进度看了 40s 我就想笑
最开始以为是 EXIF 数据造成的分析延长,但是 EXIF 看到我要动他吓死了,直接提起上诉啊,那咋办呢?写个 benchmark 跑一跑吧!
=== extractExif ===
WITH EXIF: open: 77µs decode: 89µs walk (53 tags): 0.7µs total: 167µs
WITHOUT EXIF: open: 26µs decode: 1.1ms (EOF error)
=== AnalyzeImage substages ===
WITH EXIF:
vips.NewImageFromFile 165ms
AutoRotate+RemoveMetadata 0.7ms
blurhash (PNG export+decode) 786ms
extractExif (full) 0.16ms
WITHOUT EXIF:
vips.NewImageFromFile 0.3ms ← 550x faster
AutoRotate+RemoveMetadata 0.2ms
blurhash (PNG export+decode) 762ms
extractExif (full) 1.0ms (EOF)
哎呀 怪不得要上诉了 EXIF 酱只能坚持 165ms 呐,真是杂鱼呢❤
事已至此,只能跑跑整个的 benchmark 了。
=== WITH EXIF ===
vips.NewImageFromFile 531µs (0.05%)
AutoRotate 56µs
RemoveMetadata 45µs
ExportPng 773ms (67.7%) ← bottleneck
png.Decode 208ms (18.2%)
blurhash.Encode 160ms (14.0%)
os.Open 60µs
exif.Decode 73µs
exif.Walk 0.8µs
TOTAL 1.141s
=== WITHOUT EXIF ===
vips.NewImageFromFile 256µs
ExportPng 766ms (67.5%) ← same bottleneck
png.Decode 207ms
blurhash.Encode 160ms
TOTAL 1.134s
在还原犯罪现场的时候发现一件事,莫名其妙调用了 ExportPng 一次,虽然这不是用来优化的,事情一定不简单 o.0
后来发现 ExportPng 是用来生成 blurhash 的,也就是加载的时候你们看到的模糊占位符,这不对啊,这相当于重新编码了一遍图片,我的压缩到 webp 的优化也大抵就花这么久了。
依照我们数学老师最后教的焚绝,化小不化大,既然 blurhash 本来也没什么有用的信息,直接先把图片压缩 64x 到妈都不认识再看看吧~
CURRENT: 1.133s (ExportPng 763ms + png.Decode 210ms + blurhash 160ms)
PROPOSED: 35ms (ExportPng 34ms + png.Decode 0.09ms + blurhash 0.05ms)
一顿操作猛如虎,事实也确实如此,比香港记者跑得还快了现在。但是代价是什么呢…… 嘛不重要了 :maki1+08:
| Stage | Current | Fixed | Speedup |
|---|---|---|---|
| NewImageFromFile | 311µs | 596µs | |
| AutoRotate | 52µs | 65µs | |
| RemoveMetadata | 62µs | 51µs | |
| Copy + Resize(64px) | — | 306µs | — |
| ExportPng | 778.0ms | 37.3ms | 21x |
| png.Decode | 213.4ms | 101µs | 2106x |
| blurhash.Encode(4,3) | 162.6ms | 47µs | 3460x |
| TOTAL | 1.15s | 38.5ms | 30x |
tested on M3 Max, with a 4032x3024 2.2MB JPEG file
