Files

43 KiB
Raw Permalink Blame History

独立二进制性能基准

这里测常驻内存、异步调度、HTTP/1.1、HTTP/2 和 TCP/WebSocket/SSE 长连接;不改运行时。构建单独执行,运行阶段只启动已编译产物。全部程序绑定自己创建的 loopback 随机端口,不接触已有服务,不进入默认 cargo test。

构建与短时验证

在仓库根目录运行,使用已经安装的 Rust 工具链与锁定的离线依赖:

cargo build --offline -p dever-cli --bin dever
cargo build --offline --locked --release --target-dir target/native-runtime -p dever-tests --example network_bench --example live_bench --example async_bench
python3 test/performance/run.py build --output target/performance/build-current --workers 1 --connections 512
python3 -m unittest discover -s test/performance -p test_runner.py -v
python3 test/performance/test_peer_settings.py -v
python3 test/performance/test_network_bench.py -v
python3 test/performance/test_live_bench.py -v
python3 test/performance/run.py run --artifacts target/performance/build-current --output target/performance/smoke-current --suite all --duration 0.5 --warmup 0.2 --rates 100 --paths /plain --repeats 1

只复验当前 Dever/Markdown 双源码 CMS 和 base/SQLite/PostgreSQL/both 四个 profile 时,可使用 python3 test/performance/run.py build --selection cms-profiles --output target/performance/cms-build-current。 该选择不构建无关的 HTTP、长连接、ORM 场景和 peer;生成的产物仍可用 run --suite cms 分别测量 cms(Dever)和 cms-md(Markdown)的真实登录、文章创建/替换、发布和前台查询;旧 READY|3 计数 fixture 已删除。仅做四 profile 构建可选 --selection profiles。PostgreSQL profile 使用不连接数据库的编译专用声明,不需要真实 URL;真实 PostgreSQL ORM/压力测试仍必须显式配置根 config/setting.json 的 database.postgres_test。

仅复验 runtime/HTTP/TLS/HTTP2/TCP/WebSocket/SSE 时使用 --selection protocols,不构建 ORM/CMS 或重复四 profile;三种 Rust peer 可用 --network-bench、--live-bench、--async-bench 指定明确准备的路径。

输出目录必须是新路径,保留先前报告。当前 Dever 产物统一经公开 dever build 使用 LLVM O2、LLD 和明确准备的 runtime archive;构建日志及二进制 SHA-256、大小、耗时保存在 manifest.json。runtime/http/live 每组构建一个包含该组 CMD 的应用,各测量通过不同命令启动,大小不能解读为仅一个函数的程序。profile-report.json 仍使用同一份最小 CMD 源码生成 base、SQLite、PostgreSQL 和 both 产物,记录 bridge 的锁定依赖。作者必须准备匹配源码的四个 archive;编译器自身的 debug/release 不决定 archive 优化级别。Rust 对照程序由上面的 Cargo release 命令另行准备并记录二进制摘要。Dever 使用生产默认调度,--workers 和 task_capacity 只配置 Rust 对照,不声称两者调度配置相同;可用专属 CPU affinity 统一核数。运行器要求 application_configuration=llvm-cmd-v1,拒绝旧 Rust 后端产物冒充当前 LLVM 测量。测量不调用 Cargo。

双源码 CMS 也有独立入口,不构建网络 peer 和无关场景:

python3 -B test/performance/cms.py build --output target/performance/cms-publish-build
python3 -B test/performance/cms.py run --manifest target/performance/cms-publish-build/manifest.json --output target/performance/cms-publish-128 --cgroup-parent /sys/fs/cgroup --memory-mib 128
python3 -B test/performance/cms.py run --manifest target/performance/cms-publish-build/manifest.json --output target/performance/cms-publish-64 --cgroup-parent /sys/fs/cgroup --memory-mib 64
python3 -B -m unittest discover -s test/performance -p test_cms.py -v

运行使用仅含二进制和私有 config/setting.json 的副本,不保留 module。先在自有 SQLite 平台库 bootstrap、迁移租户库并给 admin/front 分别建立 Owner,再启动随机 loopback 端口。服务启动与登录/发布/查询在专属 cgroup 内,bootstrap 和部署命令不在该预算内。保留 Cookie/Origin、真实身份和租户验证,不伪造登录身份。逐篇创建、替换、发布并核对全部前台结果;重复发布必须返回 409,两套源码结果必须一致。默认 16 篇顺序业务基线报告启动时间、每种请求样本数/中位/最大延迟、RSS/PSS 与 cgroup peak/OOM,不称为并发饱和吞吐或 p99。测试手动传送服务返回的 Secure Cookie,只验证 owned HTTP 协议,不代表浏览器 HTTPS 验收。日志阈值固定 error,出现服务错误、超时、OOM 或清理失败均失败。

追加 --concurrency 8 --read-seconds 10 会在发布后运行八个独立连接的已登录前台列表查询,每次都核对完整业务结果。报告另列 concurrent_reads 的实际成功 QPS、p50/p95/p99、测量期资源趋势和断连 FD 回落;这是闭环读负载,不能解释为写入吞吐或预设请求到达率。并发数最多64、时长最多3600秒,同一服务进程内支持30分钟持续读取;客户端使用固定2048桶的对数延迟直方图,分位数报告包含请求及结果核对的桶上界,避免随请求数保存无限样本。首次失败停止其他客户端,所有连接按原请求期限关闭;不记录 Cookie 或密码。完整Linux门及64/128MiB长测入口见 test/ci/README.md。

真实 PostgreSQL 的 Owner HTTP/双物理租户验收另由共享 Rust fixture 管理数据库生命周期:

cargo test --offline --locked -p dever-tests --features api,postgres --test postgres_api postgres_cms_http_isolates_two_tenant_databases -- --ignored --exact --nocapture

