diff --git a/API.md b/API.md index 53c7374..c797f2e 100644 --- a/API.md +++ b/API.md @@ -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 }` diff --git a/static/index.html b/static/index.html index 86d6b82..0a3a5a8 100644 --- a/static/index.html +++ b/static/index.html @@ -130,6 +130,11 @@
连接测试与速度采样共用:超过此时长仍未收到数据才判定超时(默认 1800 秒 = 30 分钟)。测慢接口/超长上下文时建议调大,如 3600。
+
+ +
+
每次采样完成后等待 N 秒再发下一次请求,让大模型接口空闲休息一下(默认 5 秒,设为 0 关闭)。
+
diff --git a/static/js/app.js b/static/js/app.js index 8e4b1a7..beb7696 100644 --- a/static/js/app.js +++ b/static/js/app.js @@ -71,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), }; } diff --git a/tester.py b/tester.py index 82bb06c..0a66232 100644 --- a/tester.py +++ b/tester.py @@ -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,7 @@ 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(): @@ -103,7 +118,12 @@ class TestRunner(threading.Thread): 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)