snowllm 0.1.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.
- snowllm/__init__.py +8 -0
- snowllm/_capi.py +246 -0
- snowllm/_version.py +4 -0
- snowllm/async_engine.py +170 -0
- snowllm/block_manager.py +68 -0
- snowllm/cli.py +207 -0
- snowllm/engine.py +353 -0
- snowllm/forward_context.py +103 -0
- snowllm/generation.py +233 -0
- snowllm/geometry.py +140 -0
- snowllm/layers.py +160 -0
- snowllm/loader.py +138 -0
- snowllm/model.py +347 -0
- snowllm/models/__init__.py +24 -0
- snowllm/models/qwen3_5/__init__.py +16 -0
- snowllm/models/qwen3_5/qwen3_5.py +89 -0
- snowllm/models/qwen3_5/qwen3_5_mtp.py +41 -0
- snowllm/models/qwen3_5/qwen3_5_weights.py +229 -0
- snowllm/multimodal.py +59 -0
- snowllm/ops/__init__.py +113 -0
- snowllm/ops/_common.py +79 -0
- snowllm/ops/attention_full.py +229 -0
- snowllm/ops/attention_linear.py +74 -0
- snowllm/ops/cache.py +49 -0
- snowllm/ops/embedding.py +34 -0
- snowllm/ops/gemm.py +25 -0
- snowllm/ops/moe.py +106 -0
- snowllm/ops/mtp.py +37 -0
- snowllm/ops/norm.py +48 -0
- snowllm/ops/rope.py +22 -0
- snowllm/ops/sampling.py +40 -0
- snowllm/ops/vision.py +63 -0
- snowllm/protocol.py +52 -0
- snowllm/reader.py +167 -0
- snowllm/request.py +73 -0
- snowllm/responses.py +305 -0
- snowllm/sampler.py +86 -0
- snowllm/server.py +197 -0
- snowllm/spec_decode.py +162 -0
- snowllm/state.py +58 -0
- snowllm/tool_parser.py +103 -0
- snowllm/trace.py +24 -0
- snowllm/vision.py +315 -0
- snowllm-0.1.0.dist-info/METADATA +123 -0
- snowllm-0.1.0.dist-info/RECORD +49 -0
- snowllm-0.1.0.dist-info/WHEEL +5 -0
- snowllm-0.1.0.dist-info/entry_points.txt +2 -0
- snowllm-0.1.0.dist-info/licenses/LICENSE +202 -0
- snowllm-0.1.0.dist-info/top_level.txt +1 -0
snowllm/__init__.py
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
# SPDX-FileCopyrightText: Copyright contributors to the SnowLLM project
|
|
3
|
+
|
|
4
|
+
from ._capi import ABI_VERSION, SnowLLMError, synchronize
|
|
5
|
+
from .geometry import ModelGeometry
|
|
6
|
+
from ._version import __version__
|
|
7
|
+
|
|
8
|
+
__all__ = ["ABI_VERSION", "ModelGeometry", "SnowLLMError", "__version__", "synchronize"]
|
snowllm/_capi.py
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
# SPDX-FileCopyrightText: Copyright contributors to the SnowLLM project
|
|
3
|
+
|
|
4
|
+
import ctypes
|
|
5
|
+
import importlib.util
|
|
6
|
+
import os
|
|
7
|
+
from ctypes import POINTER, c_char_p, c_float, c_int, c_int64, c_void_p
|
|
8
|
+
|
|
9
|
+
__all__ = ["SnowLLMError", "build_geometry", "lib", "synchronize",
|
|
10
|
+
"assert_single_hip_runtime"]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class SnowLLMError(RuntimeError):
|
|
14
|
+
pass
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class BuildGeometry(ctypes.Structure):
|
|
18
|
+
_fields_ = [
|
|
19
|
+
(n, c_int64)
|
|
20
|
+
for n in (
|
|
21
|
+
"hidden", "num_heads", "num_kv_heads", "head_size", "block_size", "vocab_size",
|
|
22
|
+
"qkv_proj_n", "qkv_q_head_stride", "qkv_off_scale", "qkv_off_k", "qkv_off_v",
|
|
23
|
+
"lin_num_k_heads", "lin_num_v_heads", "lin_head_k", "lin_head_v", "lin_key_dim",
|
|
24
|
+
"lin_value_dim", "lin_conv_dim", "lin_conv_k", "lin_conv_state", "lin_in_proj_n",
|
|
25
|
+
"lin_in_proj_n_pad",
|
|
26
|
+
"lin_in_proj_ba_n_pad", "lin_off_qkv", "lin_off_z", "lin_off_b", "lin_off_a",
|
|
27
|
+
"moe_num_experts", "moe_shared_expert", "moe_num_slabs", "moe_topk", "moe_topk_all",
|
|
28
|
+
"moe_inter", "sampling_max_k", "lm_head_decode_max_m",
|
|
29
|
+
)
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def build_geometry() -> "BuildGeometry":
|
|
34
|
+
g = BuildGeometry()
|
|
35
|
+
lib.snowllm_model_config(ctypes.byref(g))
|
|
36
|
+
return g
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _find_lib() -> str:
|
|
40
|
+
if env := os.environ.get("SNOWLLM_LIB"):
|
|
41
|
+
return env
|
|
42
|
+
try:
|
|
43
|
+
import snowllm_kernels
|
|
44
|
+
except ModuleNotFoundError as e:
|
|
45
|
+
if e.name != "snowllm_kernels":
|
|
46
|
+
raise
|
|
47
|
+
raise SnowLLMError(
|
|
48
|
+
"the snowllm-kernels package is not installed, so there are no kernels to call. "
|
|
49
|
+
"`pip install snowllm-kernels` (it carries libsnowllm.so and needs no compiler), or "
|
|
50
|
+
"point SNOWLLM_LIB at a libsnowllm.so you built yourself."
|
|
51
|
+
) from e
|
|
52
|
+
lib = str(snowllm_kernels.LIB)
|
|
53
|
+
if not os.path.exists(lib):
|
|
54
|
+
raise SnowLLMError(
|
|
55
|
+
f"snowllm-kernels resolved to {os.path.dirname(snowllm_kernels.__file__)} but there is "
|
|
56
|
+
f"no libsnowllm.so at {lib}. If that is a source checkout, build it "
|
|
57
|
+
f"(`cmake --build build-release`); if it is an install, reinstall the wheel."
|
|
58
|
+
)
|
|
59
|
+
return lib
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _preload_hip_runtime() -> None:
|
|
63
|
+
spec = importlib.util.find_spec("_rocm_sdk_core")
|
|
64
|
+
if spec is None or not spec.submodule_search_locations:
|
|
65
|
+
return
|
|
66
|
+
sdk = os.path.join(spec.submodule_search_locations[0], "lib", "libamdhip64.so.7")
|
|
67
|
+
if os.path.exists(sdk):
|
|
68
|
+
try:
|
|
69
|
+
ctypes.CDLL(sdk, mode=ctypes.RTLD_GLOBAL)
|
|
70
|
+
except OSError:
|
|
71
|
+
pass
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
_preload_hip_runtime()
|
|
75
|
+
_lib_path = _find_lib()
|
|
76
|
+
lib = ctypes.CDLL(_lib_path)
|
|
77
|
+
|
|
78
|
+
ABI_VERSION = 7
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _check_abi() -> None:
|
|
82
|
+
try:
|
|
83
|
+
lib.snowllm_abi_version.restype = c_int
|
|
84
|
+
got = lib.snowllm_abi_version()
|
|
85
|
+
except AttributeError:
|
|
86
|
+
got = 0
|
|
87
|
+
if got != ABI_VERSION:
|
|
88
|
+
raise SnowLLMError(
|
|
89
|
+
f"ABI mismatch: this snowllm expects {ABI_VERSION}, "
|
|
90
|
+
f"{_lib_path} provides {got or 'no version at all'}. Rebuild the one that is stale."
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
_check_abi()
|
|
95
|
+
|
|
96
|
+
P, S = c_void_p, c_void_p
|
|
97
|
+
_SIGS = [
|
|
98
|
+
("snowllm_model_config", None, [POINTER(BuildGeometry)]),
|
|
99
|
+
("snowllm_shuffle_bytes", c_int64, [c_int64]),
|
|
100
|
+
("snowllm_prefill_row_quantum", c_int64, []),
|
|
101
|
+
("snowllm_sampling_max_k", c_int64, []),
|
|
102
|
+
("snowllm_error_string", c_char_p, [c_int]),
|
|
103
|
+
("snowllm_synchronize", c_int, [S]),
|
|
104
|
+
|
|
105
|
+
("snowllm_qkv_proj_shuffle_w", c_int, [P, P, S]),
|
|
106
|
+
("snowllm_qkv_proj_scratch_bytes", c_int64, [c_int64]),
|
|
107
|
+
("snowllm_qkv_proj_shuffle_w_fp8", c_int, [P, P, S]),
|
|
108
|
+
("snowllm_attn_out_scale_oproj_shuffle_w", c_int, [P, P, S]),
|
|
109
|
+
("snowllm_attn_out_scale_oproj_scratch_bytes", c_int64, [c_int64]),
|
|
110
|
+
("snowllm_attn_out_scale_oproj_shuffle_w_fp8", c_int, [P, P, S]),
|
|
111
|
+
("snowllm_linear_in_proj_shuffle_w", c_int, [P, P, S]),
|
|
112
|
+
("snowllm_linear_out_proj_shuffle_w", c_int, [P, P, S]),
|
|
113
|
+
("snowllm_linear_out_proj_shuffle_w_fp8", c_int, [P, P, S]),
|
|
114
|
+
("snowllm_linear_in_proj_qz_shuffle_w_fp8", c_int, [P, P, S]),
|
|
115
|
+
("snowllm_linear_in_proj_ba_shuffle_w", c_int, [P, P, S]),
|
|
116
|
+
("snowllm_fused_linear_attn_workspace_bytes", c_int64, [c_int64]),
|
|
117
|
+
("snowllm_mtp_fc_shuffle_w", c_int, [P, P, S]),
|
|
118
|
+
("snowllm_mtp_fc_scratch_bytes", c_int64, [c_int64]),
|
|
119
|
+
("snowllm_mtp_pre_fc", c_int, [P, P, P, P, P, c_int64, c_float, S]),
|
|
120
|
+
("snowllm_mtp_fc", c_int, [P, P, P, P, c_int64, c_int, S]),
|
|
121
|
+
("snowllm_moe_shuffle_gate_up", c_int, [P, P, P, c_int64, S]),
|
|
122
|
+
("snowllm_moe_shuffle_down", c_int, [P, P, c_int64, S]),
|
|
123
|
+
("snowllm_moe_shuffle_gate_up_fused", c_int, [P, P, c_int64, S]),
|
|
124
|
+
("snowllm_proj_scale_shuffle_fp8", c_int, [P, P, c_int64, c_int64, S]),
|
|
125
|
+
("snowllm_moe_scale_shuffle_gate_up_fp8", c_int, [P, P, P, c_int64, S]),
|
|
126
|
+
("snowllm_moe_scale_shuffle_down_fp8", c_int, [P, P, c_int64, S]),
|
|
127
|
+
("snowllm_moe_shuffle_gate_up_fp8", c_int, [P, P, P, c_int64, S]),
|
|
128
|
+
("snowllm_moe_shuffle_down_fp8", c_int, [P, P, c_int64, S]),
|
|
129
|
+
("snowllm_moe_shuffle_router_bytes", c_int64, []),
|
|
130
|
+
("snowllm_moe_shuffle_router", c_int, [P, P, S]),
|
|
131
|
+
("snowllm_moe_workspace_bytes", c_int64, [c_int64]),
|
|
132
|
+
("snowllm_lm_head_shuffle_weight", c_int, [P, P, S]),
|
|
133
|
+
("snowllm_lm_head_rows_for", c_int64, [c_int64]),
|
|
134
|
+
("snowllm_lm_head_pad_bytes", c_int64, [c_int64]),
|
|
135
|
+
("snowllm_lm_head_scratch_bytes", c_int64, [c_int64]),
|
|
136
|
+
("snowllm_lm_head", c_int, [P, P, P, c_int64, P, P, S]),
|
|
137
|
+
("snowllm_paged_prefill_max_tokens", c_int64, []),
|
|
138
|
+
("snowllm_paged_decode_workspace_size", c_int64, [c_int64, c_int64]),
|
|
139
|
+
("snowllm_kv_pool_bytes", None, [c_int64, c_int, P, P]),
|
|
140
|
+
("snowllm_kv_scale_bytes", None, [c_int64, P, P]),
|
|
141
|
+
("snowllm_resolve_slots", c_int, [P, c_int64, P, P, P, c_int64, S]),
|
|
142
|
+
("snowllm_kv_blocks_for", c_int64, [c_int64]),
|
|
143
|
+
("snowllm_linear_state_slots", None, [c_int64, c_int64, c_int64, c_int64, P]),
|
|
144
|
+
("snowllm_prefill_q_blocks", c_int64, [P, c_int64]),
|
|
145
|
+
("snowllm_prefill_q_block_map", None, [P, c_int64, P]),
|
|
146
|
+
("snowllm_moe_variant_force", None, [c_int]),
|
|
147
|
+
("snowllm_moe_variant_name", c_char_p, [c_int]),
|
|
148
|
+
("snowllm_paged_window_blocks", c_int64, [c_int64, c_int64]),
|
|
149
|
+
("snowllm_paged_window_view", c_int, [P, c_int64, P, c_int64, c_int64, c_int64, P,
|
|
150
|
+
c_int64, P, S]),
|
|
151
|
+
("snowllm_paged_decode_max_q_tokens", c_int64, []),
|
|
152
|
+
("snowllm_paged_decode_num_slots", c_int64, [c_int64]),
|
|
153
|
+
("snowllm_paged_decode_plan_elems", c_int64, [c_int64, c_int64]),
|
|
154
|
+
|
|
155
|
+
("snowllm_gather_embedding", c_int, [P, P, P, c_int64, S]),
|
|
156
|
+
("snowllm_rmsnorm", c_int, [P, P, P, c_int64, c_float, S]),
|
|
157
|
+
("snowllm_rmsnorm_residual", c_int, [P, P, P, P, c_int64, c_float, S]),
|
|
158
|
+
("snowllm_rmsnorm_shuffled", c_int, [P, P, P, c_int64, c_float, S]),
|
|
159
|
+
("snowllm_rmsnorm_residual_shuffled", c_int, [P, P, P, P, c_int64, c_float, S]),
|
|
160
|
+
("snowllm_qkv_proj", c_int, [P, P, P, P, c_int64, c_int, S]),
|
|
161
|
+
("snowllm_qkv_proj_fp8", c_int, [P, P, P, P, P, c_int64, c_int, S]),
|
|
162
|
+
("snowllm_qk_norm", c_int, [P, c_int64, P, P, P, P, c_int64, c_float, S]),
|
|
163
|
+
("snowllm_qk_norm_rope", c_int, [P, c_int64, P, P, P, P, P, P, c_int64, c_float, S]),
|
|
164
|
+
("snowllm_attn_out_scale_oproj", c_int, [P, P, c_int64, P, P, P, c_int64, c_int, S]),
|
|
165
|
+
("snowllm_attn_out_scale_oproj_fp8", c_int, [P, P, c_int64, P, P, P, P, c_int64, c_int, S]),
|
|
166
|
+
("snowllm_rope_cos_sin", c_int, [P, P, P, P, c_int64, S]),
|
|
167
|
+
("snowllm_rope_cos_sin_mrope", c_int, [P, P, P, P, c_int64, S]),
|
|
168
|
+
("snowllm_rope_apply_q", c_int, [P, P, P, c_int64, S]),
|
|
169
|
+
("snowllm_rope_apply_k", c_int, [P, P, P, c_int64, S]),
|
|
170
|
+
("snowllm_reshape_and_cache", c_int, [P, P, c_int64, c_int64, P, P, P, c_int64, S]),
|
|
171
|
+
("snowllm_reshape_and_cache_int8", c_int,
|
|
172
|
+
[P, P, c_int64, c_int64, P, P, P, P, P, c_int64, S]),
|
|
173
|
+
("snowllm_paged_attn_prefill_int8", c_int,
|
|
174
|
+
[P, P, P, P, P, P, P, P, P, c_int64, c_int64, c_int64, c_float, S, P]),
|
|
175
|
+
("snowllm_paged_attn_decode_int8", c_int,
|
|
176
|
+
[P, P, P, P, P, P, P, P, P, c_int64, c_int64, c_int64, P, c_float, c_int64, S]),
|
|
177
|
+
("snowllm_paged_attn_prefill", c_int,
|
|
178
|
+
[P, P, P, P, P, P, P, c_int64, c_int64, c_int64, c_float, S, P]),
|
|
179
|
+
("snowllm_paged_attn_decode", c_int,
|
|
180
|
+
[P, P, P, P, P, P, P, c_int64, c_int64, c_int64, P, c_float, c_int64, S]),
|
|
181
|
+
("snowllm_paged_attn_decode_plan", c_int, [P, P, c_int64, c_int64, c_int, S]),
|
|
182
|
+
("snowllm_fused_linear_attn", c_int,
|
|
183
|
+
[P, P, P, P, P, P, P,
|
|
184
|
+
P, P, P, P,
|
|
185
|
+
P, P, P, P,
|
|
186
|
+
P, P, P, P,
|
|
187
|
+
c_int64, c_int64, c_int, S]),
|
|
188
|
+
("snowllm_fused_moe", c_int, [P, P, P, P, P, P, c_int64, S]),
|
|
189
|
+
("snowllm_fused_moe_fp8", c_int, [P, P, P, P, P, P, P, P, c_int64, S]),
|
|
190
|
+
("snowllm_lm_head_gemm_decode", c_int, [P, P, P, c_int64, S]),
|
|
191
|
+
("snowllm_lm_head_gemm_prefill", c_int, [P, P, P, P, c_int64, S]),
|
|
192
|
+
("snowllm_sampling_softmax", c_int, [P, P, c_int64, c_int64, P, S]),
|
|
193
|
+
("snowllm_sampling_topk_topp", c_int, [P, P, P, c_int64, c_int64, c_int64, P, P, S]),
|
|
194
|
+
("snowllm_sampling_multinomial", c_int, [P, P, P, P, c_int64, c_int64, S]),
|
|
195
|
+
("snowllm_sampling_argmax", c_int, [P, P, c_int64, c_int64, S]),
|
|
196
|
+
|
|
197
|
+
("snowllm_gemm_bf16_shuffle_b", c_int, [P, P, c_int64, c_int64, S]),
|
|
198
|
+
("snowllm_vision_layernorm", c_int, [P, P, P, P, c_int64, c_int64, c_float, S]),
|
|
199
|
+
("snowllm_vision_gemm_scratch_bytes", c_int64, [c_int64, c_int64]),
|
|
200
|
+
("snowllm_vision_gemm_bias_act", c_int,
|
|
201
|
+
[P, P, P, P, P, P, c_int64, c_int64, c_int64, c_int, S]),
|
|
202
|
+
("snowllm_vision_rope_qk", c_int, [P, P, P, c_int64, c_int64, c_int64, c_int64, S]),
|
|
203
|
+
("snowllm_vision_attn", c_int, [P, P, P, P, c_int64, c_int64, c_int64, c_int64, S]),
|
|
204
|
+
("snowllm_vision_supported_head_dims", c_int64, [P, c_int64]),
|
|
205
|
+
("snowllm_vision_supported_ln_hidden", c_int64, [P, c_int64]),
|
|
206
|
+
|
|
207
|
+
("snowllm_file_reader_create", P, [c_int, c_int64, c_int]),
|
|
208
|
+
("snowllm_file_reader_destroy", None, [P]),
|
|
209
|
+
("snowllm_file_reader_read", c_int, [P, c_char_p, c_int64, c_int64, P, S]),
|
|
210
|
+
]
|
|
211
|
+
_missing = []
|
|
212
|
+
for _name, _res, _args in _SIGS:
|
|
213
|
+
try:
|
|
214
|
+
_fn = getattr(lib, _name)
|
|
215
|
+
except AttributeError:
|
|
216
|
+
_missing.append(_name)
|
|
217
|
+
continue
|
|
218
|
+
_fn.restype = _res
|
|
219
|
+
_fn.argtypes = _args
|
|
220
|
+
if _missing:
|
|
221
|
+
raise SnowLLMError(f"{_lib_path} is missing {len(_missing)} entry points this binding needs:\n "
|
|
222
|
+
+ "\n ".join(_missing))
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def check(status: int, what: str) -> None:
|
|
226
|
+
if status != 0:
|
|
227
|
+
msg = lib.snowllm_error_string(status).decode()
|
|
228
|
+
raise SnowLLMError(f"{what}: hip status {status}: {msg}")
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def synchronize(stream: int = 0) -> None:
|
|
232
|
+
check(lib.snowllm_synchronize(stream), "synchronize")
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def assert_single_hip_runtime() -> None:
|
|
236
|
+
paths = set()
|
|
237
|
+
with open("/proc/self/maps") as f:
|
|
238
|
+
for line in f:
|
|
239
|
+
i = line.find("libamdhip64.so")
|
|
240
|
+
if i != -1:
|
|
241
|
+
paths.add(os.path.realpath(line.rsplit(" ", 1)[-1].strip()))
|
|
242
|
+
if len(paths) > 1:
|
|
243
|
+
raise SnowLLMError(
|
|
244
|
+
"two HIP runtimes are loaded in this process -- torch's device pointers are not valid "
|
|
245
|
+
"in snowllm's kernels:\n " + "\n ".join(sorted(paths))
|
|
246
|
+
)
|
snowllm/_version.py
ADDED
snowllm/async_engine.py
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
# SPDX-FileCopyrightText: Copyright contributors to the SnowLLM project
|
|
3
|
+
|
|
4
|
+
import asyncio
|
|
5
|
+
import pathlib
|
|
6
|
+
import time
|
|
7
|
+
import traceback
|
|
8
|
+
from collections.abc import AsyncIterator
|
|
9
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
10
|
+
|
|
11
|
+
from .engine import Engine, Request, SamplingParams
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class AsyncEngine:
|
|
15
|
+
def __init__(self, engine: Engine, profile_dir: str | None = None,
|
|
16
|
+
stats_interval: float = 0.0):
|
|
17
|
+
self.engine = engine
|
|
18
|
+
self.stats_interval = stats_interval
|
|
19
|
+
self._last_stats = (0.0, None)
|
|
20
|
+
self.profile_dir = pathlib.Path(profile_dir).expanduser() if profile_dir else None
|
|
21
|
+
if self.profile_dir:
|
|
22
|
+
self.profile_dir.mkdir(parents=True, exist_ok=True)
|
|
23
|
+
self._profiler = None
|
|
24
|
+
self._worker = ThreadPoolExecutor(max_workers=1, thread_name_prefix="snowllm-engine")
|
|
25
|
+
self._live: list[Request] = []
|
|
26
|
+
self._queues: dict[int, asyncio.Queue] = {}
|
|
27
|
+
self._sent: dict[int, int] = {}
|
|
28
|
+
self._wake = asyncio.Event()
|
|
29
|
+
self._task: asyncio.Task | None = None
|
|
30
|
+
|
|
31
|
+
def start(self) -> None:
|
|
32
|
+
self._task = asyncio.create_task(self._run())
|
|
33
|
+
|
|
34
|
+
async def start_profile(self) -> None:
|
|
35
|
+
if self.profile_dir is None:
|
|
36
|
+
raise RuntimeError("profiling is off; start the server with --profile-dir")
|
|
37
|
+
if self._profiler is not None:
|
|
38
|
+
raise RuntimeError("a profile is already running")
|
|
39
|
+
loop = asyncio.get_running_loop()
|
|
40
|
+
await loop.run_in_executor(self._worker, self._profiler_start)
|
|
41
|
+
|
|
42
|
+
async def stop_profile(self) -> str:
|
|
43
|
+
if self._profiler is None:
|
|
44
|
+
raise RuntimeError("no profile is running")
|
|
45
|
+
loop = asyncio.get_running_loop()
|
|
46
|
+
return await loop.run_in_executor(self._worker, self._profiler_stop)
|
|
47
|
+
|
|
48
|
+
def _profiler_start(self) -> None:
|
|
49
|
+
import torch
|
|
50
|
+
|
|
51
|
+
self._profiler = torch.profiler.profile(
|
|
52
|
+
activities=[torch.profiler.ProfilerActivity.CPU,
|
|
53
|
+
torch.profiler.ProfilerActivity.CUDA],
|
|
54
|
+
with_stack=True,
|
|
55
|
+
on_trace_ready=torch.profiler.tensorboard_trace_handler(str(self.profile_dir)),
|
|
56
|
+
)
|
|
57
|
+
self._profiler.start()
|
|
58
|
+
|
|
59
|
+
def _profiler_stop(self) -> str:
|
|
60
|
+
p, self._profiler = self._profiler, None
|
|
61
|
+
p.stop()
|
|
62
|
+
return str(self.profile_dir)
|
|
63
|
+
|
|
64
|
+
async def close(self) -> None:
|
|
65
|
+
if self._task:
|
|
66
|
+
self._task.cancel()
|
|
67
|
+
try:
|
|
68
|
+
await self._task
|
|
69
|
+
except asyncio.CancelledError:
|
|
70
|
+
pass
|
|
71
|
+
self._worker.shutdown(wait=True)
|
|
72
|
+
|
|
73
|
+
def stats(self):
|
|
74
|
+
return self.engine.stats()
|
|
75
|
+
|
|
76
|
+
def submit(self, prompt: list[int], params: SamplingParams, rope_factor: float = 1.0,
|
|
77
|
+
**mm) -> Request:
|
|
78
|
+
r = self.engine.add(prompt, params, rope_factor=rope_factor, **mm)
|
|
79
|
+
self._live.append(r)
|
|
80
|
+
self._queues[id(r)] = asyncio.Queue()
|
|
81
|
+
self._sent[id(r)] = 0
|
|
82
|
+
self._wake.set()
|
|
83
|
+
return r
|
|
84
|
+
|
|
85
|
+
async def stream(self, r: Request) -> AsyncIterator[int]:
|
|
86
|
+
q = self._queues[id(r)]
|
|
87
|
+
try:
|
|
88
|
+
while True:
|
|
89
|
+
tok = await q.get()
|
|
90
|
+
if tok is None:
|
|
91
|
+
return
|
|
92
|
+
yield tok
|
|
93
|
+
finally:
|
|
94
|
+
if not r.done:
|
|
95
|
+
self.abort(r)
|
|
96
|
+
self._queues.pop(id(r), None)
|
|
97
|
+
|
|
98
|
+
def abort(self, r: Request, reason: str = "abort") -> None:
|
|
99
|
+
r.done = True
|
|
100
|
+
r.finish_reason = r.finish_reason or reason
|
|
101
|
+
if r in self.engine.waiting:
|
|
102
|
+
self.engine.waiting.remove(r)
|
|
103
|
+
self._queues[id(r)].put_nowait(None)
|
|
104
|
+
self._close(r)
|
|
105
|
+
|
|
106
|
+
async def _run(self) -> None:
|
|
107
|
+
loop = asyncio.get_running_loop()
|
|
108
|
+
while True:
|
|
109
|
+
if not self.engine.waiting and not self.engine.running:
|
|
110
|
+
await self._wake.wait()
|
|
111
|
+
self._wake.clear()
|
|
112
|
+
continue
|
|
113
|
+
try:
|
|
114
|
+
await loop.run_in_executor(self._worker, self.engine.step)
|
|
115
|
+
except asyncio.CancelledError:
|
|
116
|
+
raise
|
|
117
|
+
except Exception:
|
|
118
|
+
traceback.print_exc()
|
|
119
|
+
self._fail_all()
|
|
120
|
+
continue
|
|
121
|
+
self._dispatch()
|
|
122
|
+
self._log_stats()
|
|
123
|
+
|
|
124
|
+
def _log_stats(self) -> None:
|
|
125
|
+
if not self.stats_interval or self.engine.acct is None:
|
|
126
|
+
return
|
|
127
|
+
now = time.monotonic()
|
|
128
|
+
was_at, was = self._last_stats
|
|
129
|
+
if was is not None and now - was_at < self.stats_interval:
|
|
130
|
+
return
|
|
131
|
+
cur = self.engine.acct.snapshot()
|
|
132
|
+
self._last_stats = (now, cur)
|
|
133
|
+
if was is None:
|
|
134
|
+
return
|
|
135
|
+
dp_t = cur["prefill_tokens"] - was["prefill_tokens"]
|
|
136
|
+
dp_s = cur["prefill_seconds"] - was["prefill_seconds"]
|
|
137
|
+
dd_t = cur["decode_tokens"] - was["decode_tokens"]
|
|
138
|
+
dd_s = cur["decode_seconds"] - was["decode_seconds"]
|
|
139
|
+
dd_r = cur["decode_rows"] - was["decode_rows"]
|
|
140
|
+
st = self.engine.stats()
|
|
141
|
+
kv = 1.0 - st.free_kv_blocks / max(self.engine.blocks.total, 1)
|
|
142
|
+
print(f"[snowllm {time.strftime('%H:%M:%S')}] "
|
|
143
|
+
f"prefill {dp_t / dp_s if dp_s else 0.0:7.1f} tok/s | "
|
|
144
|
+
f"decode {dd_t / dd_s if dd_s else 0.0:7.1f} tok/s | "
|
|
145
|
+
f"AL {dd_t / dd_r if dd_r else 0.0:.3f} | "
|
|
146
|
+
f"running {st.running:3d} waiting {st.waiting:4d} | KV {kv * 100:4.1f}%", flush=True)
|
|
147
|
+
|
|
148
|
+
def _fail_all(self) -> None:
|
|
149
|
+
for r in self._live:
|
|
150
|
+
r.done, r.finish_reason = True, "error"
|
|
151
|
+
self._dispatch()
|
|
152
|
+
|
|
153
|
+
def _dispatch(self) -> None:
|
|
154
|
+
for r in list(self._live):
|
|
155
|
+
q = self._queues.get(id(r))
|
|
156
|
+
if q is None:
|
|
157
|
+
self._close(r)
|
|
158
|
+
continue
|
|
159
|
+
n = self._sent[id(r)]
|
|
160
|
+
for tok in r.out[n:]:
|
|
161
|
+
q.put_nowait(tok)
|
|
162
|
+
self._sent[id(r)] = len(r.out)
|
|
163
|
+
if r.done:
|
|
164
|
+
q.put_nowait(None)
|
|
165
|
+
self._close(r)
|
|
166
|
+
|
|
167
|
+
def _close(self, r: Request) -> None:
|
|
168
|
+
if r in self._live:
|
|
169
|
+
self._live.remove(r)
|
|
170
|
+
self._sent.pop(id(r), None)
|
snowllm/block_manager.py
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
# SPDX-FileCopyrightText: Copyright contributors to the SnowLLM project
|
|
3
|
+
|
|
4
|
+
import torch
|
|
5
|
+
|
|
6
|
+
from . import ops
|
|
7
|
+
from .request import Request
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def pad_table(rows: list[list[int]]) -> torch.Tensor:
|
|
11
|
+
w = max((len(x) for x in rows), default=1) or 1
|
|
12
|
+
t = torch.zeros(len(rows), w, dtype=torch.int32)
|
|
13
|
+
for i, x in enumerate(rows):
|
|
14
|
+
t[i, : len(x)] = torch.tensor(x, dtype=torch.int32)
|
|
15
|
+
return t.to("cuda")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def slot_mapping(bt: torch.Tensor, rows: list[tuple[int, int]]) -> torch.Tensor:
|
|
19
|
+
seq = torch.tensor([b for b, _ in rows], dtype=torch.int32, device="cuda")
|
|
20
|
+
pos = torch.tensor([p for _, p in rows], dtype=torch.int32, device="cuda")
|
|
21
|
+
return ops.resolve_slots(bt, seq, pos)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def block_tables(batch: list[Request]) -> torch.Tensor:
|
|
25
|
+
return pad_table([r.blocks for r in batch])
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class BlockAllocator:
|
|
29
|
+
def __init__(self, num_blocks: int):
|
|
30
|
+
self.free = list(range(num_blocks))
|
|
31
|
+
self.total = num_blocks
|
|
32
|
+
|
|
33
|
+
def alloc(self, n: int) -> list[int] | None:
|
|
34
|
+
if n > len(self.free):
|
|
35
|
+
return None
|
|
36
|
+
return [self.free.pop() for _ in range(n)]
|
|
37
|
+
|
|
38
|
+
def release(self, blocks: list[int]) -> None:
|
|
39
|
+
self.free.extend(blocks)
|
|
40
|
+
|
|
41
|
+
def grow(self, r: Request, n: int = 1) -> bool:
|
|
42
|
+
need = ops.kv_blocks_for(r.num_cached + n)
|
|
43
|
+
while need > len(r.blocks):
|
|
44
|
+
more = self.alloc(1)
|
|
45
|
+
if more is None:
|
|
46
|
+
return False
|
|
47
|
+
r.blocks += more
|
|
48
|
+
return True
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class StateSlots:
|
|
52
|
+
def __init__(self, max_num_seqs: int, T: int, dummy: int):
|
|
53
|
+
self.T = T
|
|
54
|
+
self.free = list(range(max_num_seqs))
|
|
55
|
+
self._dummy = dummy
|
|
56
|
+
|
|
57
|
+
def take(self, r: Request) -> None:
|
|
58
|
+
r.slot = self.free.pop()
|
|
59
|
+
r.state_head = ops.linear_state_slots(r.slot, -1, 1, self.T)[0]
|
|
60
|
+
|
|
61
|
+
def give_back(self, r: Request) -> None:
|
|
62
|
+
self.free.append(r.slot)
|
|
63
|
+
|
|
64
|
+
def name(self, r: Request, rows: int) -> list[int]:
|
|
65
|
+
return ops.linear_state_slots(r.slot, r.state_head, rows, self.T)
|
|
66
|
+
|
|
67
|
+
def dummy_indices(self, n: int) -> torch.Tensor:
|
|
68
|
+
return torch.full((n,), self._dummy, dtype=torch.int32, device="cuda")
|