Run the face tests on the tablet, where the NEON kernel actually runs

The similarity scan picks its dot product per machine, and the NEON one
is the kernel that ships to the phone and the tablet — and the one a
desktop cargo test never executes. A wrong lane index or a mishandled
tail there is a silent wrong answer on exactly the devices nobody runs
the suite on, which is a poor place for the only untested code path.

dr-face carries no weights and touches no display, so its tests are a
plain ARM64 binary that runs under adb shell with nothing installed.
The script builds it against the SDK's newest NDK, pushes it, runs it
and cleans up. It checks for the device first, so a tablet that is not
plugged in costs a second rather than the two minutes it takes to
compile for it.

Not wired into CI, which has no device attached.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-29 12:02:03 +02:00
co-authored by Claude Opus 5
parent 8f596262b8
commit f4395bd17c
2 changed files with 83 additions and 0 deletions
+5
View File
@@ -674,6 +674,11 @@ app builds for has it, and the explicit `vfmaq` matters because LLVM will not fu
add on its own. The portable loop remains the definition the others are tested against. All three
produce the same 1,531,969 pairs.
The NEON kernel is the one a desktop `cargo test` never executes, so
`tools/face-tests-on-device.sh` runs the suite on an attached device: `dr-face` carries no weights
and touches no display, so its tests are a plain ARM64 binary that runs under `adb shell` with
nothing installed. Worth running whenever the kernels change.
**Where a regroup's time actually goes**, on that library, because the answer moved twice while it
was being looked at:
+78
View File
@@ -0,0 +1,78 @@
#!/usr/bin/env bash
# Run dr-face's test suite on a connected Android device.
#
# ./tools/face-tests-on-device.sh [extra cargo test args…]
#
# ## Why this exists
#
# `dr-face`'s similarity scan picks its dot product per machine (docs/faces.md
# §9): AVX2 where the CPU has it, **NEON on aarch64**, and a portable loop
# otherwise. The NEON kernel is the one that runs on the phone and the tablet,
# and it is the one a desktop `cargo test` never executes — a wrong lane index
# or a mishandled tail there would be a silent wrong answer on exactly the
# devices nobody runs the test suite on.
#
# `the_fastest_kernel_agrees_with_the_portable_one` is written to catch that,
# and this is how it gets to run on the hardware it is about. Everything else
# in the suite comes along for free: `dr-face` carries no weights and touches
# no display, so its tests are a plain ARM64 binary that runs under `adb
# shell` with nothing installed.
#
# Not part of CI, which has no device attached. Run it when the kernels change.
set -euo pipefail
TARGET=aarch64-linux-android
API=26
DEST=/data/local/tmp/dr_face_tests
ndk="${ANDROID_NDK_HOME:-}"
if [[ -z "$ndk" ]]; then
# The newest NDK the SDK has, which is what the app is built with.
ndk=$(find "${ANDROID_HOME:-$HOME/Android/Sdk}/ndk" -maxdepth 1 -mindepth 1 -type d 2>/dev/null |
sort -V | tail -1)
fi
[[ -n "$ndk" && -d "$ndk" ]] || {
echo "no NDK found — set ANDROID_NDK_HOME" >&2
exit 1
}
clang="$ndk/toolchains/llvm/prebuilt/linux-x86_64/bin/$TARGET$API-clang"
[[ -x "$clang" ]] || {
echo "no $TARGET$API-clang in $ndk" >&2
exit 1
}
rustup target list --installed | grep -qx "$TARGET" || rustup target add "$TARGET"
# Told to the device before the build, so a missing tablet costs seconds rather
# than the two minutes it takes to compile for it.
adb devices | grep -qw device || {
echo "no device attached — plug the tablet in and enable USB debugging" >&2
exit 1
}
echo "building dr-face tests for $TARGET…"
binary=$(
CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER="$clang" \
CC_aarch64_linux_android="$clang" \
cargo test -p dr-face --lib --release --target "$TARGET" --no-run --message-format=json "$@" |
python3 -c '
import json, sys
for line in sys.stdin:
m = json.loads(line)
if m.get("profile", {}).get("test") and m.get("executable"):
print(m["executable"])
' | tail -1
)
[[ -n "$binary" ]] || {
echo "cargo produced no test binary" >&2
exit 1
}
echo "pushing $(basename "$binary")…"
adb push "$binary" "$DEST" >/dev/null
adb shell chmod 755 "$DEST"
# `--test-threads` left alone: the scan spawns its own workers and the point is
# to exercise them the way the app will.
adb shell "$DEST" --color never
adb shell rm -f "$DEST"