Soumik-404 commited on
Commit
3a74e13
·
0 Parent(s):

first commit

Browse files
.gitignore ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ models/
2
+ __pycache__/
3
+ *.pyc
4
+ .DS_Store
5
+ .env
6
+ *collection.json
Dockerfile ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM ubuntu:22.04 AS builder
2
+
3
+ ENV DEBIAN_FRONTEND=noninteractive
4
+
5
+ RUN apt-get update && apt-get install -y --no-install-recommends \
6
+ cmake \
7
+ build-essential \
8
+ python3 \
9
+ python3-pip \
10
+ git \
11
+ wget \
12
+ software-properties-common \
13
+ gnupg \
14
+ libomp-dev \
15
+ && wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | tee /etc/apt/trusted.gpg.d/llvm.asc \
16
+ && add-apt-repository -y "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-18 main" \
17
+ && apt-get update && apt-get install -y --no-install-recommends clang-18 \
18
+ && ln -s /usr/bin/clang-18 /usr/bin/clang \
19
+ && ln -s /usr/bin/clang++-18 /usr/bin/clang++ \
20
+ && rm -rf /var/lib/apt/lists/*
21
+
22
+ WORKDIR /build
23
+
24
+ RUN git clone --recursive https://github.com/microsoft/BitNet.git .
25
+
26
+ RUN pip3 install --no-cache-dir 3rdparty/llama.cpp/gguf-py
27
+
28
+ RUN sed -i 's/int8_t \* y_col = y + col \* by;/const int8_t * y_col = y + col * by;/' src/ggml-bitnet-mad.cpp
29
+
30
+ RUN python3 utils/codegen_tl2.py \
31
+ --model bitnet_b1_58-3B \
32
+ --BM 160,320,320 \
33
+ --BK 96,96,96 \
34
+ --bm 32,32,32
35
+
36
+ RUN cmake -B build \
37
+ -DBITNET_X86_TL2=OFF \
38
+ -DCMAKE_C_COMPILER=clang \
39
+ -DCMAKE_CXX_COMPILER=clang++ \
40
+ -DCMAKE_BUILD_TYPE=Release \
41
+ && cmake --build build --config Release -j$(nproc) --target llama-server
42
+
43
+ FROM ubuntu:22.04
44
+
45
+ ENV DEBIAN_FRONTEND=noninteractive
46
+
47
+ RUN apt-get update && apt-get install -y --no-install-recommends \
48
+ libgomp1 \
49
+ python3 \
50
+ python3-pip \
51
+ && rm -rf /var/lib/apt/lists/*
52
+
53
+ RUN useradd -m -u 1000 user
54
+
55
+ WORKDIR /app
56
+
57
+ COPY --from=builder /build/build/bin/llama-server ./build/bin/llama-server
58
+ COPY --from=builder /build/build/3rdparty/llama.cpp/src/libllama.so ./build/lib/
59
+ COPY --from=builder /build/build/3rdparty/llama.cpp/ggml/src/libggml.so ./build/lib/
60
+
61
+ ENV LD_LIBRARY_PATH=/app/build/lib
62
+ ENV HF_HUB_ENABLE_HF_TRANSFER=1
63
+
64
+ RUN pip3 install --no-cache-dir huggingface-hub hf_transfer \
65
+ && mkdir -p /models \
66
+ && huggingface-cli download microsoft/BitNet-b1.58-2B-4T-gguf ggml-model-i2_s.gguf --local-dir /models
67
+
68
+ RUN chown -R user:user /app /models
69
+
70
+ USER user
71
+
72
+ EXPOSE 7860
73
+
74
+ ENTRYPOINT ["./build/bin/llama-server", "--host", "0.0.0.0", "--port", "7860"]
75
+ CMD ["-m", "/models/ggml-model-i2_s.gguf", "--mlock", "--parallel", "5"]
Dockerfiles/Dockerfile.2core ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM ubuntu:22.04 AS builder
2
+
3
+ ENV DEBIAN_FRONTEND=noninteractive
4
+
5
+ RUN apt-get update && apt-get install -y --no-install-recommends \
6
+ cmake \
7
+ build-essential \
8
+ python3 \
9
+ python3-pip \
10
+ git \
11
+ wget \
12
+ software-properties-common \
13
+ gnupg \
14
+ libomp-dev \
15
+ && wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | tee /etc/apt/trusted.gpg.d/llvm.asc \
16
+ && add-apt-repository -y "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-18 main" \
17
+ && apt-get update && apt-get install -y --no-install-recommends clang-18 \
18
+ && ln -s /usr/bin/clang-18 /usr/bin/clang \
19
+ && ln -s /usr/bin/clang++-18 /usr/bin/clang++ \
20
+ && rm -rf /var/lib/apt/lists/*
21
+
22
+ WORKDIR /build
23
+
24
+ RUN git clone --recursive https://github.com/microsoft/BitNet.git .
25
+
26
+ RUN pip3 install --no-cache-dir 3rdparty/llama.cpp/gguf-py
27
+
28
+ RUN sed -i 's/int8_t \* y_col = y + col \* by;/const int8_t * y_col = y + col * by;/' src/ggml-bitnet-mad.cpp
29
+
30
+ RUN python3 utils/codegen_tl2.py \
31
+ --model bitnet_b1_58-3B \
32
+ --BM 160,320,320 \
33
+ --BK 96,96,96 \
34
+ --bm 32,32,32
35
+
36
+ RUN cmake -B build \
37
+ -DBITNET_X86_TL2=OFF \
38
+ -DCMAKE_C_COMPILER=clang \
39
+ -DCMAKE_CXX_COMPILER=clang++ \
40
+ -DCMAKE_BUILD_TYPE=Release \
41
+ && cmake --build build --config Release -j2 --target llama-server
42
+
43
+ FROM ubuntu:22.04
44
+
45
+ ENV DEBIAN_FRONTEND=noninteractive
46
+
47
+ RUN apt-get update && apt-get install -y --no-install-recommends \
48
+ libgomp1 \
49
+ && rm -rf /var/lib/apt/lists/*
50
+
51
+ WORKDIR /app
52
+
53
+ COPY --from=builder /build/build/bin/llama-server ./build/bin/llama-server
54
+ COPY --from=builder /build/build/3rdparty/llama.cpp/src/libllama.so ./build/lib/
55
+ COPY --from=builder /build/build/3rdparty/llama.cpp/ggml/src/libggml.so ./build/lib/
56
+
57
+ ENV LD_LIBRARY_PATH=/app/build/lib
58
+
59
+ EXPOSE 8080
60
+
61
+ ENTRYPOINT ["./build/bin/llama-server", "--host", "0.0.0.0", "--port", "8080"]
62
+ CMD ["-t", "2", "-tb", "2", "--threads-http", "2", "--mlock", "--parallel", "2"]
Dockerfiles/Dockerfile.allcore ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM ubuntu:22.04 AS builder
2
+
3
+ ENV DEBIAN_FRONTEND=noninteractive
4
+
5
+ RUN apt-get update && apt-get install -y --no-install-recommends \
6
+ cmake \
7
+ build-essential \
8
+ python3 \
9
+ python3-pip \
10
+ git \
11
+ wget \
12
+ software-properties-common \
13
+ gnupg \
14
+ libomp-dev \
15
+ && wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | tee /etc/apt/trusted.gpg.d/llvm.asc \
16
+ && add-apt-repository -y "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-18 main" \
17
+ && apt-get update && apt-get install -y --no-install-recommends clang-18 \
18
+ && ln -s /usr/bin/clang-18 /usr/bin/clang \
19
+ && ln -s /usr/bin/clang++-18 /usr/bin/clang++ \
20
+ && rm -rf /var/lib/apt/lists/*
21
+
22
+ WORKDIR /build
23
+
24
+ RUN git clone --recursive https://github.com/microsoft/BitNet.git .
25
+
26
+ RUN pip3 install --no-cache-dir 3rdparty/llama.cpp/gguf-py
27
+
28
+ RUN sed -i 's/int8_t \* y_col = y + col \* by;/const int8_t * y_col = y + col * by;/' src/ggml-bitnet-mad.cpp
29
+
30
+ RUN python3 utils/codegen_tl2.py \
31
+ --model bitnet_b1_58-3B \
32
+ --BM 160,320,320 \
33
+ --BK 96,96,96 \
34
+ --bm 32,32,32
35
+
36
+ RUN cmake -B build \
37
+ -DBITNET_X86_TL2=OFF \
38
+ -DCMAKE_C_COMPILER=clang \
39
+ -DCMAKE_CXX_COMPILER=clang++ \
40
+ -DCMAKE_BUILD_TYPE=Release \
41
+ && cmake --build build --config Release -j$(nproc) --target llama-server
42
+
43
+ FROM ubuntu:22.04
44
+
45
+ ENV DEBIAN_FRONTEND=noninteractive
46
+
47
+ RUN apt-get update && apt-get install -y --no-install-recommends \
48
+ libgomp1 \
49
+ && rm -rf /var/lib/apt/lists/*
50
+
51
+ WORKDIR /app
52
+
53
+ COPY --from=builder /build/build/bin/llama-server ./build/bin/llama-server
54
+ COPY --from=builder /build/build/3rdparty/llama.cpp/src/libllama.so ./build/lib/
55
+ COPY --from=builder /build/build/3rdparty/llama.cpp/ggml/src/libggml.so ./build/lib/
56
+
57
+ ENV LD_LIBRARY_PATH=/app/build/lib
58
+
59
+ EXPOSE 8080
60
+
61
+ ENTRYPOINT ["./build/bin/llama-server", "--host", "0.0.0.0", "--port", "8080"]
62
+ CMD ["--mlock", "--parallel", "5"]
README.md ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: BitNet b1.58 2B API
3
+ emoji: 🤖
4
+ colorFrom: red
5
+ colorTo: cyan
6
+ sdk: docker
7
+ app_port: 7860
8
+ ---
stress_test.py ADDED
@@ -0,0 +1,304 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ BitNet b1.58 async stress test.
3
+
4
+ Fires N concurrent requests against the llama-server OpenAI-compatible API
5
+ and collects per-request timing, token throughput, and error stats.
6
+ Uses asyncio + aiohttp for non-blocking I/O.
7
+
8
+ Usage:
9
+ python stress_test.py [--url URL] [--requests N] [--concurrency C] [--warmup W]
10
+ """
11
+
12
+ import sys
13
+ import time
14
+ import json
15
+ import asyncio
16
+ import statistics
17
+ import argparse
18
+ from datetime import datetime
19
+
20
+ import aiohttp
21
+ import colorama
22
+
23
+ colorama.init(autoreset=True)
24
+
25
+ C = colorama.Fore
26
+ S = colorama.Style
27
+ DIM = colorama.Style.DIM
28
+
29
+ PAYLOAD = {
30
+ "model": "bitnet",
31
+ "messages": [
32
+ {
33
+ "role": "user",
34
+ "content": (
35
+ "Explain the concept of neural networks in simple terms. "
36
+ "Include how they are trained and what makes them different "
37
+ "from traditional computer programs."
38
+ )
39
+ }
40
+ ],
41
+ "max_tokens": 80,
42
+ "temperature": 0.7,
43
+ }
44
+
45
+ DESCRIPTION = "Medium chat completion (32 tok prompt, 80 tok generate)"
46
+
47
+
48
+ def log(msg, color=C.WHITE, bright=False):
49
+ style = S.BRIGHT if bright else ""
50
+ ts = datetime.now().strftime("%H:%M:%S.%f")[:-3]
51
+ print(f"{DIM}{ts}{S.RESET_ALL} {style}{color}{msg}{S.RESET_ALL}")
52
+
53
+
54
+ # ---------------------------------------------------------------------------
55
+ # Per-request worker
56
+ # ---------------------------------------------------------------------------
57
+ async def do_request(session, req_id, url, timeout):
58
+ start = time.perf_counter()
59
+ try:
60
+ async with session.post(
61
+ url,
62
+ json=PAYLOAD,
63
+ timeout=aiohttp.ClientTimeout(total=timeout),
64
+ ) as resp:
65
+ elapsed = time.perf_counter() - start
66
+ status = resp.status
67
+ body = await resp.json()
68
+
69
+ if status == 200 and "choices" in body:
70
+ usage = body.get("usage", {})
71
+ pt = usage.get("prompt_tokens", 0)
72
+ ct = usage.get("completion_tokens", 0)
73
+ tt = usage.get("total_tokens", 0)
74
+ tok_sec = ct / elapsed if elapsed > 0 else 0
75
+ content = body.get("choices", [{}])[0].get("message", {}).get("content", "")
76
+
77
+ ok = True
78
+ err = None
79
+ ctext = content[:55].replace("\n", " ")
80
+ log(
81
+ f"[#{req_id:03d}] {C.GREEN}OK{C.RESET} "
82
+ f"{elapsed:6.1f}s {ct:3d} tok {tok_sec:5.1f} tok/s "
83
+ f"\"{ctext}...\"",
84
+ )
85
+ else:
86
+ pt = ct = tt = 0
87
+ tok_sec = 0
88
+ ok = False
89
+ err = body.get("error", {}).get("message", f"HTTP {status}")
90
+ content = ""
91
+ log(f"[#{req_id:03d}] {C.RED}FAIL{C.RESET} {elapsed:6.1f}s {err}", C.RED)
92
+
93
+ except asyncio.TimeoutError:
94
+ elapsed = time.perf_counter() - start
95
+ ok = False
96
+ err = "TIMEOUT"
97
+ pt = ct = tt = 0
98
+ tok_sec = 0
99
+ content = ""
100
+ log(f"[#{req_id:03d}] {C.RED}TIMEOUT{C.RESET} {elapsed:6.1f}s", C.RED)
101
+ except Exception as e:
102
+ elapsed = time.perf_counter() - start
103
+ ok = False
104
+ err = str(e)[:70]
105
+ pt = ct = tt = 0
106
+ tok_sec = 0
107
+ content = ""
108
+ log(f"[#{req_id:03d}] {C.RED}ERROR{C.RESET} {elapsed:6.1f}s {err}", C.RED)
109
+
110
+ return {
111
+ "id": req_id,
112
+ "ok": ok,
113
+ "status": status if "status" in locals() else 0,
114
+ "elapsed": elapsed,
115
+ "prompt_tokens": pt,
116
+ "completion_tokens": ct,
117
+ "total_tokens": tt,
118
+ "tokens_per_sec": tok_sec,
119
+ "content_len": len(content),
120
+ "error": err,
121
+ }
122
+
123
+
124
+ # ---------------------------------------------------------------------------
125
+ # Report
126
+ # ---------------------------------------------------------------------------
127
+ def print_audit(results, wall_time, url, n_req, concurrency, warmup):
128
+ ok_r = [r for r in results if r["ok"]]
129
+ fail_r = [r for r in results if not r["ok"]]
130
+ elapsed_ok = [r["elapsed"] for r in ok_r]
131
+ tok_sec_ok = [r["tokens_per_sec"] for r in ok_r]
132
+ ct_ok = [r["completion_tokens"] for r in ok_r]
133
+ pt_ok = [r["prompt_tokens"] for r in ok_r]
134
+ total_prompt = sum(pt_ok)
135
+ total_completion = sum(ct_ok)
136
+
137
+ p95 = sorted(elapsed_ok)[int(len(elapsed_ok) * 0.95)] if elapsed_ok else 0
138
+
139
+ # Determine if we have slot info
140
+ concurrency_note = f"{concurrency} concurrent (asyncio)"
141
+
142
+ print()
143
+ log("=" * 68, C.MAGENTA, bright=True)
144
+ log(" STRESS TEST AUDIT REPORT", C.MAGENTA, bright=True)
145
+ log("=" * 68, C.MAGENTA, bright=True)
146
+ print()
147
+
148
+ rows = [
149
+ ("Target URL", url),
150
+ ("Description", DESCRIPTION),
151
+ ("Total requests", str(n_req)),
152
+ ("Concurrency", concurrency_note),
153
+ ("Warmup requests", str(warmup)),
154
+ ("Wall clock", f"{wall_time:.1f}s"),
155
+ ]
156
+ for k, v in rows:
157
+ log(f" {C.YELLOW}{k:<22}{S.RESET_ALL} {v}")
158
+
159
+ print()
160
+ log(f" {C.CYAN}{'RESULTS':<22}{S.RESET_ALL}", bright=True)
161
+ log(f" {'Succeeded':<22} {C.GREEN}{len(ok_r):>4}{S.RESET_ALL} ({len(ok_r)/max(len(results),1)*100:5.1f}%)", C.GREEN)
162
+ log(f" {'Failed':<22} {C.RED}{len(fail_r):>4}{S.RESET_ALL} ({len(fail_r)/max(len(results),1)*100:5.1f}%)", C.RED)
163
+
164
+ print()
165
+ log(f" {C.CYAN}{'LATENCY (seconds)':<22}{S.RESET_ALL}", bright=True)
166
+ if elapsed_ok:
167
+ log(f" {'Average':<22} {statistics.mean(elapsed_ok):>6.2f}")
168
+ log(f" {'Median':<22} {statistics.median(elapsed_ok):>6.2f}")
169
+ log(f" {'Min':<22} {C.GREEN}{min(elapsed_ok):>6.2f}{S.RESET_ALL}")
170
+ log(f" {'Max':<22} {C.RED}{max(elapsed_ok):>6.2f}{S.RESET_ALL}")
171
+ log(f" {'P95':<22} {p95:>6.2f}")
172
+
173
+ print()
174
+ log(f" {C.CYAN}{'TOKEN THROUGHPUT (tok/s)':<22}{S.RESET_ALL}", bright=True)
175
+ if tok_sec_ok:
176
+ log(f" {'Average':<22} {statistics.mean(tok_sec_ok):>6.1f}")
177
+ log(f" {'Median':<22} {statistics.median(tok_sec_ok):>6.1f}")
178
+ log(f" {'Min':<22} {C.RED}{min(tok_sec_ok):>6.1f}{S.RESET_ALL}")
179
+ log(f" {'Max':<22} {C.GREEN}{max(tok_sec_ok):>6.1f}{S.RESET_ALL}")
180
+
181
+ print()
182
+ log(f" {C.CYAN}{'TOKEN COUNTS':<22}{S.RESET_ALL}", bright=True)
183
+ log(f" {'Total prompt tokens':<22} {total_prompt:>6}")
184
+ log(f" {'Total completion tok':<22} {total_completion:>6}")
185
+ log(f" {'Total combined':<22} {total_prompt + total_completion:>6}")
186
+ if ct_ok:
187
+ log(f" {'Avg completion/req':<22} {statistics.mean(ct_ok):>6.1f}")
188
+
189
+ print()
190
+ log(f" {C.CYAN}{'SYSTEM THROUGHPUT':<22}{S.RESET_ALL}", bright=True)
191
+ agg_tok_sec = total_completion / wall_time if wall_time > 0 else 0
192
+ req_per_min = len(ok_r) / (wall_time / 60) if wall_time > 0 else 0
193
+ log(f" {'Completion tok/s':<22} {C.GREEN}{agg_tok_sec:>6.1f}{S.RESET_ALL}")
194
+ log(f" {'Requests/min':<22} {req_per_min:>6.1f}")
195
+
196
+ print()
197
+ if fail_r:
198
+ log(f" {C.RED}{'ERROR BREAKDOWN':<22}{S.RESET_ALL}", bright=True)
199
+ counts = {}
200
+ for r in fail_r:
201
+ key = r["error"] or "UNKNOWN"
202
+ counts[key] = counts.get(key, 0) + 1
203
+ for err, cnt in sorted(counts.items(), key=lambda x: -x[1]):
204
+ log(f" {err:<30} {C.RED}{cnt}{S.RESET_ALL}")
205
+
206
+ # ------------------------------------------------------------------
207
+ # Findings & recommendations
208
+ # ------------------------------------------------------------------
209
+ print()
210
+ log("=" * 68, C.MAGENTA, bright=True)
211
+ log(" ANALYSIS", C.MAGENTA, bright=True)
212
+ log("=" * 68, C.MAGENTA, bright=True)
213
+
214
+ findings = []
215
+
216
+ if elapsed_ok:
217
+ single = min(elapsed_ok)
218
+ tail = max(elapsed_ok)
219
+ if tail > single * 1.5:
220
+ findings.append(
221
+ f"{C.YELLOW}• High tail latency: max {tail:.1f}s vs min {single:.1f}s. "
222
+ f"Requests queue behind each other.{S.RESET_ALL}"
223
+ )
224
+
225
+ if fail_r:
226
+ findings.append(
227
+ f"{C.RED}• {len(fail_r)}/{len(results)} requests failed — "
228
+ f"check server health and resource limits.{S.RESET_ALL}"
229
+ )
230
+
231
+ findings.append(
232
+ f"{C.GREEN}• Server runs with 2 CPU cores, 2 parallel slots "
233
+ f"(--parallel 2), continuous batching enabled.{S.RESET_ALL}"
234
+ )
235
+
236
+ if agg_tok_speed := globals().get("agg_tok_sec", 0):
237
+ if agg_tok_speed < 5:
238
+ findings.append(
239
+ f"{C.YELLOW}• System throughput ({agg_tok_speed:.1f} tok/s) is low — "
240
+ f"limited by 2-core CPU constraint.{S.RESET_ALL}"
241
+ )
242
+ else:
243
+ findings.append(
244
+ f"{C.GREEN}• System throughput ({agg_tok_speed:.1f} tok/s) "
245
+ f"is acceptable for CPU inference.{S.RESET_ALL}"
246
+ )
247
+
248
+ for f_text in findings:
249
+ log(f" {f_text}")
250
+
251
+ print()
252
+ log("=" * 68, C.MAGENTA, bright=True)
253
+ print()
254
+
255
+
256
+ # ---------------------------------------------------------------------------
257
+ # Main
258
+ # ---------------------------------------------------------------------------
259
+ async def main():
260
+ parser = argparse.ArgumentParser(
261
+ description="BitNet b1.58 async stress test",
262
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
263
+ )
264
+ parser.add_argument("--url", default="http://localhost:8080/v1/chat/completions")
265
+ parser.add_argument("--requests", type=int, default=50)
266
+ parser.add_argument("--concurrency", type=int, default=10)
267
+ parser.add_argument("--timeout", type=int, default=120)
268
+ parser.add_argument("--warmup", type=int, default=0,
269
+ help="Number of warmup requests (results discarded)")
270
+ args = parser.parse_args()
271
+
272
+ print()
273
+ log("=" * 68, C.CYAN, bright=True)
274
+ log(" BitNet b1.58 Async Stress Test", C.CYAN, bright=True)
275
+ log(f" {args.requests} requests | {args.concurrency} concurrent "
276
+ f"| {args.warmup} warmup | {args.url}", C.CYAN)
277
+ log("=" * 68, C.CYAN, bright=True)
278
+ print()
279
+
280
+ connector = aiohttp.TCPConnector(limit=args.concurrency, force_close=True)
281
+ async with aiohttp.ClientSession(connector=connector) as session:
282
+ all_reqs = list(range(1, args.requests + 1 + args.warmup))
283
+
284
+ # Warmup phase
285
+ for req_id in all_reqs[:args.warmup]:
286
+ await do_request(session, req_id, args.url, args.timeout)
287
+
288
+ # Main test
289
+ wall_start = time.perf_counter()
290
+ tasks = [
291
+ asyncio.create_task(
292
+ do_request(session, req_id, args.url, args.timeout)
293
+ )
294
+ for req_id in all_reqs[args.warmup:]
295
+ ]
296
+ results = await asyncio.gather(*tasks)
297
+ wall_time = time.perf_counter() - wall_start
298
+
299
+ print_audit(results, wall_time, args.url, args.requests,
300
+ args.concurrency, args.warmup)
301
+
302
+
303
+ if __name__ == "__main__":
304
+ asyncio.run(main())