根 database.postgres_test 必须指向专属测试实例,URL 数据库名包含 {case},账号允许创建/删除测试库;默认不运行、不探测外部服务。fixture 使用随机控制库及租户前缀,只清理本轮拥有的库,失败也清理,清理失败不能记录 passed。cms.py postgres-case 始终先复制传入项目到自有临时目录,端口/密钥/配置/产物仅写副本,输出目录保留日志和不含凭据的结果。Rust 目标额外注入仅在 test/ 的普通成员 CMD,并显式启用 --rbac-fixture:覆盖 Owner、两个租户各两篇发布、相同 slug 独立数据、跨站 Cookie/跨租户登录拒绝,以及非 Owner 多角色、跨站同名角色和原会话撤销立即失效。单独调用不启用该测试 fixture 的工具仍仅证明 Owner 合同。权限键分别读取实际站点目录,GET/DELETE 参数统一走 query,生产 CMS 和 Owner 保留角色规则不改。

runtime/HTTP/live 夹具已使用当前 App→Port→Adapter 结构,CMD 根有真实 Bool 输出;live 的 typed Adapter setting 在每个产物的私有配置中独立绑定。ORM seed 是测量前的 CMD 部署步骤,count/cancel 是普通 API,取消测试使用单连接池并核对计数和连接归还,不能把 handler 超时冒充客户端取消。

2026-10-02 LLVM 基线使用 protocols-llvm-10-02、cms-llvm-10-02 及 profiles-llvm-10-02 的准确产物摘要,私有作者编译器来源记录在 target/closure-current-compiler/provenance.json。四个最小 CMD profile 大小分别为 base 2,083,880、SQLite 4,139,976、PostgreSQL 2,265,224、both 4,238,568 字节。Dever/Markdown CMS 分别 8,323,880 / 8,326,120 字节。

当前测量 条件 结果与报告目录(均位于 target/performance)
双 CMS 发布 64/128 MiB、每组顺序16篇 零错误/OOM,启动44.6–51.9ms,RSS峰值28.20–28.33MiB;cms-llvm-{64,128}-10-02
双 CMS 登录后列表读取 64/128 MiB、8并发、10秒、不绑核 1210.6–1322.2 QPS,p99 11.48–13.59ms,RSS峰值30.81–31.04MiB,零错误/OOM;cms-read-llvm-{64,128}-10-02
HTTP/2 明文及 TLS 稳态 64/128 MiB、单核、4连接×16流、目标10k、60秒 成功/计划99.718–99.794%,9971.6–9979.2 QPS,p99 2.912–3.424ms,RSS峰值4.88–5.72MiB,零错误/OOM/断开后FD差;http2-steady-llvm-{64,128}-10-02
HTTP/2 连接恢复 64 MiB、单核、两种传输各100轮 合计19,887个完整成功请求,200次断开后FD差均0,零错误/OOM;http2-recovery-llvm-64-10-02-v2
TCP/WS/SSE 连接恢复 64/128 MiB、单核、32/128连接、各10轮 12个case、120次断开后FD差均0,完整消息校验通过,零错误/OOM;live-llvm-{64,128}-10-02

这些是本机 loopback 与热文件缓存的有限测量。CMS使用闭环客户端,HTTP/2使用固定速率且保留少量未派发计数,两者不能直接比较。15k TLS短时校准未达到99%计划成功门,所以当前一分钟基线选择10k,不把下文历史20k/饱和结果当作本次结果。首次恢复因运行器重复复制peer耗尽磁盘的失败目录保留,最终恢复证据仅用v2。历史 cms-publish-*-09-30-*、orm-runtime-64-09-30-v1 与 live-tcp-64-09-30-v1 不代表本次重跑。

共享编译的本机基线记录于 target/closure-shared-compile-measurement.json:私有debug核心、热OS缓存下,首次请求7.318秒、命中5.564秒;两个同步客户端提交同一份新源码分别7.463/7.838秒且产物一致。时间包含签名输入复核及IPC,不能当纯编译耗时或正式优化版CLI成绩。每个产物4,239,368字节,两条缓存合计8,478,736字节,校验无损坏;跨真实UID共享另有独立定向验收。

产物目录含二进制、测试证书、构建配置,可整体复制到兼容的 Linux 机器;运行器仅依赖 Python 3.11+ 标准库和 /proc。TLS 证书来自 test/dever-tests/fixtures/tls/,仅用于测试,执行时校验证书链和 localhost 名称。测试证书过期后需更新 fixture,不得关闭验证。

两个独立 peer 测试从仓库 config/setting.json 的 performance 读取 network_peer、live_peer,例如:

{"performance":{"network_peer":"target/performance/build-current/network_bench","live_peer":"target/performance/build-current/live_bench"}}

把该 performance 对象合并进已有设置,保留原有数据库配置。基准进程配置不读取环境变量。运行器在同文件系统硬链接已验证的只读 peer 二进制,仅跨文件系统时复制;每轮仍在其旁边以 0600 写入独立 config/setting.json 的 benchmark 对象。测量期间不得修改输入二进制。字段为 workers、connections、task_capacity、timeout_ms、lifetime_ms、HTTP/2 参数和 TLS 证书路径。CLI 负载参数仍显式传递。缺少配置、非法类型或越界值会报错;构建器通过不启动网络的 check-config 检查 peer 的 JSON 配置协议。旧协议的 manifest 不再接受,应重新构建产物,旧报告仍保留历史意义。

场景与统计口径

