Ctrl K
ComunidadGuías…

docs/processing-benchmark.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# 阶段 3 图片处理并发基线

测量日期:2026-07-29  
默认处理链路:5000 × 3200、16 MP 高压缩 PNG 固定最坏样本,16 次 PNG → WebP 上传  
测量方法:服务安全门与客户端并发同时设为 1、2、4、8;记录成功吞吐、p95 和容器 cgroup 峰值内存

## 本机结果

### 四格式真实解码验证

本机 Apple M4、Go 1.26.5 对 `InspectFile` 的三次 benchmark 中位值如下。这里的内存是每次操作的 Go heap 分配量;端到端处理的容器峰值见后续并发表。

| 格式与固定样本 | 耗时(ns/op) | 吞吐(MB/s) | Go heap(B/op) | allocs/op |
|---|---:|---:|---:|---:|
| JPEG,1600 × 1200 | 7,639,101 | 29.13 | 2,924,048 | 17 |
| PNG,800 × 600 | 2,871,904 | 3.64 | 1,992,412 | 32 |
| WebP,1 × 1 golden | 15,143 | 2.77 | 20,792 | 27 |
| GIF,320 × 240、2 帧 | 477,510 | 1.79 | 223,474 | 34 |

四种格式都执行真实解码;GIF 会解码全部帧并把总帧像素计入上限。该基准可用 `go test ./internal/platform/processor -run '^$' -bench BenchmarkInspectFormats -benchmem -count 3` 复现。

### 16 MP 主动 PNG → WebP 转换

#### macOS arm64 主机,linux/arm64 原生容器

| 并发 | 吞吐(次/秒) | p95(ms) | 峰值内存(bytes) |
|---:|---:|---:|---:|
| 1 | 0.621 | 1,641.7 | 390,234,112 |
| 2 | 1.166 | 1,763.5 | 635,396,096 |
| 4 | 2.197 | 1,855.3 | 1,056,608,256 |
| 8 | 3.305 | 2,457.0 | 1,877,291,008 |

#### macOS arm64 主机,linux/amd64 OrbStack 模拟容器

| 并发 | 吞吐(次/秒) | p95(ms) | 峰值内存(bytes) |
|---:|---:|---:|---:|
| 1 | 0.466 | 2,168.3 | 493,858,816 |
| 2 | 0.883 | 2,342.6 | 671,985,664 |
| 4 | 1.671 | 2,417.1 | 1,065,037,824 |
| 8 | 2.522 | 3,279.0 | 1,959,501,824 |

### 16 MP GitHub 原生 runner

