snapdragon: ci updates to use new run script (#28293)

* snapdragon: update CI script to use new snapdragon/run.py

* snapdragon: update build.py to not set +x on /lib
This commit is contained in:
Max Krasnyansky
2026-09-03 08:59:11 -07:00
committed by GitHub
parent e107984bcf
commit d30500b83b
7 changed files with 91 additions and 60 deletions
+8
View File
@@ -226,6 +226,10 @@ def main():
if res.returncode != 0: if res.returncode != 0:
logger.error("ADB push failed.") logger.error("ADB push failed.")
sys.exit(res.returncode) sys.exit(res.returncode)
chmod_cmd = adb_cmd + ["shell", f"chmod -R 755 {target_dir}/bin 2>/dev/null || true"]
logger.info(f"+ {' '.join(chmod_cmd)}")
subprocess.run(chmod_cmd)
logger.info("ADB push completed successfully!") logger.info("ADB push completed successfully!")
elif target_type == "linux": elif target_type == "linux":
@@ -249,6 +253,10 @@ def main():
if res.returncode != 0: if res.returncode != 0:
logger.error("SSH/SCP deploy failed.") logger.error("SSH/SCP deploy failed.")
sys.exit(res.returncode) sys.exit(res.returncode)
chmod_cmd = ["ssh", ssh_host, f"chmod -R 755 {target_dir}/bin 2>/dev/null || true"]
logger.info(f"+ {' '.join(chmod_cmd)}")
subprocess.run(chmod_cmd)
logger.info("SSH/SCP deploy completed successfully!") logger.info("SSH/SCP deploy completed successfully!")
elif target_type == "windows": elif target_type == "windows":
+5 -26
View File
@@ -35,7 +35,6 @@ import shutil
import sys import sys
import tempfile import tempfile
import time import time
import urllib.request
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
from dataclasses import dataclass, field from dataclasses import dataclass, field
from pathlib import Path from pathlib import Path
@@ -104,15 +103,7 @@ _NON_TERMINAL_STATE_VALUES = {s.value for s in NON_TERMINAL_STATES}
_RUN_BENCH = _TESTS_DIR / "run_bench_tests_posix.py" _RUN_BENCH = _TESTS_DIR / "run_bench_tests_posix.py"
_RUN_BACKEND_OPS = _TESTS_DIR / "run_backend_ops_posix.py" _RUN_BACKEND_OPS = _TESTS_DIR / "run_backend_ops_posix.py"
_REQUIREMENTS = _SCRIPTS_DIR / "requirements.txt" _REQUIREMENTS = _SCRIPTS_DIR / "requirements.txt"
_UPSTREAM_ADB_SCRIPTS = ( _RUN_PY = _SCRIPTS_DIR.parent / "run.py"
"https://raw.githubusercontent.com/ggml-org/llama.cpp/master/scripts/snapdragon/adb"
)
_ADB_SCRIPT_NAMES = [
"run-bench.sh",
"run-cli.sh",
"run-completion.sh",
"run-tool.sh",
]
# --- Linux (BASH) assets ------------------------------------------------------ # --- Linux (BASH) assets ------------------------------------------------------
_RUN_LINUX_TEMPLATE = _TESTS_DIR / "linux" / "run_linux.sh" _RUN_LINUX_TEMPLATE = _TESTS_DIR / "linux" / "run_linux.sh"
@@ -147,7 +138,7 @@ def _build_android_artifact(
Zip structure: Zip structure:
llama_cpp_bundle/ installed package (adb pushed to /data/local/tmp/) llama_cpp_bundle/ installed package (adb pushed to /data/local/tmp/)
run-{bench,cli,completion,tool}.sh upstream adb wrappers (patched) run.py Snapdragon runner
tests/ tests/
utils.py shared adb helpers utils.py shared adb helpers
conftest.py Appium pytest fixtures conftest.py Appium pytest fixtures
@@ -159,21 +150,9 @@ def _build_android_artifact(
bundle_dir = stage_dir / "llama_cpp_bundle" bundle_dir = stage_dir / "llama_cpp_bundle"
shutil.copytree(pkg_dir, bundle_dir) shutil.copytree(pkg_dir, bundle_dir)
# Download upstream adb scripts so they land at /qdc/appium/ on the QDC dest = stage_dir / "run.py"
# runner. They wrap `adb shell` internally. Patch in `chmod +x bin/* lib/*` shutil.copy(_RUN_PY, dest)
# right after `cd $basedir` so device binaries are executable. dest.chmod(0o755)
for name in _ADB_SCRIPT_NAMES:
url = f"{_UPSTREAM_ADB_SCRIPTS}/{name}"
dest = stage_dir / name
log.info("Downloading %s", url)
urllib.request.urlretrieve(url, str(dest))
content = dest.read_text()
content = content.replace(
"cd $basedir;",
"cd $basedir; chmod +x bin/* lib/* 2>/dev/null;",
)
dest.write_text(content)
dest.chmod(0o755)
tests_dir = stage_dir / "tests" tests_dir = stage_dir / "tests"
tests_dir.mkdir() tests_dir.mkdir()
@@ -124,9 +124,9 @@ note_timeout_if_triggered() {
completion_extra_args() { completion_extra_args() {
case "$1" in case "$1" in
cpu) echo "--device none --ctx-size 128 -no-cnv -n 32 --seed 42 --batch-size 128" ;; cpu) echo "--device none --ctx-size 2048 -no-cnv -n 32 --seed 42" ;;
gpu) echo "--device GPUOpenCL --ctx-size 128 -no-cnv -n 32 --seed 42 --ubatch-size 512" ;; gpu) echo "--device GPUOpenCL --ctx-size 2048 -no-cnv -n 32 --seed 42" ;;
npu) echo "--device HTP0 --ctx-size 128 -no-cnv -n 32 --seed 42 --ubatch-size 1024" ;; npu) echo "--device HTP0 --ctx-size 2048 -no-cnv -n 32 --seed 42 --ubatch-size 1024" ;;
esac esac
} }
@@ -161,12 +161,14 @@ run_bench_case() {
local ndev=${parts[0]} device=${parts[1]} local ndev=${parts[0]} device=${parts[1]}
local log_suffix=$(backend_log_name "$name") local log_suffix=$(backend_log_name "$name")
local log="$LOG_DIR/llama_bench_${log_suffix}.log" local log="$LOG_DIR/llama_bench_${log_suffix}.log"
local ubatch_arg=""
[ "$name" = "npu" ] && ubatch_arg="--ubatch-size 1024"
echo "=== [bench:$name] llama-bench --device $device (NDEV=$ndev) ===" echo "=== [bench:$name] llama-bench --device $device (NDEV=$ndev) ==="
timeout 600 env GGML_HEXAGON_NDEV=$ndev ./bin/llama-bench \ timeout 600 env GGML_HEXAGON_NDEV=$ndev ./bin/llama-bench \
-m "$MODEL_PATH" \ -m "$MODEL_PATH" \
--device "$device" \ --device "$device" \
-ngl 99 \ -ngl 99 \
--batch-size 128 \ $ubatch_arg \
-t 4 \ -t 4 \
-p 128 \ -p 128 \
-n 32 \ -n 32 \
@@ -14,7 +14,7 @@ import pytest
from utils import ( from utils import (
BIN_PATH, BIN_PATH,
push_bundle_if_needed, push_bundle_if_needed,
run_script, run_snapdragon,
write_qdc_log, write_qdc_log,
) )
@@ -31,11 +31,8 @@ def test_backend_ops_htp0(type_a):
else: else:
pattern = f"type_a={type_a}" pattern = f"type_a={type_a}"
quoted_pattern = f'"{pattern}"' if type_a == "q4_0" else pattern result = run_snapdragon(
result = run_script( ["test-backend-ops", "-b", "HTP0", "-o", "MUL_MAT", "-p", pattern],
"run-tool.sh",
extra_env={"HB": "0"},
extra_args=["test-backend-ops", "-b", "HTP0", "-o", "MUL_MAT", "-p", quoted_pattern],
) )
write_qdc_log(f"backend_ops_{type_a}.log", result.stdout or "") write_qdc_log(f"backend_ops_{type_a}.log", result.stdout or "")
assert result.returncode == 0, ( assert result.returncode == 0, (
@@ -1,8 +1,8 @@
""" """
On-device bench and completion test runner for llama.cpp (CPU, GPU, NPU backends). On-device bench and completion test runner for llama.cpp (CPU, GPU, NPU backends).
On Android: calls upstream run-*.sh scripts from llama.cpp/scripts/snapdragon/adb/ On Android: calls scripts/snapdragon/run.py on the QDC runner host
on the QDC runner host (scripts wrap commands in ``adb shell`` internally). (script wraps commands in adb shell internally).
On Linux: runs llama-bench directly via run_linux.sh (BASH framework). On Linux: runs llama-bench directly via run_linux.sh (BASH framework).
@@ -19,11 +19,10 @@ import pytest
from utils import ( from utils import (
BIN_PATH, BIN_PATH,
MODEL_DEVICE_PATH, MODEL_DEVICE_PATH,
MODEL_NAME,
PROMPT_DIR, PROMPT_DIR,
push_bundle_if_needed, push_bundle_if_needed,
run_adb_command, run_adb_command,
run_script, run_snapdragon,
write_qdc_log, write_qdc_log,
) )
@@ -52,12 +51,18 @@ def install(driver):
], ],
) )
def test_llama_completion(device): def test_llama_completion(device):
result = run_script( args = [
"run-completion.sh", "llama-completion",
extra_env={"D": device, "M": MODEL_NAME}, "-m", MODEL_DEVICE_PATH,
extra_args=["--batch-size", "128", "-n", "128", "--seed", "42", "-f", f"{PROMPT_DIR}/bench_prompt.txt",
"-f", f"{PROMPT_DIR}/bench_prompt.txt"], "-no-cnv",
) "--ctx-size", "8192",
"-n", "128",
"--seed", "42",
]
if device == "HTP0":
args += ["--ubatch-size", "1024"]
result = run_snapdragon(args, device=device)
write_qdc_log(f"llama_completion_{device}.log", result.stdout or "") write_qdc_log(f"llama_completion_{device}.log", result.stdout or "")
assert result.returncode == 0, ( assert result.returncode == 0, (
f"llama-completion {device} failed (exit {result.returncode})" f"llama-completion {device} failed (exit {result.returncode})"
@@ -76,11 +81,16 @@ _DEVICE_LOG_NAME = {"none": "cpu", "GPUOpenCL": "gpu", "HTP0": "htp"}
], ],
) )
def test_llama_bench(device): def test_llama_bench(device):
result = run_script( args = [
"run-bench.sh", "llama-bench",
extra_env={"D": device, "M": MODEL_NAME}, "-m", MODEL_DEVICE_PATH,
extra_args=["--batch-size", "128", "-p", "128", "-n", "32"], "-ngl", "99",
) "-p", "128",
"-n", "32",
]
if device == "HTP0":
args += ["--ubatch-size", "1024"]
result = run_snapdragon(args, device=device)
write_qdc_log(f"llama_bench_{_DEVICE_LOG_NAME[device]}.log", result.stdout or "") write_qdc_log(f"llama_bench_{_DEVICE_LOG_NAME[device]}.log", result.stdout or "")
assert result.returncode == 0, ( assert result.returncode == 0, (
f"llama-bench {device} failed (exit {result.returncode})" f"llama-bench {device} failed (exit {result.returncode})"
+14 -5
View File
@@ -5,6 +5,7 @@ from __future__ import annotations
import logging import logging
import os import os
import subprocess import subprocess
import sys
import tempfile import tempfile
from appium.options.common import AppiumOptions from appium.options.common import AppiumOptions
@@ -93,17 +94,25 @@ def run_adb_command(cmd: str, *, check: bool = True) -> subprocess.CompletedProc
return result return result
def run_script( def run_snapdragon(
script: str, cmd_args: list[str],
*,
device: str | None = None,
extra_run_args: list[str] | None = None,
extra_env: dict[str, str] | None = None, extra_env: dict[str, str] | None = None,
extra_args: list[str] | None = None,
) -> subprocess.CompletedProcess: ) -> subprocess.CompletedProcess:
"""Run an upstream shell script from /qdc/appium/ on the QDC runner host.""" """Run a tool via scripts/snapdragon/run.py targeting android."""
env = os.environ.copy() env = os.environ.copy()
env["GGML_HEXAGON_EXPERIMENTAL"] = "1" env["GGML_HEXAGON_EXPERIMENTAL"] = "1"
if extra_env: if extra_env:
env.update(extra_env) env.update(extra_env)
cmd = [f"{SCRIPTS_DIR}/{script}"] + (extra_args or []) cmd = [sys.executable, f"{SCRIPTS_DIR}/run.py", "--target", "android"]
if device is not None:
cmd.extend(["-d", device])
if extra_run_args:
cmd.extend(extra_run_args)
cmd.append("--")
cmd.extend(cmd_args)
result = subprocess.run( result = subprocess.run(
cmd, env=env, cmd, env=env,
text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
+30 -4
View File
@@ -146,7 +146,22 @@ def main():
env_vars[env_name] = os.environ[env_name] env_vars[env_name] = os.environ[env_name]
# Resolve and filter devices (HTP vs OpenCL) # Resolve and filter devices (HTP vs OpenCL)
devices_val = args.devices if args.devices is not None else "HTP0" device_in_cmd = None
for i, arg in enumerate(cmd_args):
if arg == "--device" and i + 1 < len(cmd_args):
device_in_cmd = cmd_args[i + 1]
break
elif arg.startswith("--device="):
device_in_cmd = arg.split("=", 1)[1]
break
if args.devices is not None:
devices_val = args.devices
elif device_in_cmd is not None:
devices_val = device_in_cmd
else:
devices_val = "HTP0"
if devices_val.isdigit(): if devices_val.isdigit():
hex_devices = devices_val hex_devices = devices_val
cl_device = "" cl_device = ""
@@ -154,7 +169,12 @@ def main():
parts = [p.strip() for p in devices_val.split(",")] parts = [p.strip() for p in devices_val.split(",")]
# Any device containing "htp" is Hexagon, rest is OpenCL # Any device containing "htp" is Hexagon, rest is OpenCL
hex_parts = [p for p in parts if "htp" in p.lower()] hex_parts = [p for p in parts if "htp" in p.lower()]
cl_parts = [p for p in parts if "htp" not in p.lower()] cl_parts = [
p for p in parts
if "htp" not in p.lower()
and p.lower() not in ("none", "cpu")
and not p.lower().startswith("gpuopencl")
]
hex_devices = ",".join(hex_parts) hex_devices = ",".join(hex_parts)
cl_device = ",".join(cl_parts) cl_device = ",".join(cl_parts)
@@ -316,11 +336,17 @@ def main():
if basename in ("llama-cli", "llama-completion", "llama-server"): if basename in ("llama-cli", "llama-completion", "llama-server"):
if "-ngl" not in cmd_args and "--n-gpu-layers" not in cmd_args: if "-ngl" not in cmd_args and "--n-gpu-layers" not in cmd_args:
cmd_args += ["-ngl", "99"] cmd_args += ["-ngl", "99"]
if "--ubatch-size" not in cmd_args and "-ub" not in cmd_args:
cmd_args += ["--ubatch-size", "1024"]
if "-fa" not in cmd_args and "--flash-attn" not in cmd_args: if "-fa" not in cmd_args and "--flash-attn" not in cmd_args:
cmd_args += ["-fa", "on"] cmd_args += ["-fa", "on"]
# Use ubatch-size 1024 for hexagon backend (HTP devices)
if hex_devices and basename in ("llama-cli", "llama-completion", "llama-server", "llama-bench"):
if "--ubatch-size" not in cmd_args and "-ub" not in cmd_args:
cmd_args += ["--ubatch-size", "1024"]
elif basename in ("llama-cli", "llama-completion", "llama-server"):
if "--ubatch-size" not in cmd_args and "-ub" not in cmd_args:
cmd_args += ["--ubatch-size", "1024"]
if basename in ("llama-cli", "llama-completion", "llama-server", "llama-bench"): if basename in ("llama-cli", "llama-completion", "llama-server", "llama-bench"):
if "-t" not in cmd_args and "--threads" not in cmd_args: if "-t" not in cmd_args and "--threads" not in cmd_args:
cmd_args += ["-t", "6"] cmd_args += ["-t", "6"]