组 场景 计时/验证
memory 同步空闲、异步空闲、256 个等待 Channel 的任务、HTTP/HTTPS 监听 READY 后采样 RSS/PSS、VmHWM、线程/FD;parked 用取消场景的等待阶段,不另建实现
async 16384 次 run/wait、16384 次有界 Channel 发送与消费、取消并排空 256 个已启动任务 源码内单调时钟;计算结果/数量必须正确,打印在计时外;前两项每进程 10 轮、去掉首轮,取消保留完整排空耗时
http HTTP/HTTPS × Dever/Rust-runtime/原生 Hyper × 三种响应 固定速率、有限并发;状态码、Content-Type 和完整正文必须匹配;连接复用与新建连接可分别运行
http2 h2c prior knowledge/TLS ALPN × Dever/Rust-runtime/原生 Hyper × 物理连接/流拓扑 计时前建立固定物理连接;校验握手数、状态码、Content-Type、完整正文、吞吐、延迟和资源
http2-recovery 同一服务进程内重复建立、使用、关闭 HTTP/2 连接 每轮校验请求总账和握手数,断开后 FD 必须回到 READY 基线,保留 RSS 序列
live TCP/WebSocket/SSE × 连接档位 × 多轮连接/保持/断开 TCP/WS 校验带轮次和连接标识的回显;SSE 校验初始事件与心跳;对比各轮断开后的 FD/RSS
orm SQLite/PostgreSQL 的 idle、CRUD、list/cursor/stream、pool saturation、transaction cancel、HTTP+JSON+DB 每个 case 使用独立 bundle 和数据库;校验精确结果,再报告数据库操作延迟、吞吐、资源和连接回收
cms examples/cms/dever 和 examples/cms/md 的 Model、Seed、用户注册和发布事务 相同入口、单独 SQLite bundle,业务初始化与事务完成后分别采样 RSS/PSS/VmHWM 和可用时的 cgroup peak

ORM 基准

所有 Dever 产物在 build 阶段通过公开 dever 项目路径生成;无数据库声明的 runtime/http/live 使用 base archive。run 阶段只复制和执行已有二进制。每个数据库 case 都创建独立 bundle;SQLite 使用该 bundle 自己的 data/db/orm.db,重复轮次不会复用数据库状态。

构建器把测试源码暂存到 module/benchmark/<domain>/app.dever,共享能力位于 benchmark/bench、benchmark/config,由 api.dever 声明 CMD。fixture 不生成 main/package/exposes,也不向 dever build 传任意 entry。

CMS 从原始双源码项目构建,通过真实登录/租户/发布 HTTP 流程测量,不注入新的业务入口。普通 build 只构建 Dever CMS,cms-profiles 构建双源码。下方历史 READY|3 报告不代表当前实现。

2026-09-18 的当前源码专项报告保存在 target/performance/cms-dual-build-09-18-v2/{manifest.json,profile-report.json} 和 target/performance/cms-dual-rss-09-18/report.json。base/SQLite/PostgreSQL/both 原生程序分别为 411,352 / 2,558,200 / 2,708,112 / 4,672,888 字节。CMS Dever/Markdown 产物分别为 3,151,352 / 3,151,416 字节;各运行 3 次,READY 全部为 3,完成时间中位数分别为 19.87 / 19.69 ms,空闲 RSS 中位数同为 5.37 MiB,PSS 分别为 3.28 / 3.33 MiB,VmHWM 中位数同为 5.37 MiB。采用本机热缓存、独立 SQLite bundle、每次 READY 后 2 秒采样;无吞吐负载,不能把启动时间或空闲内存解释为业务吞吐/峰值容量。当前 cgroup 父目录不可写,未施加 64/128 MiB 硬限额,也未记录 cgroup peak。

默认只构建和运行 SQLite。真实 PostgreSQL 统一读取仓库根 config/setting.json 的 database.postgres_test,不接受环境变量或命令行连接覆盖;该本地文件已被 Git 忽略。其 url 必须在数据库名中包含唯一 {case},例如 postgres://bench:secret@127.0.0.1/dever_{case},查询参数或用户名中的占位符不算隔离。运行器按“输出目录短哈希 + driver + entry + repeat”展开 case,因此不同报告不会复用数据库;构建器和运行器以 create-new 和 0600 权限原子写入 bundle 的 config/setting.json,构建结束会删除含连接信息的临时配置,URL 不写入 manifest 或 report。运行器不会创建或清理 PostgreSQL 数据库,因此模板必须指向预先准备的专用测试数据库,不能指向已有业务库。

{
  "database": {
    "default": {
      "type": "sqlite",
      "path": "data/db/test.db"
    },
    "postgres_test": {
      "type": "postgres",
      "url": "postgres://bench:secret@127.0.0.1/dever_{case}",
      "tls": "disabled",
      "min_connections": 0,
      "max_connections": 4,
      "max_page_size": 100,
      "wait_timeout_ms": 5000,
      "io_timeout_ms": 30000
    }
  }
}
python3 test/performance/run.py run \
  --artifacts target/performance/build-current \
  --output target/performance/orm-smoke \
  --suite orm --duration 1 --warmup 0.2 --repeats 1

python3 test/performance/run.py run \
  --artifacts target/performance/build-current \
  --output target/performance/orm-postgres-smoke \
  --suite orm --duration 1 --warmup 0.2 --repeats 1

ORM 报告包含 binary size、RSS/PSS/VmHWM、cgroup current/peak/OOM、线程/FD、吞吐和 p50/p95/p99。当前生产 runtime 没有低成本的 pool wait、SQL 数和数据库连接数观测点,报告将这些字段明确标为 unavailable,同时记录配置的 pool capacity 和进程 FD,不用推算值冒充真实指标。transaction cancel 先验证单连接 pool 可用,再由长事务占满 pool、在响应完成前断开客户端,并要求连接在 HTTP handler timeout 前归还、计数仍为 0;这样不会把 handler timeout 回滚误报为客户端取消,也不会绕过语言对 transaction 内并发任务的静态限制。

