tds-core 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tds/__init__.py +34 -0
- tds/api.py +548 -0
- tds/benchmark.py +456 -0
- tds/chat.py +709 -0
- tds/config.py +54 -0
- tds/core/__init__.py +17 -0
- tds/core/classifier.py +110 -0
- tds/core/memory.py +37 -0
- tds/core/metrics.py +49 -0
- tds/core/planner.py +104 -0
- tds/core/scheduler.py +500 -0
- tds/core/storage.py +79 -0
- tds/core/trace.py +178 -0
- tds/core/yang_yin_strategy.py +334 -0
- tds/kv/__init__.py +104 -0
- tds/kv/domain_classifier.py +132 -0
- tds/kv/domain_predictor.py +98 -0
- tds/kv/host.py +111 -0
- tds/kv/observer.py +354 -0
- tds/kv/page_manager.py +1646 -0
- tds/kv/taiji_policy.py +284 -0
- tds/kv/taiji_zimei.py +150 -0
- tds/registry/__init__.py +85 -0
- tds/registry/catalog.py +233 -0
- tds/registry/cli.py +175 -0
- tds/registry/data/builtin_catalog.json +63 -0
- tds/registry/downloader.py +270 -0
- tds/registry/errors.py +24 -0
- tds/registry/estimate.py +152 -0
- tds/registry/hardware.py +221 -0
- tds/registry/planner.py +146 -0
- tds/registry/remote.py +140 -0
- tds_core-0.2.0.dist-info/METADATA +128 -0
- tds_core-0.2.0.dist-info/RECORD +37 -0
- tds_core-0.2.0.dist-info/WHEEL +5 -0
- tds_core-0.2.0.dist-info/licenses/LICENSE +13 -0
- tds_core-0.2.0.dist-info/top_level.txt +1 -0
tds/__init__.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""太极调度层 TDS —— 大模型权重-显存动态调度中间层。
|
|
2
|
+
|
|
3
|
+
SDK 入口
|
|
4
|
+
--------
|
|
5
|
+
from tds import TDSRuntime
|
|
6
|
+
|
|
7
|
+
runtime = TDSRuntime()
|
|
8
|
+
runtime.register_adapter(MyAdapter())
|
|
9
|
+
runtime.attach() # 挂载
|
|
10
|
+
result = runtime.run_task("用户输入")
|
|
11
|
+
runtime.detach() # 卸载
|
|
12
|
+
"""
|
|
13
|
+
__version__ = "0.1.0"
|
|
14
|
+
__author__ = "BY LAOMENG 网络工作室"
|
|
15
|
+
|
|
16
|
+
from .config import TDSConfig, BAGUA_DOMAINS
|
|
17
|
+
from .api import (
|
|
18
|
+
TDSRuntime,
|
|
19
|
+
RuntimeState,
|
|
20
|
+
AttachResult,
|
|
21
|
+
DetachResult,
|
|
22
|
+
BenchmarkResult,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
__all__ = [
|
|
26
|
+
"TDSConfig",
|
|
27
|
+
"BAGUA_DOMAINS",
|
|
28
|
+
"__version__",
|
|
29
|
+
"TDSRuntime",
|
|
30
|
+
"RuntimeState",
|
|
31
|
+
"AttachResult",
|
|
32
|
+
"DetachResult",
|
|
33
|
+
"BenchmarkResult",
|
|
34
|
+
]
|
tds/api.py
ADDED
|
@@ -0,0 +1,548 @@
|
|
|
1
|
+
"""SDK 挂载/卸载入口层 —— 宿主无关的调度中间层 API。
|
|
2
|
+
|
|
3
|
+
定位
|
|
4
|
+
----
|
|
5
|
+
本模块是 TDS SDK 面向宿主的唯一入口。宿主通过 TDSRuntime 完成三件事:
|
|
6
|
+
|
|
7
|
+
1. 注册适配器(扫描/加载/卸载/显存快照);
|
|
8
|
+
2. 挂载(attach)—— 将宿主当前上下文(模型、显存状态等)接入调度链路;
|
|
9
|
+
3. 卸载(detach)—— 断开调度链路,释放 TDS 占用的资源,宿主继续独立运行。
|
|
10
|
+
|
|
11
|
+
挂载/卸载的设计要点:
|
|
12
|
+
- **唯一入口**:所有操作通过 TDSRuntime 一个对象完成,无全局 state。
|
|
13
|
+
- **幂等安全**:attach/detach 可多次调用,内部做状态保护。
|
|
14
|
+
- **宿主解耦**:SDK 不依赖任何宿主类名硬编码,宿主只需实现 BaseAdapter 接口。
|
|
15
|
+
- **热插拔**:挂载/卸载不重启宿主,调度状态(分类器、计划器、存储层)保留,
|
|
16
|
+
仅断开外部适配器的调度驱动。
|
|
17
|
+
|
|
18
|
+
设计哲学
|
|
19
|
+
--------
|
|
20
|
+
挂载(attach)= 让 TDS 接管调度决策,adapter 的 load/unload/prefetch 被调度器驱动;
|
|
21
|
+
卸载(detach)= 让宿主恢复自主调度,adapter 直接服务于宿主自身框架,不再受 TDS 驱动。
|
|
22
|
+
|
|
23
|
+
宿主对接收敛:宿主把自身的适配器实现注册到 TDSRuntime,SDK 内部不引用
|
|
24
|
+
宿主任何类名、模块名或版本信息,仅通过 BaseAdapter 抽象层交互。
|
|
25
|
+
"""
|
|
26
|
+
from __future__ import annotations
|
|
27
|
+
|
|
28
|
+
import threading
|
|
29
|
+
import time
|
|
30
|
+
from dataclasses import dataclass, field
|
|
31
|
+
from typing import Any, Dict, List, Optional, Sequence
|
|
32
|
+
|
|
33
|
+
from .core.scheduler import Scheduler
|
|
34
|
+
from .core.memory import VramState
|
|
35
|
+
from .config import TDSConfig, BAGUA_DOMAINS
|
|
36
|
+
from .core.classifier import BaguaClassifier
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# --------------------------------------------------------------------------
|
|
40
|
+
# 运行时状态
|
|
41
|
+
# --------------------------------------------------------------------------
|
|
42
|
+
|
|
43
|
+
class RuntimeState:
|
|
44
|
+
"""SDK 运行时状态机。"""
|
|
45
|
+
INIT = "init" # 初始化,未挂载
|
|
46
|
+
ATTACHED = "attached" # 已挂载,调度器接管
|
|
47
|
+
DETACHED = "detached" # 已卸载,适配器独立
|
|
48
|
+
|
|
49
|
+
ALL = [INIT, ATTACHED, DETACHED]
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@dataclass
|
|
53
|
+
class AttachResult:
|
|
54
|
+
"""attach() 返回结果。"""
|
|
55
|
+
success: bool = True
|
|
56
|
+
adapter_name: str = ""
|
|
57
|
+
vram_before: Optional[Dict[str, Any]] = None
|
|
58
|
+
vram_after: Optional[Dict[str, Any]] = None
|
|
59
|
+
message: str = ""
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@dataclass
|
|
63
|
+
class DetachResult:
|
|
64
|
+
"""detach() 返回结果。"""
|
|
65
|
+
success: bool = True
|
|
66
|
+
adapters_detached: int = 0
|
|
67
|
+
message: str = ""
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@dataclass
|
|
71
|
+
class BenchmarkResult:
|
|
72
|
+
"""基准测试返回结果。"""
|
|
73
|
+
name: str = ""
|
|
74
|
+
mode: str = "" # single | concurrent
|
|
75
|
+
duration_ms: float = 0.0
|
|
76
|
+
tasks_completed: int = 0
|
|
77
|
+
tasks_failed: int = 0
|
|
78
|
+
avg_latency_ms: float = 0.0
|
|
79
|
+
avg_tps: float = 0.0
|
|
80
|
+
peak_vram_mb: float = 0.0
|
|
81
|
+
min_vram_mb: float = 0.0
|
|
82
|
+
details: List[Dict[str, Any]] = field(default_factory=list)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
# --------------------------------------------------------------------------
|
|
86
|
+
# TDSRuntime — 唯一入口
|
|
87
|
+
# --------------------------------------------------------------------------
|
|
88
|
+
|
|
89
|
+
class TDSRuntime:
|
|
90
|
+
"""SDK 挂载/卸载运行时。
|
|
91
|
+
|
|
92
|
+
使用方式
|
|
93
|
+
--------
|
|
94
|
+
1. 创建:runtime = TDSRuntime()
|
|
95
|
+
2. 注册适配器:runtime.register_adapter(MyAdapter())
|
|
96
|
+
3. 挂载:runtime.attach()
|
|
97
|
+
4. 执行调度:result = runtime.run_task("用户输入文本")
|
|
98
|
+
5. 卸载:runtime.detach()
|
|
99
|
+
|
|
100
|
+
参数
|
|
101
|
+
----
|
|
102
|
+
config: TDSConfig, 可选,SDK 配置,默认 TDSConfig 实例
|
|
103
|
+
"""
|
|
104
|
+
|
|
105
|
+
def __init__(self, config: Optional[TDSConfig] = None):
|
|
106
|
+
self.cfg = config or TDSConfig()
|
|
107
|
+
self.scheduler: Optional[Scheduler] = None
|
|
108
|
+
self._adapters: Dict[str, Any] = {}
|
|
109
|
+
self._state = RuntimeState.INIT
|
|
110
|
+
self._lock = threading.RLock()
|
|
111
|
+
# 显存账本:最近一次调度后的 vram 快照(成功/异常路径均回填,问题6)
|
|
112
|
+
self.last_vram: Optional[Dict[str, Any]] = None
|
|
113
|
+
|
|
114
|
+
@property
|
|
115
|
+
def state(self) -> str:
|
|
116
|
+
return self._state
|
|
117
|
+
|
|
118
|
+
def is_attached(self) -> bool:
|
|
119
|
+
return self._state == RuntimeState.ATTACHED
|
|
120
|
+
|
|
121
|
+
def is_detached(self) -> bool:
|
|
122
|
+
return self._state == RuntimeState.DETACHED
|
|
123
|
+
|
|
124
|
+
# ---- 适配器管理 ----
|
|
125
|
+
|
|
126
|
+
def register_adapter(self, adapter: Any, name: str = "default") -> bool:
|
|
127
|
+
"""注册宿主适配器。
|
|
128
|
+
|
|
129
|
+
参数
|
|
130
|
+
----
|
|
131
|
+
adapter: 实现 BaseAdapter 接口的适配器实例
|
|
132
|
+
name: 适配器名称(支持多适配器注册)
|
|
133
|
+
|
|
134
|
+
返回
|
|
135
|
+
----
|
|
136
|
+
bool: 注册成功为 True
|
|
137
|
+
|
|
138
|
+
注意
|
|
139
|
+
----
|
|
140
|
+
- 适配器是宿主的实现,SDK 内部不引用宿主类名
|
|
141
|
+
- 同一 name 注册会被覆盖
|
|
142
|
+
"""
|
|
143
|
+
if not hasattr(adapter, "scan_usage"):
|
|
144
|
+
raise ValueError("适配器必须实现 scan_usage 方法")
|
|
145
|
+
if not hasattr(adapter, "load"):
|
|
146
|
+
raise ValueError("适配器必须实现 load 方法")
|
|
147
|
+
if not hasattr(adapter, "unload"):
|
|
148
|
+
raise ValueError("适配器必须实现 unload 方法")
|
|
149
|
+
if not hasattr(adapter, "prefetch"):
|
|
150
|
+
raise ValueError("适配器必须实现 prefetch 方法")
|
|
151
|
+
if not hasattr(adapter, "vram_state"):
|
|
152
|
+
raise ValueError("适配器必须实现 vram_state 方法")
|
|
153
|
+
|
|
154
|
+
with self._lock:
|
|
155
|
+
self._adapters[name] = adapter
|
|
156
|
+
|
|
157
|
+
return True
|
|
158
|
+
|
|
159
|
+
def get_adapter(self, name: str = "default") -> Optional[Any]:
|
|
160
|
+
"""获取已注册的适配器。"""
|
|
161
|
+
return self._adapters.get(name)
|
|
162
|
+
|
|
163
|
+
def list_adapters(self) -> List[str]:
|
|
164
|
+
"""列出所有已注册的适配器名称。"""
|
|
165
|
+
return list(self._adapters.keys())
|
|
166
|
+
|
|
167
|
+
# ---- 挂载 / 卸载 ----
|
|
168
|
+
|
|
169
|
+
def attach(self, adapter_name: str = "default") -> AttachResult:
|
|
170
|
+
"""挂载 SDK —— 将适配器接入调度链路。
|
|
171
|
+
|
|
172
|
+
挂载后,调度器会接管 adapter 的 load/unload/prefetch 调用,
|
|
173
|
+
宿主无需直接调用适配器方法。
|
|
174
|
+
|
|
175
|
+
参数
|
|
176
|
+
----
|
|
177
|
+
adapter_name: 要挂载的适配器名称
|
|
178
|
+
|
|
179
|
+
返回
|
|
180
|
+
----
|
|
181
|
+
AttachResult: 挂载结果
|
|
182
|
+
|
|
183
|
+
异常
|
|
184
|
+
----
|
|
185
|
+
RuntimeError: 适配器未注册或 SDK 已挂载
|
|
186
|
+
"""
|
|
187
|
+
with self._lock:
|
|
188
|
+
if adapter_name not in self._adapters:
|
|
189
|
+
raise RuntimeError(f"适配器未注册: {adapter_name}")
|
|
190
|
+
|
|
191
|
+
if self._state == RuntimeState.ATTACHED:
|
|
192
|
+
return AttachResult(
|
|
193
|
+
success=False,
|
|
194
|
+
message=f"SDK 已挂载,无需重复挂载"
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
adapter = self._adapters[adapter_name]
|
|
198
|
+
vram_before = self._capture_vram(adapter)
|
|
199
|
+
|
|
200
|
+
# 初始化调度器并绑定适配器
|
|
201
|
+
self.scheduler = Scheduler(config=self.cfg, adapter=adapter)
|
|
202
|
+
|
|
203
|
+
# 标记挂载完成
|
|
204
|
+
self._state = RuntimeState.ATTACHED
|
|
205
|
+
|
|
206
|
+
vram_after = self._capture_vram(adapter)
|
|
207
|
+
|
|
208
|
+
return AttachResult(
|
|
209
|
+
success=True,
|
|
210
|
+
adapter_name=adapter_name,
|
|
211
|
+
vram_before=vram_before,
|
|
212
|
+
vram_after=vram_after,
|
|
213
|
+
message=f"SDK 挂载成功,adapter={adapter_name}"
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
def detach(self) -> DetachResult:
|
|
217
|
+
"""卸载 SDK —— 断开调度链路,释放 TDS 资源。
|
|
218
|
+
|
|
219
|
+
卸载后,适配器恢复到独立运行状态,宿主框架直接管理调度。
|
|
220
|
+
TDS 内部状态(分类器、计划器)保留,可随时重新挂载。
|
|
221
|
+
|
|
222
|
+
返回
|
|
223
|
+
----
|
|
224
|
+
DetachResult: 卸载结果
|
|
225
|
+
|
|
226
|
+
异常
|
|
227
|
+
----
|
|
228
|
+
RuntimeError: SDK 未挂载
|
|
229
|
+
"""
|
|
230
|
+
with self._lock:
|
|
231
|
+
if self._state == RuntimeState.DETACHED:
|
|
232
|
+
return DetachResult(
|
|
233
|
+
success=False,
|
|
234
|
+
message="SDK 已卸载,无需重复卸载"
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
if self._state != RuntimeState.ATTACHED or self.scheduler is None:
|
|
238
|
+
raise RuntimeError("SDK 未挂载,无法卸载")
|
|
239
|
+
|
|
240
|
+
# 获取调度器关联的适配器
|
|
241
|
+
scheduler_adapter = self.scheduler.adapter
|
|
242
|
+
scheduler_adapter_name = None
|
|
243
|
+
for name, adapter in self._adapters.items():
|
|
244
|
+
if adapter is scheduler_adapter:
|
|
245
|
+
scheduler_adapter_name = name
|
|
246
|
+
break
|
|
247
|
+
|
|
248
|
+
# 断开调度器(adapter 仍保留在 _adapters 中供后续重新挂载)
|
|
249
|
+
self.scheduler.adapter = None
|
|
250
|
+
self.scheduler = None
|
|
251
|
+
self._state = RuntimeState.DETACHED
|
|
252
|
+
|
|
253
|
+
return DetachResult(
|
|
254
|
+
success=True,
|
|
255
|
+
adapters_detached=1,
|
|
256
|
+
message=f"SDK 卸载成功"
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
# ---- 调度入口 ----
|
|
260
|
+
|
|
261
|
+
def run_task(self, task_desc: str, graph=None, blocks=None) -> Dict[str, Any]:
|
|
262
|
+
"""执行调度任务。
|
|
263
|
+
|
|
264
|
+
挂载后调用,由调度器自动驱动适配器完成 load/unload/prefetch。
|
|
265
|
+
|
|
266
|
+
参数
|
|
267
|
+
----
|
|
268
|
+
task_desc: 任务描述(文本,用于语义分类)
|
|
269
|
+
graph: 执行图(可选)。传入时调度器先经 adapter.scan_usage(graph)
|
|
270
|
+
扫描出块序列并登记,再据此生成调度计划 —— 这是 scan 结果
|
|
271
|
+
真实驱动 adapter.load/unload 的闭环接线。
|
|
272
|
+
blocks: 可选,已构造的 Block 列表(与 graph 二选一,优先级更高)
|
|
273
|
+
|
|
274
|
+
返回
|
|
275
|
+
----
|
|
276
|
+
dict: 调度结果,包含 step、primary、scores、pressure、plan、metrics 等
|
|
277
|
+
|
|
278
|
+
异常
|
|
279
|
+
----
|
|
280
|
+
RuntimeError: SDK 未挂载
|
|
281
|
+
"""
|
|
282
|
+
with self._lock:
|
|
283
|
+
if self._state != RuntimeState.ATTACHED or self.scheduler is None:
|
|
284
|
+
raise RuntimeError("SDK 未挂载,请先调用 attach()")
|
|
285
|
+
|
|
286
|
+
try:
|
|
287
|
+
result = self.scheduler.run_task(task_desc, blocks=blocks, graph=graph)
|
|
288
|
+
except Exception:
|
|
289
|
+
# 问题6:异常路径同样回填显存账本快照,避免账本停留在
|
|
290
|
+
# 上一次成功值(外部 catch 后据 last_vram 判断水位会失真)。
|
|
291
|
+
self.last_vram = self._capture_vram(self.scheduler.adapter)
|
|
292
|
+
raise
|
|
293
|
+
self.last_vram = self._capture_vram(self.scheduler.adapter)
|
|
294
|
+
return result
|
|
295
|
+
|
|
296
|
+
# ---- 性能基准测试 ----
|
|
297
|
+
|
|
298
|
+
def run_benchmark(self, test_name: str = "default", repeat: int = 3,
|
|
299
|
+
concurrent_tasks: int = 1) -> BenchmarkResult:
|
|
300
|
+
"""运行性能基准测试。
|
|
301
|
+
|
|
302
|
+
测试类型:
|
|
303
|
+
- single: 单流测试,逐个任务串行执行
|
|
304
|
+
- concurrent: 并发测试,同时执行 N 个任务
|
|
305
|
+
|
|
306
|
+
参数
|
|
307
|
+
----
|
|
308
|
+
test_name: 测试名称
|
|
309
|
+
repeat: 测试重复次数
|
|
310
|
+
concurrent_tasks: 并发任务数(concurrent 模式下)
|
|
311
|
+
|
|
312
|
+
返回
|
|
313
|
+
----
|
|
314
|
+
BenchmarkResult: 测试结果
|
|
315
|
+
|
|
316
|
+
异常
|
|
317
|
+
----
|
|
318
|
+
RuntimeError: SDK 未挂载
|
|
319
|
+
"""
|
|
320
|
+
with self._lock:
|
|
321
|
+
if self._state != RuntimeState.ATTACHED or self.scheduler is None:
|
|
322
|
+
raise RuntimeError("SDK 未挂载,请先调用 attach()")
|
|
323
|
+
|
|
324
|
+
adapter = self.scheduler.adapter
|
|
325
|
+
results: List[Dict[str, Any]] = []
|
|
326
|
+
durations = []
|
|
327
|
+
completed = 0
|
|
328
|
+
failed = 0
|
|
329
|
+
peak_vram = 0.0
|
|
330
|
+
min_vram = float('inf')
|
|
331
|
+
|
|
332
|
+
if concurrent_tasks > 1:
|
|
333
|
+
# 并发模式
|
|
334
|
+
for i in range(repeat):
|
|
335
|
+
start_time = time.perf_counter()
|
|
336
|
+
concurrent_results = []
|
|
337
|
+
|
|
338
|
+
for j in range(concurrent_tasks):
|
|
339
|
+
try:
|
|
340
|
+
# 每个并发任务使用不同的描述以模拟真实负载
|
|
341
|
+
task_desc = f"并发任务 {j+1}/{concurrent_tasks} 在测试 {i+1}/{repeat}"
|
|
342
|
+
result = self.scheduler.run_task(task_desc)
|
|
343
|
+
concurrent_results.append(result)
|
|
344
|
+
except Exception as e:
|
|
345
|
+
concurrent_results.append({"error": str(e)})
|
|
346
|
+
failed += 1
|
|
347
|
+
|
|
348
|
+
# 记录显存
|
|
349
|
+
try:
|
|
350
|
+
vram = adapter.vram_state()
|
|
351
|
+
if vram:
|
|
352
|
+
usage = vram.used_mb
|
|
353
|
+
peak_vram = max(peak_vram, usage)
|
|
354
|
+
min_vram = min(min_vram, usage)
|
|
355
|
+
except:
|
|
356
|
+
pass
|
|
357
|
+
|
|
358
|
+
end_time = time.perf_counter()
|
|
359
|
+
duration = (end_time - start_time) * 1000.0
|
|
360
|
+
durations.append(duration)
|
|
361
|
+
completed += len(concurrent_results)
|
|
362
|
+
|
|
363
|
+
results.append({
|
|
364
|
+
"repeat": i + 1,
|
|
365
|
+
"concurrent_tasks": concurrent_tasks,
|
|
366
|
+
"duration_ms": duration,
|
|
367
|
+
"completed": len(concurrent_results),
|
|
368
|
+
"failed": sum(1 for r in concurrent_results if "error" in r)
|
|
369
|
+
})
|
|
370
|
+
else:
|
|
371
|
+
# 单流模式
|
|
372
|
+
for i in range(repeat):
|
|
373
|
+
start_time = time.perf_counter()
|
|
374
|
+
try:
|
|
375
|
+
result = self.scheduler.run_task(f"基准测试单流任务 {i+1}/{repeat}")
|
|
376
|
+
completed += 1
|
|
377
|
+
except Exception as e:
|
|
378
|
+
failed += 1
|
|
379
|
+
results.append({"error": str(e)})
|
|
380
|
+
|
|
381
|
+
end_time = time.perf_counter()
|
|
382
|
+
duration = (end_time - start_time) * 1000.0
|
|
383
|
+
durations.append(duration)
|
|
384
|
+
|
|
385
|
+
# 记录显存
|
|
386
|
+
try:
|
|
387
|
+
vram = adapter.vram_state()
|
|
388
|
+
if vram:
|
|
389
|
+
usage = vram.used_mb
|
|
390
|
+
peak_vram = max(peak_vram, usage)
|
|
391
|
+
min_vram = min(min_vram, usage)
|
|
392
|
+
except:
|
|
393
|
+
pass
|
|
394
|
+
|
|
395
|
+
results.append({
|
|
396
|
+
"repeat": i + 1,
|
|
397
|
+
"duration_ms": duration,
|
|
398
|
+
"completed": 1,
|
|
399
|
+
"failed": 0 if "error" not in str(result) else 1
|
|
400
|
+
})
|
|
401
|
+
|
|
402
|
+
avg_duration = sum(durations) / len(durations) if durations else 0.0
|
|
403
|
+
avg_tps = completed / (sum(durations) / 1000.0) if durations and sum(durations) > 0 else 0.0
|
|
404
|
+
|
|
405
|
+
if min_vram == float('inf'):
|
|
406
|
+
min_vram = 0.0
|
|
407
|
+
|
|
408
|
+
return BenchmarkResult(
|
|
409
|
+
name=test_name,
|
|
410
|
+
mode="concurrent" if concurrent_tasks > 1 else "single",
|
|
411
|
+
duration_ms=sum(durations),
|
|
412
|
+
tasks_completed=completed,
|
|
413
|
+
tasks_failed=failed,
|
|
414
|
+
avg_latency_ms=avg_duration,
|
|
415
|
+
avg_tps=avg_tps,
|
|
416
|
+
peak_vram_mb=peak_vram,
|
|
417
|
+
min_vram_mb=min_vram,
|
|
418
|
+
details=results
|
|
419
|
+
)
|
|
420
|
+
|
|
421
|
+
def attach_detach_benchmark(self, test_name: str = "attach_detach",
|
|
422
|
+
iterations: int = 5) -> BenchmarkResult:
|
|
423
|
+
"""挂载/卸载性能基准测试。
|
|
424
|
+
|
|
425
|
+
测试挂载/卸载操作的开销,验证热插拔性能。
|
|
426
|
+
|
|
427
|
+
参数
|
|
428
|
+
----
|
|
429
|
+
test_name: 测试名称
|
|
430
|
+
iterations: 测试迭代次数
|
|
431
|
+
|
|
432
|
+
返回
|
|
433
|
+
----
|
|
434
|
+
BenchmarkResult: 测试结果
|
|
435
|
+
"""
|
|
436
|
+
if self._state != RuntimeState.INIT:
|
|
437
|
+
raise RuntimeError("SDK 必须处于 INIT 状态才能运行挂载/卸载测试")
|
|
438
|
+
|
|
439
|
+
if not self._adapters:
|
|
440
|
+
raise RuntimeError("没有注册适配器,无法运行挂载/卸载测试")
|
|
441
|
+
|
|
442
|
+
adapter_name = list(self._adapters.keys())[0]
|
|
443
|
+
results: List[Dict[str, Any]] = []
|
|
444
|
+
attach_durations = []
|
|
445
|
+
detach_durations = []
|
|
446
|
+
|
|
447
|
+
for i in range(iterations):
|
|
448
|
+
# 挂载
|
|
449
|
+
start_attach = time.perf_counter()
|
|
450
|
+
try:
|
|
451
|
+
attach_result = self.attach(adapter_name)
|
|
452
|
+
attach_duration = (time.perf_counter() - start_attach) * 1000.0
|
|
453
|
+
attach_durations.append(attach_duration)
|
|
454
|
+
except Exception as e:
|
|
455
|
+
attach_duration = (time.perf_counter() - start_attach) * 1000.0
|
|
456
|
+
attach_durations.append(attach_duration)
|
|
457
|
+
results.append({
|
|
458
|
+
"iteration": i + 1,
|
|
459
|
+
"phase": "attach",
|
|
460
|
+
"duration_ms": attach_duration,
|
|
461
|
+
"success": False,
|
|
462
|
+
"error": str(e)
|
|
463
|
+
})
|
|
464
|
+
continue
|
|
465
|
+
|
|
466
|
+
# 卸载
|
|
467
|
+
start_detach = time.perf_counter()
|
|
468
|
+
try:
|
|
469
|
+
detach_result = self.detach()
|
|
470
|
+
detach_duration = (time.perf_counter() - start_detach) * 1000.0
|
|
471
|
+
detach_durations.append(detach_duration)
|
|
472
|
+
except Exception as e:
|
|
473
|
+
detach_duration = (time.perf_counter() - start_detach) * 1000.0
|
|
474
|
+
detach_durations.append(detach_duration)
|
|
475
|
+
results.append({
|
|
476
|
+
"iteration": i + 1,
|
|
477
|
+
"phase": "detach",
|
|
478
|
+
"duration_ms": detach_duration,
|
|
479
|
+
"success": False,
|
|
480
|
+
"error": str(e)
|
|
481
|
+
})
|
|
482
|
+
|
|
483
|
+
results.append({
|
|
484
|
+
"iteration": i + 1,
|
|
485
|
+
"phase": "complete",
|
|
486
|
+
"attach_duration_ms": attach_durations[-1],
|
|
487
|
+
"detach_duration_ms": detach_durations[-1] if detach_durations else 0,
|
|
488
|
+
"success": True
|
|
489
|
+
})
|
|
490
|
+
|
|
491
|
+
avg_attach = sum(attach_durations) / len(attach_durations) if attach_durations else 0.0
|
|
492
|
+
avg_detach = sum(detach_durations) / len(detach_durations) if detach_durations else 0.0
|
|
493
|
+
total_duration = sum(attach_durations) + sum(detach_durations)
|
|
494
|
+
|
|
495
|
+
return BenchmarkResult(
|
|
496
|
+
name=test_name,
|
|
497
|
+
mode="attach_detach",
|
|
498
|
+
duration_ms=total_duration,
|
|
499
|
+
tasks_completed=iterations,
|
|
500
|
+
tasks_failed=0,
|
|
501
|
+
avg_latency_ms=(avg_attach + avg_detach),
|
|
502
|
+
avg_tps=iterations / (total_duration / 1000.0) if total_duration > 0 else 0.0,
|
|
503
|
+
peak_vram_mb=0.0,
|
|
504
|
+
min_vram_mb=0.0,
|
|
505
|
+
details=results,
|
|
506
|
+
# 挂载/卸载测试额外字段
|
|
507
|
+
avg_attach_ms=avg_attach,
|
|
508
|
+
avg_detach_ms=avg_detach
|
|
509
|
+
)
|
|
510
|
+
|
|
511
|
+
# ---- 内部方法 ----
|
|
512
|
+
|
|
513
|
+
def _capture_vram(self, adapter: Any) -> Optional[Dict[str, Any]]:
|
|
514
|
+
"""捕获当前显存状态。"""
|
|
515
|
+
try:
|
|
516
|
+
state = adapter.vram_state()
|
|
517
|
+
if state:
|
|
518
|
+
return {
|
|
519
|
+
"total_mb": state.total_mb,
|
|
520
|
+
"used_mb": state.used_mb,
|
|
521
|
+
"free_mb": state.free_mb,
|
|
522
|
+
"usage_mb": state.used_mb,
|
|
523
|
+
"usage_ratio": state.usage_ratio
|
|
524
|
+
}
|
|
525
|
+
except Exception:
|
|
526
|
+
pass
|
|
527
|
+
return None
|
|
528
|
+
|
|
529
|
+
def reset(self):
|
|
530
|
+
"""重置 SDK 运行时到初始状态。"""
|
|
531
|
+
with self._lock:
|
|
532
|
+
self.scheduler = None
|
|
533
|
+
self._adapters.clear()
|
|
534
|
+
self._state = RuntimeState.INIT
|
|
535
|
+
|
|
536
|
+
def __repr__(self):
|
|
537
|
+
return (
|
|
538
|
+
f"TDSRuntime(state={self._state}, "
|
|
539
|
+
f"adapters={len(self._adapters)}, "
|
|
540
|
+
f"version={__version__})"
|
|
541
|
+
)
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
# ---- 版本信息 ----
|
|
545
|
+
__version__ = "0.1.0"
|
|
546
|
+
__author__ = "BY LAOMENG 网络工作室"
|
|
547
|
+
__all__ = ["TDSRuntime", "RuntimeState", "AttachResult", "DetachResult",
|
|
548
|
+
"BenchmarkResult"]
|