Benchmarking armv8 Instruction Sets

I've been testing various old and newer Android phones recently, as a side effect of some programming I'm doing, and noticing how oddly the armv8 instruction set architecture extensions can behave when used in anger.

Specifically, I'm testing whisper.cpp to see if I could get a simple app to record audio and convert it to text locally on my phone. The speech-to-text models are good, how much CPU they use and how fast they can convert is important, i.e. realtime / faster than realtime or not.

Here's what I see.

On the only device I have that can run it, armv9.0_1 is the worst or near-worst on every model, it's even slower than NEON:

model armv8.0_1 armv8.2_2 armv9.0_1 (auto-selected)
base 7880 4340 5970
base.en-q5 4678 3256 7561
tiny-q5 2047 1343 2923
tiny.en-q5 1950 1593 2943

Running Arm's Scalable Vector Extensions 2 (SVE2 and imm8) isn't worth it on some smartphones.

Here's another look at the numbers with a 3-run average.

model armv8.2_2 armv9.0_1 (auto-selected)
base 1470 3687
base.en-q5 1818 3999
tiny-q5 1001 2061

Google, in their infinite wisdom, wants people to use their Tensor Processing Units, but they don't expose them in any friendly way to developers via the Android NDK.

In fact, it's so bad, that even internally their people don't know how to use them! This is a well-run company, I tells ya!

See: "Access to some functionality, like the chip’s integrated tensor processing unit, remains elusive."

George Hotz is right, these companies should hand over the ISA reference manuals and build stable enough drivers to let people compile and run code as close to the metal as possible.

A long time ago, I also had fun getting code to compile and run on the Qualcomm Hexagon DSP built into every phone using a Snapdragon processor. Their lockdown of the cDSP via FastRPC was kind of a nightmare, and only a small handful of very modern (for the time) phones, would allow any user processing on those devices at all.

Anyways, here are the whisper.cpp benchmark results on the devices I could get my hands on, using the JFK speech sample. ("Ask not what your country can do for you...")

The INFER(ms) column is the one to pay attention to. In absolute terms, it tells you whether the processor could handle real-time or near real-time processing.

The speech clip itself is 11 seconds long, so if the DECODE + INFER fields combined are less than that, then there's probably a good chance it will work.