run/wait 同时跑 Dever 与 Rust-runtime 的 root/worker 两种位置。root 在 run_entry_with 入口执行;worker 在一个计时外创建的结构化外层任务内执行。二者内部都用原有 task::run/wait、Bytes 输入和 checked Int 加法,保留子任务额度、取消和清理;没有以裸 Tokio task 代替语言任务。这个实验不修改默认调度。Channel/取消暂时仍是 Dever 单方基线。/proc 上下文切换计数汇总采样时仍存活的线程,线程退出会使累计值减少;总量分析用独立 perf stat,不能将采样差值当作精确任务计数。

live 用 --connection-tiers 32,128,512 --cycles 3 --duration 2 配置,每个档位独立启动服务,每轮保持约 2 秒,再关闭全部客户端并等待 300 ms。Rust 对端发布零基轮次的 connecting/connected/closing/disconnected 四阶段;Python 只用 connected/disconnected 保持窗口比较资源,记录最后一次断开样本和相对基线的 FD/RSS 差值。采样前后阶段变化的过渡样本不归属任何阶段,也不写入阶段报告。正文不匹配、阶段缺失、SSE 保持期无心跳或未回收全部客户端都失败;RSS 保留可能来自分配器缓存,不能单凭不回落断言泄漏。默认只测 32 条,档位不能超过构建时的 connections;--duration 为 0.3–60 秒,采样间隔最多 100 ms。live 的 SSE/WS 是明文 HTTP/1 场景,不包含 WSS 或 HTTP/2。

HTTP/2 用 物理连接数 x 每连接并发流数 描述拓扑。当前固定每连接 16 流、单流接收窗口 65535 字节、连接接收窗口 262144 字节、frame/send-buffer 上限 16384 字节。客户端在计时前完成精确数量的 h2c 或 TLS+h2 握手,每个连接的 SendRequest clone 由其 16 个请求槽共享;测量期间不重试、不替换连接、不自动扩容。request_slots 必须等于两个维度的乘积,successful_handshakes 必须等于物理连接数。断开后等待 300 ms,再以 READY 基线核对 FD。

三个路径为 /plain(hello)、/json(预制 {"ok":true})、/bytes(65536 个 x)。JSON 路径测 JSON 响应传输,不代表动态 JSON 解析/序列化性能。/bytes 跨越 HTTP/2 默认单流接收窗口,用于验证流控下的大正文传输。大正文在服务启动时准备,各服务均无业务日志。

网络记录 scheduled/sent/completed/succeeded/errors/dropped;dropped_capacity 表示无可用请求槽,dispatch_expired 表示负载发生器未能在窗口内发出。latency_ms 从计划发送时间算到成功完成,包含排队/调度;request_latency_ms 单独测实际发出到成功完成,dispatch_lag_ms 记录调度延迟。不能用两个 p99 相减估算请求耗时。没有成功样本时成功延迟为 null,失败不会稀释成功分位数。Tokio 定时器会让高频请求成批到期,这个发生器用于可复现的有界负载和相对比较,不适合独立证明微秒级服务延迟或极限吞吐。检查负载端 CPU、lag、丢弃,再解释服务结果。

延迟使用有界直方图:1 ms 内按微秒分桶,之后每个二倍区间分 64 桶,分位数取桶上界。每轮保留原始计数和资源采样,不把多个 p99 再平均成整体 p99。异步的 p95_ns_per_operation_sample 是批次平均耗时的分位数,不是逐任务 p95。ready_seconds 是父进程启动到观察 READY 的耗时,包含创建进程和最多约 5 ms 的观察误差。

小机器运行预算

建议先测 1 核/128 MiB,再试 64 MiB 和 2 核/256 MiB。这里是应用预算,不包含操作系统自身内存。--server-cpu 固定服务端 CPU,--client-cpus 指定逗号分隔的客户端 CPU 集合;--client-workers 指定 HTTP/1、HTTP/2 和 live 负载的 worker 数。客户端 CPU 不能包含服务端 CPU;不指定客户端集合时继承当前允许的 CPU。--cpu-quota 是计算份额上限,不能代替绑核。

python3 test/performance/run.py run \
  --artifacts target/performance/build-current \
  --output target/performance/budget-128m-1 \
  --suite all --duration 10 --warmup 2 --repeats 3 \
  --rates 100,1000 --paths /plain,/json,/bytes \
  --concurrency 16 --connection-mode both \
  --http2-connections 1,4 --recovery-connections 1 \
  --connection-tiers 32,128,512 --cycles 3 \
  --server-cpu 0 --client-cpus 1 --client-workers 1 \
  --cgroup-parent /sys/fs/cgroup/system.slice \
  --memory-mib 128 --cpu-quota 1

上例 cgroup 路径只适用于有相应权限且父组已启用 cpu/memory 控制器的机器。工具为每个被测进程创建新的 dever-perf-* 子组,仅把自己的子进程放进去;不会修改父组、其他服务或全局 swap。指定内存限额时把该子组 swap 限额设为零。没有权限会明确失败,不回退成“无限额通过”。负载端和 Python 采样进程留在预算之外。

memory.current/peak 包含该组被计费的用户内存、内核/网络资源等;不能与进程 RSS 混用。报告同时保留两者、OOM 事件和 CPU 节流统计。进程由 Python 子进程 exec 启动,cgroup 峰值也可能包含 exec 前短暂的 Python 启动页,空闲基线应看 READY 后的 current/RSS/PSS。50 ms 默认采样可能漏掉短暂峰值,VmHWM 和 cgroup peak 补充整个生命周期峰值。

构建和指纹读取会预热文件缓存,已缓存的可执行文件/共享库页可能已计费到父组或其他组,因此本工具的 cgroup current 可以小于进程 RSS。它不是冷启动总 RAM 成本;三者应交叉观察。本阶段测热文件缓存下的新进程,不操作系统 drop_caches,也不据此宣称整台机器最低内存。

