v2.3.0 多并发测试 + 多测试结果对比
- 并发数配置:默认单流(1),预设2/4并发档,支持丝滑添加任意自定义并发数(≥1), 多档勾选时同一测试分别跑各并发档并并排对比 - 并发执行:每采样同时发起N个并行流(ThreadPoolExecutor),整批吞吐聚合 (多流prompt/output之和/批首字/批耗时),并记录每流明细streams; 并发=1 与旧版单流行为一致;每流独立随机前缀避免共享缓存 - 汇总:新增 by_concurrency(按并发分组,含单流均解码) + by_length_concurrency(长度x并发网格) + concurrency_levels - 详情页:新增「按并发数汇总」表 + 并发对比折线图(X=并发数,预填充左虚线/解码右实线, /concurrency-chart) - 多测试对比:历史表格勾选(可全选)多个测试 -> /api/compare 返回对比表+柱状图CSV+并发折线图CSV, 前端弹窗展示指标对比表 + 柱状图(预填充空心/解码实心) + 解码随并发折线图,画图CSV可复制/PNG可下载 - 通用图表代理 POST /api/chart(转发 data-chart-tool 请求体返回 PNG) - 历史列表增加「并发」列与勾选列;Excel 导出增加并发数列表/按并发分组/采样并发列 - 文档:README/API.md 同步更新
This commit is contained in:
@@ -130,9 +130,10 @@
|
||||
| `name` | string | `""` | 测试名称/主题(会存入测试记录并展示在历史与详情) |
|
||||
| `context_lengths` | number[] | `[512,2048,4096,8192,16384,32768,65536,131072]` | 要测试的上下文长度列表,每个长度独立校准+预热+采样 |
|
||||
| `max_tokens` | number | `128` | 解码输出 token 长度 |
|
||||
| `samples` | number | `2` | 每个上下文长度的采样次数 |
|
||||
| `warmup` | bool | `true` | 测试前空转预热(不计速度) |
|
||||
| `avoid_cache` | bool | `true` | 随机前缀避免缓存命中 |
|
||||
| `samples` | number | `2` | 每个(长度×并发)组合的采样次数 |
|
||||
| `concurrency_levels` | number[] | `[1]` | 并发数列表(默认单流)。>1 时每采样同时发起 N 个并行流,聚合为整批吞吐指标;多档自动并排对比 |
|
||||
| `warmup` | bool | `true` | 测试前空转预热(不计速度,按并发数预热) |
|
||||
| `avoid_cache` | bool | `true` | 随机前缀避免缓存命中(每个并发流独立前缀) |
|
||||
|
||||
**响应:** `{ "ok": true, "id": 9 }`
|
||||
|
||||
@@ -169,9 +170,10 @@
|
||||
"id": 9, "created_at": "...", "status": "done",
|
||||
"provider": "openai", "model": "...", "name": "...", "error": "",
|
||||
"config": { "base_url": "...", "api_key": "sk-x****", ... },
|
||||
"gen": { "name": "...", "context_lengths": [512, 2048], "max_tokens": 128, "samples": 1, "warmup": true, "avoid_cache": true },
|
||||
"gen": { "name": "...", "context_lengths": [512, 2048], "max_tokens": 128, "samples": 1, "concurrency_levels": [1, 2, 4], "warmup": true, "avoid_cache": true },
|
||||
"summary": {
|
||||
"samples_total": 2, "samples_ok": 2,
|
||||
"samples_total": 6, "samples_ok": 6,
|
||||
"concurrency_levels": [1, 2, 4],
|
||||
"calibration_chars_per_token": 1.82,
|
||||
"avg_ttft_ms": 1808.7, "min_ttft_ms": 1122.8, "max_ttft_ms": 2494.6,
|
||||
"avg_prefill_speed": 694.2, "min_prefill_speed": 515.7, "max_prefill_speed": 872.7,
|
||||
@@ -179,9 +181,14 @@
|
||||
"avg_prompt_tokens": 1378.0, "avg_output_tokens": 128.0,
|
||||
"avg_total_ms": 4148.4, "min_total_ms": 3449.9, "max_total_ms": 4846.9,
|
||||
"by_length": {
|
||||
"512": { "samples_total": 1, "samples_ok": 1, "avg_ttft_ms": 1122.8, "avg_prefill_speed": 515.7, "avg_decode_speed": 55.0, "avg_prompt_tokens": 579, "avg_output_tokens": 128, "avg_total_ms": 3449.9 },
|
||||
"2048": { "samples_total": 1, "samples_ok": 1, "avg_ttft_ms": 2494.6, "avg_prefill_speed": 872.7, "avg_decode_speed": 54.4, "avg_prompt_tokens": 2177, "avg_output_tokens": 128, "avg_total_ms": 4846.9 }
|
||||
}
|
||||
"512": { "samples_total": 3, "samples_ok": 3, "avg_ttft_ms": 1122.8, "avg_prefill_speed": 515.7, "avg_decode_speed": 55.0, "avg_prompt_tokens": 579, "avg_output_tokens": 128, "avg_total_ms": 3449.9 },
|
||||
"2048": { "samples_total": 3, "samples_ok": 3, "avg_ttft_ms": 2494.6, "avg_prefill_speed": 872.7, "avg_decode_speed": 54.4, "avg_prompt_tokens": 2177, "avg_output_tokens": 128, "avg_total_ms": 4846.9 }
|
||||
},
|
||||
"by_concurrency": {
|
||||
"1": { "samples_total": 2, "samples_ok": 2, "avg_ttft_ms": 1122.8, "avg_prefill_speed": 515.7, "avg_decode_speed": 55.0, "avg_stream_decode": 55.0, "avg_prompt_tokens": 579, "avg_output_tokens": 128, "avg_total_ms": 3449.9 },
|
||||
"2": { "samples_total": 2, "samples_ok": 2, "avg_ttft_ms": 2494.6, "avg_prefill_speed": 872.7, "avg_decode_speed": 108.0, "avg_stream_decode": 54.0, "avg_prompt_tokens": 1158, "avg_output_tokens": 256, "avg_total_ms": 4846.9 }
|
||||
},
|
||||
"by_length_concurrency": { "512": { "1": {...}, "2": {...} }, "2048": {...} }
|
||||
},
|
||||
"runs": [
|
||||
{ "run_index": 1, "context_length": 512,
|
||||
@@ -245,6 +252,52 @@
|
||||
```
|
||||
> `csv` 即画图数据(第一列=上下文长度,第二列=预填充速度,第三列=解码速度),前端「复制画图数据」按钮复制的就是它。
|
||||
|
||||
### `GET /api/tests/<id>/concurrency-chart`
|
||||
并发对比折线图 PNG(X 轴=**并发数**,左轴=预填充速度虚线、右轴=解码速度实线),用于直观展示吞吐随并发的变化。仅当本次测试包含**多个并发档**时才有数据。
|
||||
|
||||
**失败响应:** `{ "ok": false, "error": "无并发分组采样数据(本次测试可能只测了单流),无法画图" }`(400)
|
||||
|
||||
### `GET /api/tests/<id>/concurrency-chart-data`
|
||||
并发对比画图数据(CSV + 图表请求配置)。
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"csv": "并发数, 预填充速度(tok/s), 解码速度(tok/s)\n1, 767.30, 43.40\n2, 1388.20, 70.90\n4, 2085.00, 78.70",
|
||||
"rows": [[1, 767.3, 43.4], [2, 1388.2, 70.9], [4, 2085.0, 78.7]],
|
||||
"payload": { "data": "...", "chartType": "line", "title": "...", "dualYAxis": true, ... }
|
||||
}
|
||||
```
|
||||
|
||||
### `POST /api/chart`
|
||||
通用图表代理:把任意 data-chart-tool `/api/chart` 请求体转发过去并返回 PNG(多测试对比面板用)。请求体即 data-chart-tool 的参数(`data`/`chartType`/`seriesTypes`/`seriesStyles`/`dualYAxis`…)。
|
||||
|
||||
**成功响应:** `Content-Type: image/png`
|
||||
|
||||
### `POST /api/compare`
|
||||
把多个测试结果放在一起对比。
|
||||
|
||||
**请求:** `{ "ids": [9, 10, 11] }`(最多 20 个)
|
||||
|
||||
**响应:**
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"rows": [ { "id": 9, "label": "#9 xxx", "created_at": "...", "name": "...", "model": "...",
|
||||
"concurrency_levels": [1, 2, 4], "by_concurrency": {...},
|
||||
"samples_ok": 6, "samples_total": 6,
|
||||
"avg_ttft_ms": 1122.8, "avg_prefill_speed": 515.7, "avg_decode_speed": 55.0,
|
||||
"avg_stream_decode": 55.0, "avg_output_tokens": 128, "avg_total_ms": 3449.9 } ],
|
||||
"bar_csv": "测试, 预填充速度(tok/s), 解码速度(tok/s)\n#9 xxx, 515.70, 55.00",
|
||||
"bar_payload": { "data": "...", "chartType": "bar", "seriesTypes": ["bar", "bar"], "seriesStyles": ["hollow", "solid"], ... },
|
||||
"line": { // 仅当所选测试存在 >=2 个共同并发档时返回,否则为 null
|
||||
"csv": "并发数, #9 xxx, #10 yyy\n1, 55.00, 52.10\n2, 108.00, 99.30\n4, 190.20, 175.60",
|
||||
"payload": { "data": "...", "chartType": "line", ... },
|
||||
"levels": [1, 2, 4]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 导出(Excel / JSON)
|
||||
@@ -252,8 +305,8 @@
|
||||
### `GET /api/tests/<id>/export.xlsx`
|
||||
导出 Excel 报告(**3 个 Sheet**:汇总 / 采样明细 / 日志),`Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`。
|
||||
|
||||
- **汇总**:测试信息 + 整体统计指标(平均/最大/最小)+ 按上下文长度分组
|
||||
- **采样明细**:每次采样的上下文长度与全部指标
|
||||
- **汇总**:测试信息(含并发数列表)+ 整体统计指标(平均/最大/最小)+ 按上下文长度分组 + **按并发数分组**(整批吞吐,含单流均解码)
|
||||
- **采样明细**:每次采样的上下文长度、**并发**、流成功/总数与全部指标
|
||||
- **日志**:完整测试日志
|
||||
|
||||
### `GET /api/tests/<id>/export.json`
|
||||
@@ -267,12 +320,14 @@
|
||||
|----|------|----------|
|
||||
| `configs` | 保存的接口配置 | id, name, provider, base_url, api_key, model, temperature |
|
||||
| `tests` | 测试记录 | id, status(running/done/error/canceled), provider, model, **name**, config_json, gen_cfg_json, summary_json, error |
|
||||
| `test_runs` | 每次采样 | id, test_id, run_index, **context_length**, metrics_json, error |
|
||||
| `test_runs` | 每次采样 | id, test_id, run_index, **context_length**, metrics_json, error(metrics 含 `concurrency`/`streams_total`/`streams_ok`/`avg_stream_decode`/`streams`(每流明细)) |
|
||||
| `logs` | 测试日志 | id, test_id, level, msg, rel |
|
||||
|
||||
**summary 整体指标字段:**
|
||||
`avg_/min_/max_` 前缀 × `ttft_ms` / `prefill_speed` / `decode_speed` / `total_ms`,以及 `avg_prompt_tokens` / `avg_output_tokens` / `avg_cached_tokens` / `best_ttft_ms`(= min_ttft_ms)。
|
||||
|
||||
**分组字段:** `by_length`(按上下文长度)、`by_concurrency`(按并发数,含 `avg_stream_decode` 单流均解码)、`by_length_concurrency`(长度×并发全网格)、`concurrency_levels`(本次测试的并发档列表)。
|
||||
|
||||
---
|
||||
|
||||
## 9. curl 使用示例
|
||||
@@ -291,10 +346,10 @@ curl -X POST $BASE/api/configs -H 'Content-Type: application/json' \
|
||||
curl -X POST $BASE/api/configs/test -H 'Content-Type: application/json' \
|
||||
-d '{"provider":"openai","base_url":"http://121.40.164.32:18003/v1","api_key":"sk-xxx","model":"unsloth/Qwen3.8-27B-Q4_K_M"}'
|
||||
|
||||
# 启动速度测试(异步)
|
||||
# 启动速度测试(异步,含多并发档)
|
||||
curl -X POST $BASE/api/tests -H 'Content-Type: application/json' -d '{
|
||||
"config": {"provider":"openai","base_url":"http://121.40.164.32:18003/v1","api_key":"sk-xxx","model":"unsloth/Qwen3.8-27B-Q4_K_M"},
|
||||
"gen": {"name":"各长度对比","context_lengths":[512,2048,8192],"max_tokens":128,"samples":2,"warmup":true,"avoid_cache":true}
|
||||
"gen": {"name":"并发对比","context_lengths":[2048],"max_tokens":128,"samples":2,"concurrency_levels":[1,2,4],"warmup":true,"avoid_cache":true}
|
||||
}'
|
||||
|
||||
# 查询测试列表 / 详情
|
||||
@@ -303,8 +358,12 @@ curl $BASE/api/tests/9
|
||||
|
||||
# 画图数据(CSV)
|
||||
curl $BASE/api/tests/9/chart-data
|
||||
# 折线图 PNG(预填充左轴虚线 / 解码右轴实线)
|
||||
# 折线图 PNG(预填充左轴虚线 / 解码右轴实线,X=上下文长度)
|
||||
curl -o chart.png $BASE/api/tests/9/chart
|
||||
# 并发对比折线图(X=并发数)
|
||||
curl -o cc.png $BASE/api/tests/9/concurrency-chart
|
||||
# 多测试对比
|
||||
curl -X POST $BASE/api/compare -H 'Content-Type: application/json' -d '{"ids":[9,10,11]}'
|
||||
|
||||
# 导出
|
||||
curl -OJ $BASE/api/tests/9/export.xlsx
|
||||
|
||||
Reference in New Issue
Block a user