Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fc2005806a | ||
|
|
9580372f85 | ||
|
|
9e457cd22e |
@@ -114,11 +114,12 @@
|
||||
},
|
||||
"gen": {
|
||||
"name": "Qwen3 不同上下文长度速度对比",
|
||||
"context_lengths": [512, 2048, 4096, 8192, 16384, 32768, 65536, 131072],
|
||||
"context_lengths": [4096, 8192, 16384, 32768, 65536, 98304, 131072],
|
||||
"max_tokens": 128,
|
||||
"samples": 2,
|
||||
"warmup": true,
|
||||
"avoid_cache": true
|
||||
"avoid_cache": true,
|
||||
"interval": 5
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -128,12 +129,13 @@
|
||||
| 字段 | 类型 | 默认 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `name` | string | `""` | 测试名称/主题(会存入测试记录并展示在历史与详情) |
|
||||
| `context_lengths` | number[] | `[512,2048,4096,8192,16384,32768,65536,131072]` | 要测试的上下文长度列表,每个长度独立校准+预热+采样 |
|
||||
| `context_lengths` | number[] | `[4096,8192,16384,32768,65536,98304,131072]` | 要测试的上下文长度列表,每个长度独立校准+预热+采样 |
|
||||
| `max_tokens` | number | `128` | 解码输出 token 长度 |
|
||||
| `samples` | number | `2` | 每个(长度×并发)组合的采样次数 |
|
||||
| `concurrency_levels` | number[] | `[1]` | 并发数列表(默认单流)。>1 时每采样同时发起 N 个并行流,聚合为整批吞吐指标;多档自动并排对比 |
|
||||
| `warmup` | bool | `true` | 测试前空转预热(不计速度,按并发数预热) |
|
||||
| `avoid_cache` | bool | `true` | 随机前缀避免缓存命中(每个并发流独立前缀) |
|
||||
| `interval` | number | `5` | 每次采样之间的间隔秒数,让接口空闲休息(0 表示不等待) |
|
||||
|
||||
**响应:** `{ "ok": true, "id": 9 }`
|
||||
|
||||
|
||||
@@ -130,6 +130,11 @@
|
||||
<div class="icon-input"><span class="icon">⏱</span><input id="gen-timeout" type="number" min="30" step="30" value="1800"></div>
|
||||
<div class="hint">连接测试与速度采样共用:超过此时长仍未收到数据才判定超时(默认 1800 秒 = 30 分钟)。测慢接口/超长上下文时建议调大,如 3600。</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>测试间隔(秒)<span class="hint-inline">每次采样之间让接口空闲</span></label>
|
||||
<div class="icon-input"><span class="icon">⏳</span><input id="gen-interval" type="number" min="0" step="1" value="5"></div>
|
||||
<div class="hint">每次采样完成后等待 N 秒再发下一次请求,让大模型接口空闲休息一下(默认 5 秒,设为 0 关闭)。</div>
|
||||
</div>
|
||||
<div class="btn-group">
|
||||
<button class="btn primary block" id="btn-start">▶ 开始测试</button>
|
||||
<button class="btn danger block" id="btn-cancel" disabled>■ 停止</button>
|
||||
|
||||
+9
-3
@@ -19,10 +19,11 @@ const STATUS_LABEL = {
|
||||
let currentTestId = null; // 正在跑的测试 id
|
||||
let pollTimer = null;
|
||||
let lastLogId = 0;
|
||||
let polling = false; // 防止轮询请求重叠:上一请求未返回前不再发,避免同批日志被追加两次
|
||||
let consoleLogs = []; // 当前测试已加载日志 [{id,level,msg,rel}]
|
||||
|
||||
// 上下文长度:chips 列表 + 启用集合(默认 512/2048/4096/8192/16384/32768/65536/131072)
|
||||
const DEFAULT_CONTEXT_LENGTHS = [512, 2048, 4096, 8192, 16384, 32768, 65536, 131072];
|
||||
// 上下文长度:chips 列表 + 启用集合(默认 4096/8192/16384/32768/65536/98304/131072)
|
||||
const DEFAULT_CONTEXT_LENGTHS = [4096, 8192, 16384, 32768, 65536, 98304, 131072];
|
||||
let contextLengths = [...DEFAULT_CONTEXT_LENGTHS];
|
||||
let contextLengthsActive = new Set(contextLengths);
|
||||
|
||||
@@ -70,6 +71,7 @@ function currentGen() {
|
||||
avoid_cache: $("#gen-avoid-cache").checked,
|
||||
warmup: $("#gen-warmup").checked,
|
||||
read_timeout: parseInt($("#gen-timeout").value) || 1800,
|
||||
interval: Math.max(0, parseInt($("#gen-interval").value) || 5),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -326,6 +328,7 @@ function appendLogs(logs) {
|
||||
html += `<div class="ln ${esc(l.level)}"><span class="ts">[${l.rel.toFixed(3)}s]</span> ${esc(l.msg)}</div>`;
|
||||
}
|
||||
box.insertAdjacentHTML("beforeend", html);
|
||||
consoleLogs.push(...logs); // 记录已加载日志,修正“日志条数”一直显示 0 条的问题
|
||||
$("#log-count").textContent = `${consoleLogs.length} 条`;
|
||||
if (atBottom) box.scrollTop = box.scrollHeight;
|
||||
}
|
||||
@@ -386,7 +389,8 @@ function startTest() {
|
||||
}
|
||||
|
||||
async function pollLogs() {
|
||||
if (currentTestId == null) return;
|
||||
if (currentTestId == null || polling) return;
|
||||
polling = true;
|
||||
try {
|
||||
const d = await api(`/api/tests/${currentTestId}/logs?after=${lastLogId}`);
|
||||
if (d.logs && d.logs.length) {
|
||||
@@ -399,6 +403,8 @@ async function pollLogs() {
|
||||
}
|
||||
} catch (e) {
|
||||
/* 网络抖动忽略 */
|
||||
} finally {
|
||||
polling = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,15 @@ class TestRunner(threading.Thread):
|
||||
def log(self, level, msg):
|
||||
db.add_log(self.test_id, level, msg)
|
||||
|
||||
def _sleep_interval(self, secs):
|
||||
"""测试间隔等待:期间可被用户停止,返回 False 表示已被取消"""
|
||||
deadline = time.time() + secs
|
||||
while time.time() < deadline:
|
||||
if self.should_stop():
|
||||
return False
|
||||
time.sleep(0.2)
|
||||
return not self.should_stop()
|
||||
|
||||
# ───────────────────────── 主流程 ─────────────────────────
|
||||
|
||||
def run(self):
|
||||
@@ -61,6 +70,11 @@ class TestRunner(threading.Thread):
|
||||
max_tokens = max(1, int(gen.get("max_tokens", 128))) # 解码输出长度
|
||||
avoid_cache = bool(gen.get("avoid_cache"))
|
||||
warmup = bool(gen.get("warmup", True)) # 测试前空转预热
|
||||
# 测试间隔(秒):每次采样之间让接口空闲休息,默认 5 秒,0 表示不等待
|
||||
try:
|
||||
interval = 5 if gen.get("interval") in (None, "") else max(0, float(gen.get("interval")))
|
||||
except (TypeError, ValueError):
|
||||
interval = 5
|
||||
|
||||
# 并发数列表(默认单流 [1];支持 2/4 及自定义,如 [1,2,4,8])
|
||||
raw_concs = gen.get("concurrency_levels") or []
|
||||
@@ -73,10 +87,10 @@ class TestRunner(threading.Thread):
|
||||
if name:
|
||||
self.log("INFO", "测试名称(主题): %s" % name)
|
||||
self.log("INFO", "提供商: %s | 模型: %s" % (lp.PROVIDER_LABELS.get(provider, provider), model))
|
||||
self.log("INFO", "上下文长度: %s tokens | 生成长度: %d tokens | 并发数: %s | 每个组合采样: %d 次 | 预热: %s | 避免缓存: %s"
|
||||
self.log("INFO", "上下文长度: %s tokens | 生成长度: %d tokens | 并发数: %s | 每个组合采样: %d 次 | 预热: %s | 避免缓存: %s | 测试间隔: %g 秒"
|
||||
% (" / ".join(str(x) for x in lengths), max_tokens,
|
||||
" / ".join(str(x) for x in concurrency_levels), n,
|
||||
"开" if warmup else "关", "开" if avoid_cache else "关"))
|
||||
"开" if warmup else "关", "开" if avoid_cache else "关", interval))
|
||||
to = self.gen.get("read_timeout") or "默认(1800)"
|
||||
self.log("INFO", "请求超时: 连接 %s s | 等待首字/预处理 %s s(接口慢可在左侧调大)"
|
||||
% (self.gen.get("connect_timeout") or 60, to))
|
||||
@@ -86,6 +100,8 @@ class TestRunner(threading.Thread):
|
||||
self.log("INFO", "校准完成: %.3f tok/字符(%.2f 字符/token)" % (ratio, 1.0 / ratio))
|
||||
|
||||
run_seq = 0
|
||||
sample_done = 0 # 已完成的采样数,用于控制测试间隔(首个采样不等待)
|
||||
last_conc = None # 只在实际切换并发档时打印一次表头,避免同一并发数重复刷屏
|
||||
for L in lengths:
|
||||
if self.should_stop():
|
||||
raise StopRequested()
|
||||
@@ -94,13 +110,20 @@ class TestRunner(threading.Thread):
|
||||
for C in concurrency_levels:
|
||||
if self.should_stop():
|
||||
raise StopRequested()
|
||||
self.log("INFO", "══ 并发数 %d(同时 %d 个流)══" % (C, C))
|
||||
if C != last_conc:
|
||||
self.log("INFO", "══ 并发数 %d(同时 %d 个流)══" % (C, C))
|
||||
last_conc = C
|
||||
if warmup:
|
||||
self._warmup(base_prompt, C)
|
||||
for i in range(1, n + 1):
|
||||
if self.should_stop():
|
||||
raise StopRequested()
|
||||
if sample_done > 0 and interval > 0:
|
||||
self.log("INFO", "⏳ 接口空闲休息 %g 秒后继续下一采样..." % interval)
|
||||
if not self._sleep_interval(interval):
|
||||
raise StopRequested()
|
||||
run_seq += 1
|
||||
sample_done += 1
|
||||
self.log("INFO", "── [%d tok · 并发%d] 采样 %d/%d 开始 ──" % (L, C, i, n))
|
||||
try:
|
||||
m = self._run_sample(C, base_prompt, max_tokens, avoid_cache)
|
||||
|
||||
Reference in New Issue
Block a user