运行器在结束/异常/中断时终止并回收自己创建的进程,删除自己的空 cgroup。memory/http/http2/live 服务最终由运行器结束进程,不把这个过程当作语言优雅退出验证;http2-recovery 和 live 单独验证连接多轮断开后的回收。小时级 soak、故障网络和真实小机器矩阵需另行执行。

每轮 Rust 负载程序使用同一已验摘要的不可变二进制硬链接,各自保留独立 config/setting.json 和日志;跨文件系统才复制。测量期间不得改写输入二进制。这样 100 轮连接恢复不会累积 100 份相同程序,也不通过符号链接让配置解析回到源程序目录。

对照边界与报告

  • Rust-runtime 服务使用与 Dever 相同的 HTTP/TLS/task runtime,帮助定位源代码桥接开销。
  • 原生 Hyper 服务匹配本测试正常 GET 的响应、连接数、头/正文上限和 TLS 配置;没有复刻 runtime 的完整 WriteTimeout、程序故障传播和结构化 Scope。不能由这个对照推断慢读/慢上传、异常报文的能力等价。
  • 每轮倒转实现顺序,减轻固定执行顺序造成的偏置。构建配置、CPU 型号、CPU 亲和性、父 cgroup、二进制指纹、原始日志、资源采样、负载端占用写入报告。
  • cases.jsonl 在每个场景完成后追加;report.json 即使中途失败也保留已完成结果。status=complete 表示测量完整,不表示没有请求错误或已经达到性能目标。先查看 errors/dropped/OOM,再查看延迟和内存。
  • 不预设绝对 QPS 或最低内存。已有计算热路径的 1.15 对照门槛仍在 test/dever-tests/tests/performance.rs,不推广成 HTTP 或整个语言的性能承诺。

首轮结果:2026-09-12

环境为 Linux x86_64、Intel Xeon Processor (Skylake, IBRS) 虚拟 CPU。服务绑定 CPU 0,负载端绑定 CPU 1;服务 runtime 1 worker、2 blocking slots、272 task slots、32 connections,负载端 16 个请求槽。主测每场景预热 0.5 秒、测量 2 秒,重复 3 个独立进程,目标速率 1000 请求/秒。不是持续压力或吞吐上限测量。

Dever 空闲场景 三轮 RSS 中位值
同步程序 1.90 MiB
异步运行时 2.55 MiB
256 个已启动、等待 Channel 的任务 3.38 MiB
HTTP 监听 3.45 MiB
HTTPS 监听 4.37 MiB

以上为 128 MiB 专属 cgroup 下的 READY 后采样。另用 64 MiB 限额重复 3 轮空闲场景,全部完成、无 OOM。READY 的一次输出会启动异步程序的 blocking worker,异步场景观察到 3 个线程;这是该 fixture 的基线,不是“完全不打印”的最小运行时。

异步三轮中位值:完整 run/wait 一轮约 15.90 µs,Channel 生产与消费平均每项约 0.89 µs;停止并排空 256 个已启动等待任务约 0.61–0.67 ms。前两项每进程校验 10×16384 项,去掉首批预热;包含结构化任务语义,不能把 run/wait 数字称为纯任务分配耗时。尚未提供这几组异步操作的同语义 Rust 对照。

HTTP/HTTPS 三实现、三路径共 54 组正式测量,108000 个计划请求中 107962 个发出且成功,0 请求错误。38 个未发出:32 个超过发送窗口、6 个无空闲并发槽;预热另有 43 个未发出,原始记录保留。各组成功速率约 999 请求/秒,受目标速率限制,不能宣称最大吞吐为 1000。

Dever 路径 HTTP 每轮实际请求 p99 范围 HTTPS 每轮实际请求 p99 范围
/plain 0.243–1.248 ms 0.580–0.909 ms
/json 0.280–0.342 ms 0.897–2.752 ms
/bytes 0.392–0.500 ms 0.839–2.400 ms

表中是 dispatched→completed,每列列出三个独立 p99 的范围,没有把它们合并成全局 p99;计划发送延迟和负载端 lag 另在 JSON 中。短时虚拟机抖动明显,这些结果不用于宣称哪种实现更快。Dever 在这组负载的 RSS 中位值约 HTTP 3.9 MiB、HTTPS 4.9–5.1 MiB。

另以每次新建连接、100 请求/秒测试 HTTP/HTTPS 全路径三实现,共 18 组、900 个正式请求,全部成功且无丢弃。主测 78 组(15 内存、9 异步、54 网络)、新连接 18 组、64 MiB 内存 15 组均无 OOM。

原始结果及对应产物保存在仓库 target/performance/:

  • baseline-128m/report.json:最终三轮基线,对应 build-2/manifest.json。
  • connections-new-128m/report.json:新连接模式,对应 build-2。
  • memory-64m/report.json:64 MiB 空闲基线,对应 build-1;该组使用的 Dever 二进制与 build-2 一致。
  • smoke-1/report.json:早期连通检查,保留当时指纹,不用于最终网络比较。

以上首轮结果保留当时的测试范围;后续测量见下节。

第二轮:高负载与调度定位

http-load-p2-final/report.json 复用 build-2 产物,1 worker、32 服务连接和 32 客户端请求槽、服务 1 CPU/128 MiB。仅 /plain,预热 0.5 秒、测量 2 秒;HTTP/HTTPS、三个实现、5000/15000/30000 请求每秒,均重复三轮,共 54 组。

正式窗口共计划 1800000 个请求,1500830 个实际发出且成功,错误 0。未发出 299170 个:容量丢弃 298476、发送窗口过期 694;所有预算组无 OOM。下表是三个独立进程的成功 QPS 中位值,不是最大吞吐。

