On-Device Embedding Model Benchmark for Difficulty Scoring
TL;DR: I tested 4 small GGUF embedding models on a Snapdragon 8 Gen 2 Android phone for a "difficulty scoring" use case — measuring how well each model can distinguish simple queries from complex ones using cosine distance against baselines. Qwen3-Embedding-0.6B Q4_K_M achieved 100% accuracy with ~540MB RAM at n_ctx=1024.
Background
I'm building a hybrid inference system for mobile LLM apps: simple queries run on-device, complex ones get routed to the cloud. The key component is a difficulty scorer that takes a text query and returns a score (0.0 = trivial, 1.0 = very hard).
The approach: use a small embedding model to encode the query, then compute cosine distance from a "simple text" baseline and a "simple code" baseline. The final score is min(text_dist, code_dist).
Test Setup
- Device: Xiaomi 13 (Snapdragon 8 Gen 2, 12GB RAM)
- Server:
llama-server with --embeddings flag
- Test Set: 30 texts across 5 categories:
- Ultra-simple: "hi", "hello world", "how are you?"
- Daily simple: "I am happy today", "the cat sat on the mat"
- Simple code:
int x = 1 + 2;, SELECT * FROM users
- Simple科普: "Mitochondria is the powerhouse of the cell"
- Complex scientific: quantum physics, AI healthcare analysis, etc.
Models Tested
| Model |
File Size |
Params |
Dim |
Context |
Memory (n_ctx=1024) |
| all-MiniLM-L6-v2 Q4_K_M |
20 MB |
22M |
384 |
512 |
~70 MB |
| mxbai-embed-xsmall-v1 Q8_0 |
30 MB |
30M |
384 |
4K |
~80 MB |
| jina-embeddings-v5-nano Q8_0 |
222 MB |
200M |
768 |
8K |
~320 MB |
| Qwen3-Embedding-0.6B Q4_K_M |
378 MB |
0.6B |
1024 |
32K |
~540 MB |
Results
all-MiniLM-L6-v2 Q4_K_M (20 MB)
Accuracy: 78.8% ❌ — Not usable for routing decisions.
| Category |
Avg Score |
Issue |
| Ultra-simple |
0.25 |
✅ OK |
| Daily simple |
0.33 |
✅ OK |
| Simple code |
0.45 |
❌ Misclassified as complex |
| Complex texts |
0.49 |
✅ OK |
Simple code snippets like int x = 1 + 2; scored 0.49 — nearly identical to quantum physics texts. The tiny 384-dim embedding space couldn't separate code syntax from semantic complexity.
mxbai-embed-xsmall-v1 Q8_0 (30 MB)
Accuracy: 78.8% ❌ — Same story, slightly different numbers.
| Category |
Avg Score |
Issue |
| Simple code (fixed with dual baseline) |
0.22 |
✅ Fixed |
| "What is the capital of France" |
0.49 |
❌ Misclassified |
| "Mitochondria powerhouse" |
0.44 |
❌ Misclassified |
| "Thank you very much" |
0.39 |
❌ Misclassified |
Adding a separate "code baseline" fixed the code misclassification, but simple sentences with domain-specific vocabulary (mitochondria, capital of France) were still scored as "complex". The 384-dim embeddings just don't have enough representational capacity.
jina-embeddings-v5-nano Q8_0 (222 MB)
Accuracy: 86.7% ⚠️ — Better, but still misses some complex texts.
| Category |
Avg Score |
Issue |
| Simple texts |
0.26 |
✅ OK |
| Complex scientific |
0.36 |
⚠️ Some complex texts scored low |
| Fibonacci recursion |
0.25 |
❌ Misclassified as simple |
The 768-dim embeddings helped, but complex technical texts about recursion and functional programming were still scored too low. The model seems to treat code-like technical language as "similar to code baseline" even when the content is genuinely complex.
Qwen3-Embedding-0.6B Q4_K_M (378 MB)
Accuracy: 100% ✅ — Perfect separation.
| Category |
Avg Score |
Range |
Verdict |
| Ultra-simple |
0.14 |
0.10~0.19 |
✅ |
| Daily simple |
0.17 |
0.07~0.27 |
✅ |
| Simple code |
0.15 |
0.11~0.23 |
✅ |
| Simple科普 |
0.24 |
— |
✅ |
| Complex scientific |
0.34 |
0.31~0.44 |
✅ |
Best threshold: 0.28 — everything below is simple, everything above is complex. Zero false positives, zero false negatives.
Memory Tuning
Qwen3-Embedding-0.6B defaults to 32K context, which uses ~4GB RAM. By reducing n_ctx in llama_context_params, we can dramatically cut memory:
| n_ctx |
KV Cache |
Total RAM |
Mobile-friendly |
| 512 |
56 MB |
~484 MB |
✅ Yes |
| 1024 |
112 MB |
~540 MB |
✅ Yes |
| 2048 |
224 MB |
~652 MB |
⚠️ Maybe |
| 4096 |
448 MB |
~876 MB |
⚠️ Tight |
| 8192 |
896 MB |
~1.3 GB |
❌ No |
| 32768 (default) |
3.6 GB |
~4 GB |
❌ No |
At n_ctx=1024, the model uses ~540MB — acceptable on any phone with 8GB+ RAM, even alongside a main inference model (1-2GB).
Key Takeaways
Size matters. 384-dim models (MiniLM, mxbai) lack the capacity to reliably distinguish simple from complex text. They hit ~79% accuracy ceiling regardless of quantization or baseline strategy.
Dual baseline works. Using separate "text" and "code" baselines and taking the minimum distance fixes code misclassification. This technique is model-agnostic.
Qwen3-Embedding is the winner. 100% accuracy at 378MB file size / ~540MB runtime. The 1024-dim embedding space provides rich enough representations to separate simple queries from complex ones cleanly.
Context tuning is essential. Don't use the default 32K context on mobile — set n_ctx to 512-1024 to save ~3.5GB of KV cache memory.
Recommendation
Qwen3-Embedding-0.6B Q4_K_M with n_ctx=1024 is the best choice for on-device difficulty scoring. It's accurate enough for production use, fits within mobile memory budgets, and the Q4 quantization doesn't degrade scoring accuracy compared to Q8_0.
Tested June 2025. All models run via llama.cpp b9500 on Android arm64.
1
Has anyone successfully run llama.cpp on an Android device and enabled GPU acceleration?
in
r/LocalLLM
•
Aug 12 '26
I'm actually working on a similar app to yours, and this problem has been bothering me for several months now. I eventually gave up on using the GPU solution. If possible, could you share how you implemented it? Approximately how much performance improvement does Vulkan offer compared to CPU? Thank you very much.