证据:[Verify run 30461966181](https://github.com/Willxup/imagesilo/actions/runs/30461966181),对应提交 `27c8a2c97a8889f62994d55fd4f266176a158377`。原生镜像构建、完整 smoke 和 1/2/4/8 主动转换 benchmark 均通过。

#### 原生 linux/arm64

| 并发 | 吞吐(次/秒) | p95(ms) | 峰值内存(bytes) |
|---:|---:|---:|---:|
| 1 | 0.348 | 2,878.0 | 396,300,288 |
| 2 | 0.694 | 2,908.5 | 626,872,320 |
| 4 | 1.301 | 3,094.0 | 1,059,647,488 |
| 8 | 1.298 | 6,299.1 | 1,839,075,328 |

#### 原生 linux/amd64

| 并发 | 吞吐(次/秒) | p95(ms) | 峰值内存(bytes) |
|---:|---:|---:|---:|
| 1 | 0.335 | 3,008.8 | 422,658,048 |
| 2 | 0.654 | 3,067.6 | 623,267,840 |
| 4 | 0.897 | 4,493.0 | 1,091,997,696 |
| 8 | 0.890 | 9,404.9 | 1,823,301,632 |

### 阶段 7 并发 1 内存分类

证据:[Verify run 30479364047](https://github.com/Willxup/imagesilo/actions/runs/30479364047),对应提交 `b866eebcd411a38fba2365786a1fa05ed4cc443d`。两个原生架构仍各执行 16 次、并发 1 的 16 MP PNG → WebP;阶段 7 额外记录正式输出总量、结束匿名内存和结束文件页缓存。

| 架构 | 吞吐(次/秒) | p95(ms) | 16 个输出(bytes) | 结束匿名内存(bytes) | 结束文件页缓存(bytes) | cgroup 峰值(bytes) |
|---|---:|---:|---:|---:|---:|---:|
| amd64 | 0.342 | 2,951.1 | 89,923,072 | 86,945,792 | 91,430,912 | 396,574,720 |
| arm64 | 0.348 | 2,881.4 | 89,923,072 | 101,023,744 | 91,430,912 | 396,828,672 |

结束文件页缓存与 16 个正式 WebP 的总量几乎相同,属于 Linux 可回收文件缓存,不是进程匿名堆。持续工作集应看 87–101 MB 的匿名内存;cgroup 峰值仍包含单次编码瞬时内存和此前输出文件的页缓存,不能直接等同为“单请求进程内存”。

### 16 MP 默认原样保存

字节变更开关全部关闭时,并发 1 的 arm64 p95 为 198.2 ms、峰值 86,446,080 bytes;amd64 模拟 p95 为 269.2 ms、峰值 153,608,192 bytes。16 个顺序上传的吞吐分别为 5.08 次/秒和 3.72 次/秒。

并发 2 的对照值为:arm64 p95 242.7 ms、峰值 127,074,304 bytes;amd64 模拟 p95 363.7 ms、峰值 176,701,440 bytes。默认路径只做严格解码校验、缩略图和原子落盘,不执行 WebP 编码。

### 外部共享主机受控原生 amd64

共享服务器为 4 核 x86_64、9.5 GiB 内存。测试容器硬限制为 1 CPU、1 GiB,服务并发 1,每种模式只执行 4 次 16 MP 上传;测试结束后临时容器、镜像和 `/var/tmp` 数据均已删除。

| 模式 | 吞吐(次/秒) | p95(ms) | 结束内存(bytes) | 峰值内存(bytes) |
|---|---:|---:|---:|---:|
| 默认原样保存 | 1.063 | 934.0 | 38,424,576 | 70,332,416 |
| PNG → WebP | 0.146 | 6,882.7 | 97,079,296 | 304,029,696 |

这里的延迟受到 1 CPU 硬限制,不能与未限速 runner 横向比较;内存结果直接证明默认并发 1 在真实 amd64 共享服务器上保持轻量,主动转换也能稳定运行在 1 GiB 容器边界内。

阶段 7 又在同一外部 amd64 共享主机用提交 `b866eeb` 构建发布候选并连续运行 600 秒。构建器硬限制为 1 CPU、1 GiB,完成后立即删除;运行容器硬限制为 0.5 CPU、256 MiB、并发 1,共完成 118 轮串行 health/readiness/标准 URL/别名哈希校验。运行 cgroup 当前内存 `14,135,296` bytes、峰值 `35,938,304` bytes、结束匿名内存 `8,814,592` bytes;goroutine `10 → 8`、文件描述符 `10 → 10`,SIGTERM 正常退出。

### 40 MP 边界探针(已不再作为默认)

早期默认上限 40 MP 与轻量目标不符;下列结果仅保留为边界问题的发现证据。默认像素上限现为 16 MP,最终默认决策使用后续 16 MP 数据。

#### macOS arm64 主机,linux/arm64 原生容器

| 并发 | 吞吐(次/秒) | p95(ms) | 峰值内存(bytes) |
|---:|---:|---:|---:|
| 1 | 0.109 | 9,299.5 | 1,022,279,680 |
| 2 | 0.207 | 9,989.3 | 1,478,410,240 |
| 4 | 0.400 | 10,205.3 | 2,843,156,480 |
| 8 | 0.565 | 14,179.5 | 5,236,785,152 |

#### macOS arm64 主机,linux/amd64 OrbStack 模拟容器

| 并发 | 吞吐(次/秒) | p95(ms) | 峰值内存(bytes) |
|---:|---:|---:|---:|
| 1 | 0.080 | 12,934.5 | 1,012,928,512 |
| 2 | 0.155 | 13,468.9 | 1,676,865,536 |
| 4 | 0.295 | 13,505.9 | 2,806,001,664 |
| 8 | 0.421 | 19,424.1 | 5,408,296,960 |

amd64 数字当前只是本机模拟反馈,不作为原生阶段门证据。以上多档并发结果作为阶段 3 的一次性选型证据保留;当前 GitHub Actions 只在官方原生 amd64 与 arm64 runner 上复测默认并发 1,并把原始 JSON 写入任务摘要,不再持续消耗资源重复测试 2/4/8。

## 40 MP GitHub 原生 runner 边界探针

证据:[Verify run 30458478114](https://github.com/Willxup/imagesilo/actions/runs/30458478114),对应提交 `04f3184429485e9ec98fa87b138ba637e254158d`。`quality`、原生 arm64/amd64 镜像构建、完整 smoke 和全部 benchmark 均通过。

### 原生 linux/arm64

| 并发 | 吞吐(次/秒) | p95(ms) | 峰值内存(bytes) |
|---:|---:|---:|---:|
| 1 | 0.054 | 18,548.0 | 1,196,453,888 |
| 2 | 0.109 | 18,503.9 | 1,614,176,256 |
| 4 | 0.212 | 18,920.9 | 2,974,887,936 |
| 8 | 0.212 | 37,962.6 | 4,844,515,328 |

### 原生 linux/amd64

| 并发 | 吞吐(次/秒) | p95(ms) | 峰值内存(bytes) |
|---:|---:|---:|---:|
| 1 | 0.060 | 16,863.6 | 1,265,750,016 |
| 2 | 0.118 | 17,023.4 | 1,922,170,880 |
| 4 | 0.156 | 25,684.4 | 2,837,225,472 |
| 8 | 0.156 | 51,666.9 | 4,824,039,424 |

## 决策

默认像素上限选择 16 MP,覆盖常见 10–15 MP 大图并留出余量。更高像素上限必须作为单独的部署选择重新 benchmark,不再是新安装或升级后的默认安全边界。

默认 `processingConcurrency` 选择 1。默认原样保存的本机峰值约 86–154 MB,受限原生 amd64 服务器峰值约 70 MB;主动 WebP 转换的阶段 7 原生 runner cgroup 峰值约 397 MB,其中结束时约 91 MB 是与正式输出总量吻合的可回收文件页缓存,结束匿名内存为 87–101 MB。并发 2 的一次性选型数据会把峰值叠加到约 623–627 MB,因此不作为默认值。批量选择仍由浏览器本地队列完成。

最坏样本同时暴露并修正了早期 6 MP 探针没有发现的内存问题:生产真实解码校验不再把整图复制到 Go heap,而是让 libvips 流式求值;无跨请求复用价值的 libvips 操作缓存设为 0;每次图片处理结束后归还 glibc 保留的空闲原生页。修正前并发 1 连续 16 次的结束内存约 1.9 GB、峰值约 2.2 GB;修正后两次上传结束约 108 MB,16 次上传结束值主要由 16 个正式 WebP 文件的可回收页缓存构成,不再存在匿名原生堆持续累积。

共享服务器专项处理 benchmark 默认限制为 0.5 CPU、1 GiB、并发 1 和 4 次请求;发布候选连续运行验收默认限制为 0.5 CPU、256 MiB、并发 1。只有在独占测试环境中才应显式提高这些值。

安全门是无队列门:占满时上传立即返回 `503 Service Unavailable` 和 `Retry-After: 1`。当前 GitHub benchmark 在默认并发 1 下要求 16 次请求全部成功且容器不退出;满载拒绝语义另由 HTTP 集成测试与 race 测试覆盖。脚本仍允许开发者通过 `CONCURRENCIES` 显式执行专项多并发测量,但 CI 不会自动运行。

## 复现

```bash
IMAGE=imagesilo:phase3-probe \
PLATFORM=linux/arm64 \
PORT=18086 \
BENCH_SUFFIX=arm64-local \
bash scripts/processing-benchmark.sh
```