目标请求/秒 Dever HTTP Rust-runtime HTTP Hyper HTTP Dever HTTPS Rust-runtime HTTPS Hyper HTTPS
5000 4993 4994 4993 4970 4972 4970
15000 14904 14897 14922 14839 14756 14726
30000 21866 22187 22324 21217 21320 22236

最高输入档位 Dever 服务 CPU 中位值为 HTTP 0.53 核、HTTPS 0.62 核,客户端为 0.77/0.86 核;Dever RSS 约 4.12/5.32 MiB。32 请求槽与定时器成批发送已经影响结果,不能据此断言服务端上限是每秒两万。提高并发槽、使用独立负载机器应作为下一次吞吐上限测量的条件。

http-load-p2/report.json 保留初次中断:Linux 退出时 /proc 内存字段先于 waitpid 消失,导致采样工具失败。修复后才完整重跑;不能把初次中断算成语言服务失败,也不将两次样本混合。

async-p2/report.json 使用 build-p2、1 worker/1 CPU/128 MiB、1040 task slots、每批 16384 次操作,重复 3 个进程;每进程十批、去掉首批。共 18 组,校验全部通过且无 OOM。下表为进程内中位数再取三进程中位数:

run/wait 位置 Dever 同 runtime、同优化参数的 Rust
入口 root 15.50 µs/次 15.59 µs/次
外层结构化任务 worker 2.003 µs/次 1.984 µs/次

这个串行微任务场景中,调度位置使 Dever 平均单次耗时降低约 87%,约 7.7 倍速度;不推广为所有业务程序加速。两种语言路径数值接近。源码路径显示 run_entry_with 用入口线程 block_on,run body 和 supervisor 在 Tokio worker 执行;单核下逐项等待造成频繁 OS 线程切换。profile-p2/final-runtime-*.stat 是独立 perf stat 的整体软件计数,对应 build-p2/async_bench;未加 cgroup、固定 CPU 0。虚拟机不支持 cycles/instructions,未报告硬件 IPC 或采样火焰图。

同组 Channel 约 0.912 µs/项;256 个任务停止并排空三次共 0.688/0.916/1.045 ms。这里只新增对照和证据,生产入口调度尚未优化。后续优化入口调度时需确认入口 future 的 Send 条件,根任务不应挤占子任务配额,并验证入口故障、子任务取消和 blocking 清理;不应直接照搬实验外层 run 或删除结构化任务语义。

第二轮:长连接容量与回收

live-p2-final/report.json 使用 build-p2-final、1 worker/1 CPU/128 MiB、512 connection slots、1040 task slots;32/128/512 条连接各重复三进程,每进程三轮连接/保持 2 秒/断开。TCP/WS 每约 100 ms 校验一次小消息回显,SSE 校验初始事件和 100 ms 心跳。

27 组共建立并关闭 18144 条连接,校验 377061 条消息,无协议错误和 OOM。81 次断开后的服务 FD 均回到启动基线。下面是第三轮保持窗口 RSS 的三个独立进程中位值,包含分配器在此前两轮保留的内存:

同时连接数 TCP RSS WebSocket RSS SSE RSS
32 3.99 MiB 5.11 MiB 5.03 MiB
128 4.90 MiB 9.44 MiB 9.16 MiB
512 8.70 MiB 17.00 MiB 24.97 MiB

512 条连接保持时服务 CPU 约 TCP 0.15–0.16 核、WS 0.18–0.20 核、SSE 0.12–0.14 核。这里测的是持续小消息和心跳连接,不是大消息吞吐或完全不读的客户端。

初次 live-p2/report.json 也完成了全部消息校验,但有一条下一轮的采样被误归属上轮 disconnected,产生假的 FD +15。最终报告使用采样前后阶段一致性判断,完整重跑;资源回收结论只引用 final 报告。

随后 live-cycles-p2/report.json 对每种协议保持 512 条连接,十轮重连、每轮保持 3 秒,单场景约 34 秒。共 15360 次连接、471207 条校验消息,错误/OOM 为零,30 次 FD 回落均为零差值。TCP 断开后的 RSS 最后稳定在约 8.6–8.8 MiB,WS 约 17 MiB;SSE 前三轮 21.33/24.48/25.12 MiB,后七轮为 25.24–25.36 MiB,未观察到按轮持续增长。这是短时重复回收证据,不是小时级泄漏或生产网络稳定性证明。

复现本轮长连接两组命令(输出目录另取新名称):

python3 test/performance/run.py run --artifacts target/performance/build-p2-final --output target/performance/live-repeat --suite live --duration 2 --connection-tiers 32,128,512 --cycles 3 --repeats 3 --server-cpu 0 --client-cpus 1 --client-workers 1 --cgroup-parent /sys/fs/cgroup/system.slice --memory-mib 128 --cpu-quota 1
python3 test/performance/run.py run --artifacts target/performance/build-p2-final --output target/performance/live-more-cycles --suite live --duration 3 --connection-tiers 512 --cycles 10 --repeats 1 --server-cpu 0 --client-cpus 1 --client-workers 1 --cgroup-parent /sys/fs/cgroup/system.slice --memory-mib 128 --cpu-quota 1

本轮 12 个运行器检查、4 个独立 TCP/SSE 对端检查通过,11 个 Dever 入口及两个新 Rust example 离线构建通过。Rustfmt/Clippy 未安装,未运行;Dever 格式、Python 解析、源码空白与最终范围检查通过。生产编译器/运行时没有修改,未运行全量测试。

第三轮:HTTP/2 吞吐、流控与连接回收

2026-09-13 使用 build-http2-final 产物完成正式矩阵。环境仍为 Linux x86_64、Intel Xeon Processor (Skylake, IBRS) 虚拟 CPU;服务绑定 CPU 0,负载端绑定 CPU 1。服务使用 1 worker、128 MiB cgroup、1 CPU quota、swap 0;负载端不计入服务预算。每个 case 预热 0.5 秒、测量 2 秒、启动独立服务进程,三实现与两种传输的执行顺序逐轮倒转。

