Ronti Patange commited on
Commit
b8085e5
Β·
1 Parent(s): fd7e0ff

Replace approx-sign tildes with U+2248 to avoid GFM strikethrough misparse

Browse files
Files changed (1) hide show
  1. 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 (~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,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 ~15 min, shorter
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 ~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,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% | ~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,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, ~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,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 ~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,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 ~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,13 +201,13 @@ model reaches the GPU:
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,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 ~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,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Γ— | **~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,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 ~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,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 ~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,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 ~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,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 (~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.**
 
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.**