max@ubuntu2604:~$ cat benchmark-20260702-105502.txt
DEVICE       ABI          MODEL         BACKEND       DECODE(ms)  LOAD(ms)  INFER(ms)  N
Nexus 5      armeabi-v7a  base          CPU (linked)  762         1987      14885      3
Nexus 5      armeabi-v7a  base.en-q5_1  CPU (linked)  931         1293      21031      3
Nexus 5      armeabi-v7a  tiny-q5_1     CPU (linked)  909         847       10020      3
Nexus 5      armeabi-v7a  tiny.en-q5_1  CPU (linked)  908         827       10077      3
Huawei M2    arm64-v8a    base          armv8.0_1     535         983       11802      3
Huawei M2    arm64-v8a    base.en-q5_1  armv8.0_1     508         482       10271      3
Huawei M2    arm64-v8a    tiny-q5_1     armv8.0_1     587         374       4745       3
Huawei M2    arm64-v8a    tiny.en-q5_1  armv8.0_1     535         363       4775       3
Pixel 2      arm64-v8a    base          armv8.0_1     420         310       8078       3
Pixel 2      arm64-v8a    base.en-q5_1  armv8.0_1     436         216       5898       3
Pixel 2      arm64-v8a    tiny-q5_1     armv8.0_1     522         195       3100       3
Pixel 2      arm64-v8a    tiny.en-q5_1  armv8.0_1     520         201       3080       3
Pixel 3a     arm64-v8a    base          armv8.0_1     1214        339       9421       3
Pixel 3a     arm64-v8a    base.en-q5_1  armv8.0_1     1323        248       7263       3
Pixel 3a     arm64-v8a    tiny-q5_1     armv8.0_1     1340        195       3328       3
Pixel 3a     arm64-v8a    tiny.en-q5_1  armv8.0_1     1336        190       3307       3
Pixel 5      arm64-v8a    base          armv8.0_1     1456        286       7497       3
Pixel 5      arm64-v8a    base          armv8.2_2     1396        256       3469       3
Pixel 5      arm64-v8a    base.en-q5_1  armv8.0_1     1395        257       5929       3
Pixel 5      arm64-v8a    base.en-q5_1  armv8.2_2     1408        223       4779       3
Pixel 5      arm64-v8a    tiny-q5_1     armv8.0_1     1402        187       2522       3
Pixel 5      arm64-v8a    tiny-q5_1     armv8.2_2     1391        196       2061       3
Pixel 5      arm64-v8a    tiny.en-q5_1  armv8.0_1     1511        161       2583       3
Pixel 5      arm64-v8a    tiny.en-q5_1  armv8.2_2     1556        156       2221       3
Mi 11        arm64-v8a    base          armv8.0_1     300         130       3864       3
Mi 11        arm64-v8a    base          armv8.2_2     301         133       1472       3
Mi 11        arm64-v8a    base.en-q5_1  armv8.0_1     301         86        2575       3
Mi 11        arm64-v8a    base.en-q5_1  armv8.2_2     305         88        2052       3
Mi 11        arm64-v8a    tiny-q5_1     armv8.0_1     287         66        1148       3
Mi 11        arm64-v8a    tiny-q5_1     armv8.2_2     291         66        912        3
Mi 11        arm64-v8a    tiny.en-q5_1  armv8.0_1     285         63        1152       3
Mi 11        arm64-v8a    tiny.en-q5_1  armv8.2_2     300         65        918        3
Pixel 9 Pro  arm64-v8a    base          armv8.0_1     980         218       3833       3
Pixel 9 Pro  arm64-v8a    base          armv8.2_2     875         199       1470       3
Pixel 9 Pro  arm64-v8a    base          armv9.0_1     964         220       3687       3
Pixel 9 Pro  arm64-v8a    base.en-q5_1  armv8.0_1     895         138       2407       3
Pixel 9 Pro  arm64-v8a    base.en-q5_1  armv8.2_2     905         163       1818       3
Pixel 9 Pro  arm64-v8a    base.en-q5_1  armv9.0_1     867         149       3999       3
Pixel 9 Pro  arm64-v8a    tiny-q5_1     armv8.0_1     940         127       1229       3
Pixel 9 Pro  arm64-v8a    tiny-q5_1     armv8.2_2     953         128       1001       3
Pixel 9 Pro  arm64-v8a    tiny-q5_1     armv9.0_1     934         141       2061       3
Pixel 9 Pro  arm64-v8a    tiny.en-q5_1  armv8.0_1     1058        138       1924       3
Pixel 9 Pro  arm64-v8a    tiny.en-q5_1  armv8.2_2     1028        148       2183       3
Pixel 9 Pro  arm64-v8a    tiny.en-q5_1  armv9.0_1     991         133       3941       3

In all cases, the armv8.2_2 backend was the fastest, and armv9.0_1 should probably just be ignored. It's a somewhat sad situation, because I assume that most systems capable of running SVE2 probably also have a proprietary TPU / NPU that can't be easily programmed.

Users are essentially stuck going back a generation to armv8.2_2 and not using the full capabilities of the processors they paid good money for.

This is fine.

One other thing that I noticed is how bad midrange processors can be if you're not paying attention to multithreaded performance.

It's clear here that there was a "middle generation" of midrange processors with only 2 fast cores and not enough of a difference between the perf cores and the eff cores. These things actually need their efficiency cores in order to hit decent performance in multithreaded software.

Device (oldest→newest) ABI CPU clusters (kHz × count) Backend Affinity INFER (ms)
Nexus 5 v7a 2265×4 (homogeneous, hotplugs) linked no pin (ratio 1.0) 16315
Huawei M2 arm64 1516×4 / 2016×4 armv8.0_1 no pin (4 cores but ratio 1.33) 8752
Pixel 2 arm64 1900×4 / 2457×4 armv8.0_1 no pin (ratio 1.29) 7243
Pixel 3a arm64 1708×6 / 1996×2 armv8.0_1 no pin (only 2 fast cores) 6015
Pixel 5 arm64 1804×6 / 2208×1 / 2400×1 armv8.2_2 no pin (only 2 fast cores) 3676
Mi 11 arm64 1804×4 / 2419×3 / 2841×1 armv8.2_2 pin 0xf0 (4 fast, ratio 1.57) 1410
Pixel 9 Pro arm64 1950×4 / 2600×3 / 3105×1 armv8.2_2 (Tensor G4 cap) pin 0xf0 (4 fast, ratio 1.59) 1415