/plain 共 108 个 case、3600000 个计划请求,其中 2729242 个发出且完整成功,错误 0。870758 个未发出请求中,869368 个受请求槽限制,1390 个超过调度窗口;全部计数守恒。108 次正式负载和 108 次预热的握手数均与物理连接数一致,断开后的 FD 均回到 READY 基线,无 OOM。下表是 30000 请求/秒输入档位的三轮中位数;p99 是实际发出到完整响应的请求延迟。

实现 传输 1x16 成功 QPS 4x16 成功 QPS 4x16 请求 p99 4x16 服务 RSS
Dever h2c 10578 29575 2.208 ms 4.60 MiB
Rust-runtime h2c 10804 28996 3.392 ms 5.00 MiB
Hyper h2c 11956 29108 1.984 ms 4.59 MiB
Dever TLS+h2 9957 29814 1.840 ms 5.59 MiB
Rust-runtime TLS+h2 10122 28681 2.880 ms 6.07 MiB
Hyper TLS+h2 11555 28719 3.200 ms 5.68 MiB

Dever 4x16 在 5000、15000、30000 三档的 h2c 成功 QPS 中位值分别为 4995、14983、29575,TLS+h2 分别为 4994、14977、29814。1x16 在高输入下只有约 10k–10.6k 成功 QPS,而 4x16 接近 30k,说明这次单连接结果受固定 16 个请求槽明显约束;不能把任一数字解释成服务端极限。最高档 4x16 的 Dever 服务 CPU 中位值为 h2c 0.89 核、TLS+h2 0.93 核,负载端也约 0.9 核,进一步限制了极限吞吐结论。三种实现的短时差异不具备跨机器性能承诺意义。

/bytes 共 108 个 case、115200 个计划请求,115180 个完整成功、错误 0、丢弃 20、无 OOM,握手和 FD 检查全部通过。三种实现、两种传输、两种拓扑在 100/500/1000 请求每秒档均维持目标速率;这相当于单 case 最高约 62.5 MiB/s 的响应正文。Dever 的 RSS 中位值为 h2c 4.04–4.23 MiB、TLS+h2 5.05–5.30 MiB。短 case 的 p99 有明显虚拟机抖动,原始三轮数据比单个汇总值更适合定位异常。

http2-recovery-128m 对三种实现、h2c/TLS 各在同一服务进程中运行 10 轮 1x16 连接周期,共 60 轮、30000 个计划请求;29984 个完整成功、错误 0、丢弃 16。每轮握手数正确,60 次断开后的 FD 差值均为 0,无 OOM。六组最后一轮相对第一轮的断开后 RSS 变化为 8–100 KiB,短时序列未观察到按轮持续增长;这不是小时级泄漏证明。

另在 64 MiB、1 CPU、swap 0 下运行 Dever 1x16、5000 请求每秒的 h2c/TLS 代表场景。20000 个计划请求中 19991 个完整成功、错误 0、丢弃 9、无 OOM;h2c/TLS 成功 QPS 为 4997/4996,RSS 为 4.04/5.17 MiB,PSS 为 2.00/3.10 MiB。cgroup peak 只有 1.30/1.35 MiB,是热文件页可能已计费给其它组所致,不能代替进程 RSS/PSS 或冷启动总内存。

正式结果和构建清单位于:

  • build-http2-final/manifest.json:16 个二进制的指纹、大小、构建配置和固定 HTTP/2 limits。
  • http2-plain-128m/report.json:/plain 128 MiB 正式矩阵。
  • http2-bytes-128m/report.json:/bytes 128 MiB 正式矩阵。
  • http2-recovery-128m/report.json:128 MiB 十轮连接恢复矩阵。
  • http2-active-64m/report.json:64 MiB Dever 活跃代表场景。

历史 build-http2-final 产物已不在当前工作树;以下是当时使用的命令记录,精确复现历史二进制须先恢复匹配的旧产物。本机当前可直接运行的命令见下方第四轮,复现时必须换新输出目录:

python3 test/performance/run.py run --artifacts target/performance/build-http2-final --output target/performance/http2-plain-repeat --suite http2 --duration 2 --warmup 0.5 --rates 5000,15000,30000 --paths /plain --http2-connections 1,4 --repeats 3 --server-cpu 0 --client-cpus 1 --client-workers 1 --cgroup-parent /sys/fs/cgroup/system.slice --memory-mib 128 --cpu-quota 1
python3 test/performance/run.py run --artifacts target/performance/build-http2-final --output target/performance/http2-bytes-repeat --suite http2 --duration 2 --warmup 0.5 --rates 100,500,1000 --paths /bytes --http2-connections 1,4 --repeats 3 --server-cpu 0 --client-cpus 1 --client-workers 1 --cgroup-parent /sys/fs/cgroup/system.slice --memory-mib 128 --cpu-quota 1
python3 test/performance/run.py run --artifacts target/performance/build-http2-final --output target/performance/http2-recovery-repeat --suite http2-recovery --duration 0.5 --warmup 0.1 --cycles 10 --recovery-rate 1000 --recovery-path /plain --recovery-connections 1 --repeats 1 --server-cpu 0 --client-cpus 1 --client-workers 1 --cgroup-parent /sys/fs/cgroup/system.slice --memory-mib 128 --cpu-quota 1

N5 的接口与约束见 LANGUAGE.md。这轮只建立 loopback、热文件缓存、短时固定速率下的相对证据,不包含跨主机网络、故障注入、小时级 soak 或多平台矩阵。HTTP/2 不保证所有负载都比 HTTP/1.1 快;普通 WebSocket 继续走 HTTP/1,RFC 8441 扩展 CONNECT 尚未实现。

第四轮:多核负载端、HTTP/2 饱和与稳态

