[2.1.0]
ENTITY's own benchmark disproved ENTITY's flagship optimization, and found two that actually work. The v2.0.0 headline credited a +121% decode gain to big-core affinity pinning. The three-arm ablation shipped in this release measured it: the pinning earns ~0%. The gain is the thread count. Meanwhile Arm's KleidiAI — the reason seven CPU backend variants ship — was never executing at all, because it has kernels only for Q4_0/Q8_0 and every published benchmark used Q3_K_L.
Added
- Three-arm benchmark ablation. A third configuration between naïve and Auto: threads-only,
Auto's derived thread count with core affinity switched off (no
sched_setaffinity, no pinned thread pool). It is the in-app equivalent of an upstreamllama.cpp -t Nrun, and it is what lets the result be attributed instead of assumed. The app prints the split under its results table. pinCoresthroughInferenceEngine.applyConfig→ JNIconfigure()→g_pin_cores. Defaults to true, so every shipped path is unchanged; only the ablation arm turns it off.unpin_all_cores()clears any mask inherited from the previous arm.- Effective-affinity logging. Each arm logs the CPU mask the kernel actually applied
(
effective cpusin logcat), so a silently failedsched_setaffinitycannot masquerade as "pinning earns nothing". - KleidiAI advisor.
FileType.kleidiAiAcceleratedgates the claim on the loaded quantization. The model-info card used to print "KleidiAI" unconditionally — telling users their model was Arm-accelerated when it was not. It now says so, and quantifies what a non-accelerated quant costs. - Per-core CPU frequency sampling in the benchmark telemetry and CSV, plus perf/little core-clock rows in the results table.
benchmarks/plot_results.pyand the published charts;device-result-template.csvgainskleidiai_acceleratedandpinning_decode_delta_pct.
Changed
- Prompt processing no longer widens to all cores — it was a regression. Auto used split thread pools to give prompt eval every online core, assuming a compute-bound phase wants all the hardware. An A55 is about a third of an A78's throughput, so the widened pool finished late and every GEMM waited on the stragglers. Measured on 1B Q4_0: prompt on 4 fast cores 135 tok/s, spread across all 8 86 tok/s. Both phases now run on the fast-core thread count.
- Benchmark, README, FAQ, ARCHITECTURE, OPTIMIZATIONS and the release notes no longer credit the speed-up to core pinning. The affinity code still ships — it is free, and another SoC may answer differently — but it is not what makes ENTITY fast.
Fixed
- CSV export silently wrote 0-byte files. The system file picker comes to the foreground while a
multi-gigabyte model is resident; Android kills the activity behind it; the recreated instance had
no result, so
buildCsv(lastResult ?: return)bailed out — while DocumentsUI had already created the file and the app still toasted "CSV exported". This is why no raw per-pass CSV ever survived to be published. The CSV is now staged to cache before the picker opens and carried throughonSaveInstanceState; a lost result reports an error instead of writing nothing. buildFeatures.buildConfigenabled.BenchmarkActivityreadsBuildConfig.VERSION_NAMEfor CSV provenance, but AGP 8 does not generateBuildConfigunless asked, so the app did not compile.
Measured (CMF Phone 1, Dimensity 7300, unplugged)
| v2.0.0 (Q3_K_L, widened prompt pool) | v2.1.0 (Q4_0, fast-core prompt) | |
|---|---|---|
| Prompt throughput | 38.3 tok/s | 133 tok/s |
| Time to first token | 13,440 ms | 3,918 ms |
| Decode throughput | 16.7 tok/s | 14.7 tok/s |
Time-to-first-token improves 3.4×. Decode gives up ~12%, the bandwidth cost of the larger quantization. Full record, graphs and limits: BENCHMARKS.md.
Log in or sign up for Devpost to join the conversation.