Spaces:
Sleeping
Sleeping
Ronti Patange commited on
Commit Β·
b8085e5
1
Parent(s): fd7e0ff
Replace approx-sign tildes with U+2248 to avoid GFM strikethrough misparse
Browse files- BENCHMARK.md +29 -29
BENCHMARK.md
CHANGED
|
@@ -24,7 +24,7 @@ No discrete GPU, no CUDA. All acceleration is via the integrated Arc GPU.
|
|
| 24 |
|
| 25 |
Two backends were benchmarked, both Vulkan on the Arc iGPU:
|
| 26 |
|
| 27 |
-
- **Model:** Qwen2.5-VL-3B-Instruct, q4_K_M (
|
| 28 |
- **Backend A β Ollama** (IPEX-LLM portable build, Vulkan): `OLLAMA_IGPU_ENABLE=1`
|
| 29 |
(integrated GPUs are dropped by default). API: native `/api/chat`, `keep_alive: -1`.
|
| 30 |
- **Backend B β llama.cpp** (`llama-server`, Vulkan build `b9966`): runs the GGUF +
|
|
@@ -37,7 +37,7 @@ difference** β see the latency ladder below for why.
|
|
| 37 |
|
| 38 |
HuggingFace `rajistics/indian_food_images`, test split.
|
| 39 |
20 classes, 941 test images. Benchmark samples **10 images/class = 200 images**,
|
| 40 |
-
downloaded to disk before the run (the HF signed URLs expire
|
| 41 |
than a full CPU run).
|
| 42 |
|
| 43 |
**Task:** constrained top-1 classification. The model is shown one dish photo
|
|
@@ -109,7 +109,7 @@ clearly matches." Result: **74% β 86% (+12 points)**, and the magnets collapse
|
|
| 109 |
| pav_bhaji | 8/10 | 6/10 | β2 (anti-default cost) |
|
| 110 |
|
| 111 |
Wrong guesses landing on `pav_bhaji`: **15 β 1**. On `dal_makhani`: **13 β 2**.
|
| 112 |
-
Cost: the longer prompt adds tokens, so latency rose
|
| 113 |
trade: **+12 accuracy points for +1.3s** β worth it for an accuracy-critical app.
|
| 114 |
`paani_puri` (2/10) is the one class descriptions did not rescue.
|
| 115 |
|
|
@@ -120,10 +120,10 @@ iGPU. Only the model changes.
|
|
| 120 |
|
| 121 |
| Model | Size | Top-1 | Latency (median) | Notes |
|
| 122 |
|---|---|---|---|---|
|
| 123 |
-
| moondream (1.8B) | 1.7 GB | 37.0% |
|
| 124 |
| Qwen2.5-VL-3B-Instruct | 2.0+1.3 GB | 86.0% | 6.5 s | baseline for the prompt-engineering work above |
|
| 125 |
| **Qwen3-VL-4B-Instruct** | 2.4+0.8 GB | **93.0%** | 5.8 s | newer generation beats the older, bigger... |
|
| 126 |
-
| Gemma-4-E4B-it | 4.6+0.9 GB |
|
| 127 |
|
| 128 |
**Qwen3-VL-4B vs Qwen2.5-VL-3B (+7 points, same-or-better latency):** a newer,
|
| 129 |
slightly larger model decisively beat the older one, confirming that "newer
|
|
@@ -145,7 +145,7 @@ not a constrained classifier.
|
|
| 145 |
|
| 146 |
**Gemma-4-E4B β unstable on this hardware, not benchmarked to completion.**
|
| 147 |
Two failures on two configs: with 4 parallel slots the `llama-server` process
|
| 148 |
-
crashed outright around image 126 (VRAM/memory pressure from the larger,
|
| 149 |
model); with `--parallel 1` it did not crash but **hung** β one request stalled
|
| 150 |
for 5 minutes before being force-cancelled, then every request after it queued
|
| 151 |
and also stalled. Per-token eval time was visibly degrading right before the hang
|
|
@@ -154,8 +154,8 @@ this architecture, not a benchmark or prompt problem. It also required an
|
|
| 154 |
explicit `chat_template_kwargs: {"enable_thinking": false}` API flag β Gemma 4 is
|
| 155 |
a reasoning model that writes a hidden chain-of-thought into a separate
|
| 156 |
`reasoning_content` field, and without disabling that it returns empty answers
|
| 157 |
-
under a short `max_tokens` budget. The
|
| 158 |
-
first crash were 25/32 (
|
| 159 |
but two failures on two different configs is a stability finding, not noise.
|
| 160 |
|
| 161 |
### Few-shot prompting β negative result
|
|
@@ -179,10 +179,10 @@ upgrade above, which fixed the same class for free.
|
|
| 179 |
|
| 180 |
llama-server exposes 4 parallel slots. For **Qwen2.5-VL-3B**, firing 4 concurrent
|
| 181 |
requests instead of one-at-a-time processes the 200-image set in **117 s
|
| 182 |
-
(0.58 s/image)** vs
|
| 183 |
85β86% accuracy**. A single 768px request does not saturate the Arc's 112 EUs;
|
| 184 |
batching fills the GPU, so per-call latency under load actually drops (median
|
| 185 |
-
|
| 186 |
per-image number.
|
| 187 |
|
| 188 |
**This does not generalize to the larger model.** The same experiment on
|
|
@@ -201,13 +201,13 @@ model reaches the GPU:
|
|
| 201 |
|
| 202 |
| Setup | Per image (warm) | Speedup | Vision encoder |
|
| 203 |
|---|---|---|---|
|
| 204 |
-
| CPU only |
|
| 205 |
-
| Ollama, Arc (Vulkan) |
|
| 206 |
-
| **llama.cpp, Arc (Vulkan)** | **
|
| 207 |
|
| 208 |
llama.cpp full run: min 1.9 / median 5.2 / mean 5.2 / max 8.7 s (199 warm calls).
|
| 209 |
|
| 210 |
-
### Why Ollama was stuck at
|
| 211 |
|
| 212 |
The Ollama server log shows it refuses to put the vision projector on the GPU:
|
| 213 |
|
|
@@ -219,19 +219,19 @@ llama-server ... --no-mmproj-offload --image-min-tokens 1024
|
|
| 219 |
On a **shared-memory** iGPU, Ollama's heuristic keeps the **vision encoder (mmproj)
|
| 220 |
on the CPU** and offloads only the LLM text layers. Single-image latency is
|
| 221 |
dominated by vision encoding (forced to β₯1024 vision tokens), so accelerating only
|
| 222 |
-
the text layers barely helps β hence
|
| 223 |
|
| 224 |
**The fix:** run `llama-server` directly with the GGUF + mmproj and `-ngl 99`, which
|
| 225 |
offloads **both** the text layers **and** the vision projector to the Arc GPU. That
|
| 226 |
-
single change moved the bottleneck onto the GPU and cut latency from
|
| 227 |
-
a **
|
| 228 |
|
| 229 |
**Diagnostic note:** `ollama ps` reports `100% GPU` in the Ollama case, but that only
|
| 230 |
counts the LLM text weights in VRAM. It does **not** reveal the vision encoder is on
|
| 231 |
CPU β that is only visible in the verbose startup log. For a VLM, "100% GPU" from
|
| 232 |
`ollama ps` is necessary but not sufficient.
|
| 233 |
|
| 234 |
-
**Takeaway:** with the vision encoder on the GPU, a 3B VLM is a **
|
| 235 |
machine on this integrated Arc β usable for a real app, not just a demo.
|
| 236 |
|
| 237 |
## Cloud: HF ZeroGPU (A10G) vs local Arc iGPU
|
|
@@ -244,7 +244,7 @@ the identical 200-image, disambiguated-prompt benchmark against it.
|
|
| 244 |
|---|---|---|
|
| 245 |
| Accuracy | 93.0% (186/200) | **92.8%** (167/180 valid) |
|
| 246 |
| Latency (median) | 5.8 s | **2.9 s** |
|
| 247 |
-
| Speedup | 1Γ | **
|
| 248 |
|
| 249 |
Accuracy is identical within noise β quantization (q4 vs BF16) made no
|
| 250 |
measurable difference here. Latency is roughly **2Γ faster on the A10G**, a real
|
|
@@ -300,20 +300,20 @@ exact same disambiguated-classification question as the local runs.
|
|
| 300 |
The first full run scored only 83.5%, entirely explained by **20 quota-exhausted
|
| 301 |
errors on the last two classes** (pizza, samosa β last in iteration order): HF's
|
| 302 |
free ZeroGPU tier grants a limited number of GPU-seconds per day, and it ran out
|
| 303 |
-
after
|
| 304 |
the true 92.8% above. For any bulk/benchmark use of the free tier, budget for
|
| 305 |
this β HF's own error message point to HF PRO for more daily quota.
|
| 306 |
|
| 307 |
## Reproduce
|
| 308 |
|
| 309 |
-
**Backend A β Ollama (Arc,
|
| 310 |
|
| 311 |
```
|
| 312 |
serve-arc.bat # from ollama-ipex/: sets OLLAMA_IGPU_ENABLE=1, Ollama on Arc
|
| 313 |
python bench_hf.py 10 # 10 images/class = 200 images -> bench_hf_results.jsonl
|
| 314 |
```
|
| 315 |
|
| 316 |
-
**Backend B β llama.cpp (Arc,
|
| 317 |
|
| 318 |
```
|
| 319 |
llama-cpp\llama-server.exe ^
|
|
@@ -323,7 +323,7 @@ llama-cpp\llama-server.exe ^
|
|
| 323 |
|
| 324 |
python bench_llamacpp.py # bare prompt -> 74% (bench_hf_results_llamacpp.jsonl)
|
| 325 |
python bench_llamacpp_v2.py # disambiguated -> 86% (bench_hf_results_llamacpp_v2.jsonl)
|
| 326 |
-
python bench_parallel.py 4 # v2 prompt, x4 -> 86%, 200 imgs in
|
| 327 |
```
|
| 328 |
|
| 329 |
The GGUFs are from `unsloth/Qwen2.5-VL-3B-Instruct-GGUF` (Q4_K_M + mmproj-F16).
|
|
@@ -354,7 +354,7 @@ python3.13 bench_zerogpu.py <hf_token_file> # -> 92.8% valid, 2.9s median (ben
|
|
| 354 |
Requires `app.py` deployed to the `RonitP1/KhanaVision` Space with the
|
| 355 |
`RAW_PROMPT:` prefix convention (see Cloud section above) so the benchmark's
|
| 356 |
constrained-classification prompt bypasses the app's normal JSON-analysis
|
| 357 |
-
wrapper. Free-tier ZeroGPU quota covers
|
| 358 |
|
| 359 |
All benchmarks reuse cached images in `bench_hf_images/` and write per-image
|
| 360 |
results to their respective `.jsonl`.
|
|
@@ -365,13 +365,13 @@ results to their respective `.jsonl`.
|
|
| 365 |
|---|---|
|
| 366 |
| Ollama β llama.cpp (mmproj on GPU) | latency 52 s β 5 s, accuracy unchanged |
|
| 367 |
| bare prompt β disambiguated prompt | accuracy 74% β 86%, latency +1.3 s |
|
| 368 |
-
| Qwen2.5-VL-3B β Qwen3-VL-4B | accuracy 86% β 93%, latency unchanged (
|
| 369 |
| few-shot reference images | **negative** β 2/10 β 1/10 (token-budget conflict, see above) |
|
| 370 |
-
| serial β parallel Γ4 (Qwen2.5-VL-3B) | 200-img wall time
|
| 371 |
| serial β parallel Γ4 (Qwen3-VL-4B) | **no gain** β model already saturates the GPU at concurrency 1 |
|
| 372 |
| flash-attn (`-fa on`) | neutral here (vision-prefill bound, not attention bound) |
|
| 373 |
-
| local Arc β HF ZeroGPU A10G | latency 5.8 s β 2.9 s (
|
| 374 |
|
| 375 |
**Best overall config: Qwen3-VL-4B + disambiguated prompt = 93% top-1.**
|
| 376 |
-
Local (Arc iGPU, free, always-on): **
|
| 377 |
-
Cloud (HF ZeroGPU, free,
|
|
|
|
| 24 |
|
| 25 |
Two backends were benchmarked, both Vulkan on the Arc iGPU:
|
| 26 |
|
| 27 |
+
- **Model:** Qwen2.5-VL-3B-Instruct, q4_K_M (β2.0 GB) + `mmproj-F16` vision projector (β1.3 GB)
|
| 28 |
- **Backend A β Ollama** (IPEX-LLM portable build, Vulkan): `OLLAMA_IGPU_ENABLE=1`
|
| 29 |
(integrated GPUs are dropped by default). API: native `/api/chat`, `keep_alive: -1`.
|
| 30 |
- **Backend B β llama.cpp** (`llama-server`, Vulkan build `b9966`): runs the GGUF +
|
|
|
|
| 37 |
|
| 38 |
HuggingFace `rajistics/indian_food_images`, test split.
|
| 39 |
20 classes, 941 test images. Benchmark samples **10 images/class = 200 images**,
|
| 40 |
+
downloaded to disk before the run (the HF signed URLs expire β15 min, shorter
|
| 41 |
than a full CPU run).
|
| 42 |
|
| 43 |
**Task:** constrained top-1 classification. The model is shown one dish photo
|
|
|
|
| 109 |
| pav_bhaji | 8/10 | 6/10 | β2 (anti-default cost) |
|
| 110 |
|
| 111 |
Wrong guesses landing on `pav_bhaji`: **15 β 1**. On `dal_makhani`: **13 β 2**.
|
| 112 |
+
Cost: the longer prompt adds tokens, so latency rose β5.2s β β6.5s median. Net
|
| 113 |
trade: **+12 accuracy points for +1.3s** β worth it for an accuracy-critical app.
|
| 114 |
`paani_puri` (2/10) is the one class descriptions did not rescue.
|
| 115 |
|
|
|
|
| 120 |
|
| 121 |
| Model | Size | Top-1 | Latency (median) | Notes |
|
| 122 |
|---|---|---|---|---|
|
| 123 |
+
| moondream (1.8B) | 1.7 GB | 37.0% | β17 s (n=200) | scene captioner, not a fine-grained dish classifier β see below |
|
| 124 |
| Qwen2.5-VL-3B-Instruct | 2.0+1.3 GB | 86.0% | 6.5 s | baseline for the prompt-engineering work above |
|
| 125 |
| **Qwen3-VL-4B-Instruct** | 2.4+0.8 GB | **93.0%** | 5.8 s | newer generation beats the older, bigger... |
|
| 126 |
+
| Gemma-4-E4B-it | 4.6+0.9 GB | β78% (partial, crashed) | β3β5 s (partial) | **unstable on Arc/Vulkan β see below** |
|
| 127 |
|
| 128 |
**Qwen3-VL-4B vs Qwen2.5-VL-3B (+7 points, same-or-better latency):** a newer,
|
| 129 |
slightly larger model decisively beat the older one, confirming that "newer
|
|
|
|
| 145 |
|
| 146 |
**Gemma-4-E4B β unstable on this hardware, not benchmarked to completion.**
|
| 147 |
Two failures on two configs: with 4 parallel slots the `llama-server` process
|
| 148 |
+
crashed outright around image 126 (VRAM/memory pressure from the larger, β5.6 GB
|
| 149 |
model); with `--parallel 1` it did not crash but **hung** β one request stalled
|
| 150 |
for 5 minutes before being force-cancelled, then every request after it queued
|
| 151 |
and also stalled. Per-token eval time was visibly degrading right before the hang
|
|
|
|
| 154 |
explicit `chat_template_kwargs: {"enable_thinking": false}` API flag β Gemma 4 is
|
| 155 |
a reasoning model that writes a hidden chain-of-thought into a separate
|
| 156 |
`reasoning_content` field, and without disabling that it returns empty answers
|
| 157 |
+
under a short `max_tokens` budget. The β32 clean responses obtained before the
|
| 158 |
+
first crash were 25/32 (β78%) correct, suggesting real accuracy is competitive β
|
| 159 |
but two failures on two different configs is a stability finding, not noise.
|
| 160 |
|
| 161 |
### Few-shot prompting β negative result
|
|
|
|
| 179 |
|
| 180 |
llama-server exposes 4 parallel slots. For **Qwen2.5-VL-3B**, firing 4 concurrent
|
| 181 |
requests instead of one-at-a-time processes the 200-image set in **117 s
|
| 182 |
+
(0.58 s/image)** vs β21 min serial β an **β11Γ wall-time speedup at the same
|
| 183 |
85β86% accuracy**. A single 768px request does not saturate the Arc's 112 EUs;
|
| 184 |
batching fills the GPU, so per-call latency under load actually drops (median
|
| 185 |
+
β2.1 s). This speeds up *bulk work*; a single-photo app still sees the β6.5 s
|
| 186 |
per-image number.
|
| 187 |
|
| 188 |
**This does not generalize to the larger model.** The same experiment on
|
|
|
|
| 201 |
|
| 202 |
| Setup | Per image (warm) | Speedup | Vision encoder |
|
| 203 |
|---|---|---|---|
|
| 204 |
+
| CPU only | β75 s | 1.0Γ | CPU |
|
| 205 |
+
| Ollama, Arc (Vulkan) | β52 s (median 52.5) | β1.5Γ | **CPU** (forced) |
|
| 206 |
+
| **llama.cpp, Arc (Vulkan)** | **β5 s (median 5.2)** | **β14Γ** | **GPU** |
|
| 207 |
|
| 208 |
llama.cpp full run: min 1.9 / median 5.2 / mean 5.2 / max 8.7 s (199 warm calls).
|
| 209 |
|
| 210 |
+
### Why Ollama was stuck at β1.5Γ β and llama.cpp fixed it
|
| 211 |
|
| 212 |
The Ollama server log shows it refuses to put the vision projector on the GPU:
|
| 213 |
|
|
|
|
| 219 |
On a **shared-memory** iGPU, Ollama's heuristic keeps the **vision encoder (mmproj)
|
| 220 |
on the CPU** and offloads only the LLM text layers. Single-image latency is
|
| 221 |
dominated by vision encoding (forced to β₯1024 vision tokens), so accelerating only
|
| 222 |
+
the text layers barely helps β hence β1.5Γ.
|
| 223 |
|
| 224 |
**The fix:** run `llama-server` directly with the GGUF + mmproj and `-ngl 99`, which
|
| 225 |
offloads **both** the text layers **and** the vision projector to the Arc GPU. That
|
| 226 |
+
single change moved the bottleneck onto the GPU and cut latency from β52 s to β5 s β
|
| 227 |
+
a **β10Γ win over Ollama, β14Γ over CPU** β at identical accuracy.
|
| 228 |
|
| 229 |
**Diagnostic note:** `ollama ps` reports `100% GPU` in the Ollama case, but that only
|
| 230 |
counts the LLM text weights in VRAM. It does **not** reveal the vision encoder is on
|
| 231 |
CPU β that is only visible in the verbose startup log. For a VLM, "100% GPU" from
|
| 232 |
`ollama ps` is necessary but not sufficient.
|
| 233 |
|
| 234 |
+
**Takeaway:** with the vision encoder on the GPU, a 3B VLM is a **β5 s/image**
|
| 235 |
machine on this integrated Arc β usable for a real app, not just a demo.
|
| 236 |
|
| 237 |
## Cloud: HF ZeroGPU (A10G) vs local Arc iGPU
|
|
|
|
| 244 |
|---|---|---|
|
| 245 |
| Accuracy | 93.0% (186/200) | **92.8%** (167/180 valid) |
|
| 246 |
| Latency (median) | 5.8 s | **2.9 s** |
|
| 247 |
+
| Speedup | 1Γ | **β2Γ** |
|
| 248 |
|
| 249 |
Accuracy is identical within noise β quantization (q4 vs BF16) made no
|
| 250 |
measurable difference here. Latency is roughly **2Γ faster on the A10G**, a real
|
|
|
|
| 300 |
The first full run scored only 83.5%, entirely explained by **20 quota-exhausted
|
| 301 |
errors on the last two classes** (pizza, samosa β last in iteration order): HF's
|
| 302 |
free ZeroGPU tier grants a limited number of GPU-seconds per day, and it ran out
|
| 303 |
+
after β180 calls at β3 s of GPU time each. Excluding those 20 non-answers gives
|
| 304 |
the true 92.8% above. For any bulk/benchmark use of the free tier, budget for
|
| 305 |
this β HF's own error message point to HF PRO for more daily quota.
|
| 306 |
|
| 307 |
## Reproduce
|
| 308 |
|
| 309 |
+
**Backend A β Ollama (Arc, β52 s/img):**
|
| 310 |
|
| 311 |
```
|
| 312 |
serve-arc.bat # from ollama-ipex/: sets OLLAMA_IGPU_ENABLE=1, Ollama on Arc
|
| 313 |
python bench_hf.py 10 # 10 images/class = 200 images -> bench_hf_results.jsonl
|
| 314 |
```
|
| 315 |
|
| 316 |
+
**Backend B β llama.cpp (Arc, β5β6 s/img, best accuracy):**
|
| 317 |
|
| 318 |
```
|
| 319 |
llama-cpp\llama-server.exe ^
|
|
|
|
| 323 |
|
| 324 |
python bench_llamacpp.py # bare prompt -> 74% (bench_hf_results_llamacpp.jsonl)
|
| 325 |
python bench_llamacpp_v2.py # disambiguated -> 86% (bench_hf_results_llamacpp_v2.jsonl)
|
| 326 |
+
python bench_parallel.py 4 # v2 prompt, x4 -> 86%, 200 imgs in β2 min (bench_hf_results_parallel.jsonl)
|
| 327 |
```
|
| 328 |
|
| 329 |
The GGUFs are from `unsloth/Qwen2.5-VL-3B-Instruct-GGUF` (Q4_K_M + mmproj-F16).
|
|
|
|
| 354 |
Requires `app.py` deployed to the `RonitP1/KhanaVision` Space with the
|
| 355 |
`RAW_PROMPT:` prefix convention (see Cloud section above) so the benchmark's
|
| 356 |
constrained-classification prompt bypasses the app's normal JSON-analysis
|
| 357 |
+
wrapper. Free-tier ZeroGPU quota covers β180 calls/day; budget accordingly.
|
| 358 |
|
| 359 |
All benchmarks reuse cached images in `bench_hf_images/` and write per-image
|
| 360 |
results to their respective `.jsonl`.
|
|
|
|
| 365 |
|---|---|
|
| 366 |
| Ollama β llama.cpp (mmproj on GPU) | latency 52 s β 5 s, accuracy unchanged |
|
| 367 |
| bare prompt β disambiguated prompt | accuracy 74% β 86%, latency +1.3 s |
|
| 368 |
+
| Qwen2.5-VL-3B β Qwen3-VL-4B | accuracy 86% β 93%, latency unchanged (β6 s) |
|
| 369 |
| few-shot reference images | **negative** β 2/10 β 1/10 (token-budget conflict, see above) |
|
| 370 |
+
| serial β parallel Γ4 (Qwen2.5-VL-3B) | 200-img wall time β21 min β β2 min |
|
| 371 |
| serial β parallel Γ4 (Qwen3-VL-4B) | **no gain** β model already saturates the GPU at concurrency 1 |
|
| 372 |
| flash-attn (`-fa on`) | neutral here (vision-prefill bound, not attention bound) |
|
| 373 |
+
| local Arc β HF ZeroGPU A10G | latency 5.8 s β 2.9 s (β2Γ), accuracy unchanged |
|
| 374 |
|
| 375 |
**Best overall config: Qwen3-VL-4B + disambiguated prompt = 93% top-1.**
|
| 376 |
+
Local (Arc iGPU, free, always-on): **β5.8 s/image.**
|
| 377 |
+
Cloud (HF ZeroGPU, free, β180 calls/day quota): **β2.9 s/image.**
|