2026-09-18 使用 typed-orm-build-v2/manifest.json 中校验过指纹的现存 HTTP/2 二进制和测试证书;原 build-http2-final 不在当前工作树,未重建产物。服务端绑定 CPU 0、1 worker、1 CPU quota、无 swap;负载端为 CPU 1–4、4 workers,位于服务 cgroup 外。机器允许 CPU 0–4。服务预算为 128 MiB,稳态另测 64 MiB。所有场景使用 /plain、16 条物理连接、每连接 16 流,只有自有 loopback 端口;这是同一虚拟机的热缓存观测,不是跨机器或生产容量。

128 MiB 扫描覆盖 h2c/TLS+h2 的 30k/45k/60k/90k 输入,每档预热 1 秒、计时 5 秒、2 个重复。16/16 case 计数守恒,请求错误、OOM 和断开后的 FD 差均为 0;每次握手数为 16。下表为成功 QPS 的两轮中位值:

传输 30k 45k 60k 90k
h2c 29,899 36,690 36,027 39,486
TLS+h2 29,770 38,205 37,410 38,136

45k 以上服务 CPU 约 0.96–1.00 核,客户端约 1.43–1.97 核;未发送请求主要因 256 个请求槽满,调度窗口过期数量相对很少。因此观测到当前拓扑约 36k–40k req/s 的吞吐平台,不能把它归结为纯服务端极限或把输入 90k 当成实际吞吐。28k 的首轮 60 秒成功率为 h2c 98.68%、TLS+h2 97.91%,未达 99% 验收门槛;24k 的 10 秒校准 TLS+h2 为 99.18%,余量仍小。20k 的 10 秒校准分别为 99.96% 和 99.92%,所以正式稳态统一采用 20k,低于较低观测平台的 80%。这些探索报告保留,不算正式稳态通过。

正式每个 case 预热 1 秒、测量 60 秒、计划 1,200,000 次请求。成功率是成功请求除以计划请求,容量丢弃仍在原始报告中,不称为零丢弃:

服务预算 传输 成功/计划 成功 QPS 请求 p99 服务 RSS 高水位 OOM/错误/FD 差
128 MiB h2c 1,197,220/1,200,000 (99.77%) 19,953 5.376 ms 6.78 MiB 0/0/0
128 MiB TLS+h2 1,199,036/1,200,000 (99.92%) 19,983 4.800 ms 7.83 MiB 0/0/0
64 MiB h2c 1,199,221/1,200,000 (99.94%) 19,986 4.672 ms 6.43 MiB 0/0/0
64 MiB TLS+h2 1,197,545/1,200,000 (99.80%) 19,958 5.184 ms 7.90 MiB 0/0/0

128 MiB 下在各自的同一服务进程上完成 h2c/TLS+h2 各 100 轮 16x16 恢复;每轮 0.1 秒、1000 req/s,随后观察断开 0.3 秒。两组各计划 10,000 次、成功 9,944 次,其余各 56 次为调度窗口过期,请求错误和 OOM 为 0。每轮至少成功 95(h2c)/98(TLS+h2)次,握手始终 16,200 次断开后的 FD 差全部为 0。断开后 RSS 从首轮到末轮为 h2c 4,845,568→5,357,568 B,TLS+h2 5,959,680→6,647,808 B;保留完整逐轮序列,不将分钟级增长或回落解释成小时级泄漏结论。邻接的 HTTP/HTTPS 各 30/30 成功,TCP/WS/SSE 各 16/16 连接完成关闭,零错误。

复现命令(输出目录必须换新名称;运行前确认 CPU 0–4 和 cgroup 权限,不接触现有服务):

python3 test/performance/run.py run --artifacts target/performance/typed-orm-build-v2 --output target/performance/http2-scan-next --suite http2 --duration 5 --warmup 1 --rates 30000,45000,60000,90000 --paths /plain --http2-connections 16 --repeats 2 --implementations dever --server-cpu 0 --client-cpus 1,2,3,4 --client-workers 4 --cgroup-parent /sys/fs/cgroup/system.slice --memory-mib 128 --cpu-quota 1
python3 test/performance/run.py run --artifacts target/performance/typed-orm-build-v2 --output target/performance/http2-steady-128-next --suite http2 --duration 60 --warmup 1 --rates 20000 --paths /plain --http2-connections 16 --repeats 1 --implementations dever --server-cpu 0 --client-cpus 1,2,3,4 --client-workers 4 --cgroup-parent /sys/fs/cgroup/system.slice --memory-mib 128 --cpu-quota 1
python3 test/performance/run.py run --artifacts target/performance/typed-orm-build-v2 --output target/performance/http2-steady-64-next --suite http2 --duration 60 --warmup 1 --rates 20000 --paths /plain --http2-connections 16 --repeats 1 --implementations dever --server-cpu 0 --client-cpus 1,2,3,4 --client-workers 4 --cgroup-parent /sys/fs/cgroup/system.slice --memory-mib 64 --cpu-quota 1
python3 test/performance/run.py run --artifacts target/performance/typed-orm-build-v2 --output target/performance/http2-recovery-next --suite http2-recovery --duration 0.1 --cycles 100 --recovery-rate 1000 --recovery-connections 16 --repeats 1 --implementations dever --server-cpu 0 --client-cpus 1,2,3,4 --client-workers 4 --cgroup-parent /sys/fs/cgroup/system.slice --memory-mib 128 --cpu-quota 1

本轮原始结果位于 target/performance/http2-ceiling-{scan,steady-128m-20k,steady-64m-20k,recovery}-09-18/;28k 首轮及 24k/20k 校准在相邻命名目录。吞吐平台、20k 持续负载和 100 轮恢复是不同问题,不能互相替代;服务与负载仍共享虚拟机和热缓存,结果不外推到真实网络、小物理机或小时级运行。