android arm64 android-api-36 soc-mediatek-mt6899 android-npu npu-neuron
引き出しで眠っているその端末は、NPU付きのARM64 Linuxマシンです。 DroidRunnerは、それをGitHub Actionsにそう認識させます。
APK単体で、公式のGitHub Actions RunnerをAndroid端末上で動かします。 root不要、Termux不要、PCへの接続も不要。
GitHubがホストするARM64 Runnerは仮想マシンです。そのため、あなたのモデルが Qualcomm HexagonやGoogle Tensor TPU、MediaTek APUで実際に動くのかには答えられません。 背後にNPUが無く、ベンダーのドライバスタックも無いからです。
それに答えられる端末は、すでに引き出しの中にあります。DroidRunnerが目指すのは、 CIがラベルでジョブを振り分けられる実機のプールです。 「このビルドはARM64で通るか」と「このモデルはそのベンダーのNPUで動くか」を、 どちらもpushで走る同じ種類の質問にすることが目的です。
スマホの正直な比較対象は仮想マシンではなく、棚に置くRaspberry Piか、ホストされた
ARM64の従量課金です。GitHubのlinux-arm64 Runnerは公開リポジトリなら
無料ですが、プライベートは課金されます。つまりプライベートリポジトリでの実際の
選択は、買うボードとすでに持っている端末の比較になります。
| Raspberry Pi 5 | 引き出しの中のスマホ | |
|---|---|---|
| 費用 | 本体・電源・ストレージ・ケース | 支払い済み |
| NPU | 無し | このプロジェクトの存在理由 |
| 計算に使えるGPU | 実質無し | TFLite delegateから到達可能 |
| 停電 | 止まる。UPSは別料金 | バッテリーがUPS |
| 状態表示 | 画面を足す | 最初から在る |
| Linux | 普通に使える。root有り | PRoot、root不可、ベンダーの電源管理 |
GitHub Actions ↓ ラベルでジョブが割り当てられる ┌─ DroidRunner APK ────────────────────────────────┐ Androidコントローラ → Keystore (ユーザートークン) ↓ PRoot + Ubuntu ARM64 rootfs ↓ 公式 Linux ARM64 Actions Runner ↓ loopback + ジョブ単位のcapability token Device Agent → NNAPI / QNN / Neuron / ENN └──────────────────────────────────────────────────┘
シェル、Git、Node、Python、Rust、Go、GradleはPRootの中で通常のLinuxと同じように動きます。 Android APIやNPUが必要なものはそのnamespaceからは触れないため、 実行中のジョブ向けに発行されたトークンだけを受け付けるloopback API経由で、 アプリ側に委譲します。
proot自体はネイティブライブラリとしてAPKの中に同梱しています。
Android 10以降はアプリのデータ領域に置いたバイナリのexec()を拒否するためです。
したがってダウンロードするruntime bundleはデータのみで、そのmanifestには署名があり、
誰も保証していないrootfsを端末が取りに行くことはありません。
コンパニオンアプリもTermuxも、貼り付けるスクリプトも不要。
PRoot上のLinux ARM64環境で動作。
GitHub公式のlinux-arm64 Actions Runnerを無改造で使用。
Device Flowでサインインして登録先を選ぶだけ。PAT発行もclient secret同梱も不要。
1リポジトリ、または組織内の全リポジトリを担当。
Android APIレベル、SoC、NPU候補からRunnerラベルを生成。
高温・容量不足、あるいはバッテリー動作かつ残量がしきい値未満のあいだはジョブを保留。給電時のみ動かす設定は既定でオフ。
異常終了してもバックオフ付きで自動再起動。
ジョブごとに再登録し、work directoryを消去。
ECDSA署名のmanifestを、APKに埋め込んだ鍵で検証。
GitHubトークンはAndroidが暗号化し、Linux側には渡しません。
Foreground ServiceとWakeLockでRunnerを維持。
jobs: test: runs-on: [self-hosted, android, arm64] steps: - uses: actions/checkout@v4 - run: uname -a - run: ./ci/test-arm64.sh
シリコンに直接聞くこともできます:
runs-on: [self-hosted, android, nnapi-accelerator] steps: - run: droidrunner-device capabilities # 端末情報・温度・ドライバ一覧 - run: droidrunner-device bench-all # 全ドライバでCONV_2D
DEVICE AVG_US GFLOPS mtk-dsp_shim - compilation_finish mtk-mdla_shim - compilation_finish mtk-neuron_shim 4148.9 4.55 nnapi-reference 1107.7 17.04
拒否した2つのドライバはfloat32ではなく量子化モデルを要求しています。 ラベルをSoC名の解析ではなくプローブ結果から作るべき理由が、まさにこれです。
| ラベル | 意味 |
|---|---|
android | DroidRunnerが動作している実機Android端末 |
arm64 | ARM64端末 |
android-api-N | Android APIレベル |
soc-* | 検出したSoC情報 |
android-npu | そのSoCファミリーにNPUがあることは分かっている。約束ではなくヒント |
nnapi-accelerator |
アクセラレータがプローブに応答した — 指定すべきラベル |
npu-qnn |
この端末のHexagonでモデルが実際に走った — 推測ではなく実測 |
npu-tflite / npu-neuron / npu-enn |
LiteRT / MediaTek Neuron / Samsung ENN の候補(SoC名由来) |
nnapi / nnapi-accelerator / npu-qnn は
端末に聞いた実測、それ以外はSoC名由来の候補です。加速が必要なジョブは
実測系のラベルを指定してください。さもないと黙ってCPUで走ります。
npu-qnn はかつて推測でしたが、いまは
Hexagonでモデルが走ったことを示した端末にだけ付きます。
EfficientNet-Lite0を、3台の実機で同じモデル・同じ計測系で実行したものです。各行は 「依頼したドライバ」ではなく実際に実行したプロセッサを報告しています。
| 端末 | ドライバ | int8 | float32 |
|---|---|---|---|
| Tensor G4 | google-edgetpu | 0.65 ms | 16.7 ms |
| SM8650 | qnn-htp(Hexagon) | 1.22 ms | 2.78 ms |
| SM8650 | gpu(Adreno、TFLiteデリゲート) | 4.95 ms | — |
| MT6899 | mtk-neuron_shim | 2.12 ms | 7.29 ms |
| MT6899 | mtk-mdla_shim | 2.69 ms | CPUに落ちた |
| Tensor G4 | NNAPI任せ | 5.65 ms | — |
| SM8650 | nnapi-reference(CPU) | 113 ms | 39.8 ms |
| MT6899 | nnapi-reference(CPU) | 257 ms | 106 ms |
ネットワーク全体では答えられません。1ノードの拒否がそのパーティション全体をCPUへ押し戻すので、 フォールバックは「ベンダーの表のどこかが間違っている」とは言っても「どこが」は言わない。 なので演算子×精度ごとに1opのモデルを62個作り、その端末が持つ全ドライバで走らせます。
| ドライバ | float32 | int8 |
|---|---|---|
qnn-htp(SM8650 Hexagon) | 31/31 | 31/31 |
gpu(SM8650 Adreno、TFLiteデリゲート) | 31/31 | 31/31 |
mtk-neuron_shim(MT6899) | 28/31 | 30/31 |
mtk-mdla_shim(MT6899) | 0/31 | 30/31 |
mtk-dsp_shim(MT6899) | 10/31 | 19/31 |
google-edgetpu(Tensor G4) | 0/31 | 29/31 |
動作としたものは、CIだけでなく実機で確認済みです。 詳細な表は README にあります。保留は、経路を全部試したうえで どれも「端末に無い、またはアプリからロードできない」ライブラリを通ることが分かった状態です (issue #83)。