Team Ai
Modelpublic

Felipe97/llama-cpp-compiled

sourceHugging Faceupdated 21d agoView on Hugging Face
0likes1.2kdownloads
run.py494 linesDownload Raw Back to snapdragon
1#!/usr/bin/env python32#3# Run llama.cpp tools on Snapdragon devices (natively, via ADB, or SSH).4#5 6import sys7import os8import argparse9import subprocess10import platform11import shlex12import logging13 14logger = logging.getLogger("run")15 16 17MANAGED_ENV_NAMES = (18    "GGML_HEXAGON_DEVICES",19    "GGML_HEXAGON_VERBOSE",20    "GGML_HEXAGON_PROFILE",21    "GGML_HEXAGON_NHVX",22    "GGML_HEXAGON_NHMX",23    "GGML_HEXAGON_HOSTBUF",24    "GGML_HEXAGON_OPBATCH",25    "GGML_HEXAGON_OPQUEUE",26    "GGML_HEXAGON_OPPOLL",27    "GGML_HEXAGON_OPFILTER",28    "GGML_HEXAGON_OPFUSION",29    "GGML_HEXAGON_VMEM",30    "GGML_HEXAGON_MBUF",31    "GGML_HEXAGON_MM_SELECT",32    "GGML_HEXAGON_FA_SELECT",33    "GGML_HEXAGON_AR_SELECT",34    "GGML_HEXAGON_ETM",35    "GGML_HEXAGON_ARCH",36    "GGML_HEXAGON_OPTRACE",37    "GGML_OPENCL_PLATFORM",38    "GGML_OPENCL_DEVICE",39    "GGML_OPENCL_OPFILTER",40    "GGML_OPENCL_KERNEL_CACHE_DIR",41    "GGML_OPENCL_KERNEL_CACHE_DEBUG",42    "GGML_OPENCL_FA_TUNE",43    "GGML_OPENCL_DISABLE_FUSION",44    "GGML_OPENCL_ADRENO_XMEM_GEMM",45    "GGML_OPENCL_ADRENO_USE_LARGE_BUFFER",46    "GGML_SCHED_DEBUG",47    "MTMD_BACKEND_DEVICE",48    "D",49    "DEVICE",50)51 52 53def parse_target(target_str):54    if not target_str:55        return None, None56    if target_str.startswith("adb") or target_str.startswith("android"):57        parts = target_str.split(":", 1)58        serial = parts[1] if len(parts) > 1 else None59        return "android", serial60    elif target_str.startswith("lnx") or target_str.startswith("linux") or target_str.startswith("ubuntu"):61        parts = target_str.split(":", 1)62        host = parts[1] if len(parts) > 1 else None63        return "linux", host64    elif target_str in ("wos", "windows"):65        return "windows", None66    else:67        return None, None68 69 70def shlex_join(args_list):71    if hasattr(shlex, 'join'):72        return shlex.join(args_list)73    import pipes74    return " ".join(pipes.quote(x) for x in args_list)75 76 77def split_device_list(devices):78    parts = []79    curr = []80    bracket_depth = 081 82    for ch in devices:83        if ch == '[':84            bracket_depth += 185            curr.append(ch)86        elif ch == ']':87            if bracket_depth > 0:88                bracket_depth -= 189            curr.append(ch)90        elif ch == ',' and bracket_depth == 0:91            part = "".join(curr).strip()92            if part:93                parts.append(part)94            curr = []95        else:96            curr.append(ch)97 98    part = "".join(curr).strip()99    if part:100        parts.append(part)101 102    return parts103 104 105def device_arg_from_devices(devices):106    if devices.isdigit():107        n = int(devices)108        return ",".join(f"HTP{i}" for i in range(n))109 110    names = []111    for part in split_device_list(devices):112        if "[" in part:113            part = part.split("[", 1)[0].strip()114        if part:115            names.append(part)116 117    return ",".join(names)118 119 120def normalize_cmd_device_args(cmd_args):121    for i, arg in enumerate(cmd_args):122        if arg == "--device" and i + 1 < len(cmd_args):123            cmd_args[i + 1] = device_arg_from_devices(cmd_args[i + 1])124        elif arg.startswith("--device="):125            cmd_args[i] = "--device=" + device_arg_from_devices(arg.split("=", 1)[1])126 127 128def main():129    logging.basicConfig(level=logging.INFO, format='%(message)s')130    # Split arguments at '--'131    if '--' in sys.argv:132        idx = sys.argv.index('--')133        run_args = sys.argv[1:idx]134        cmd_args = sys.argv[idx + 1:]135    else:136        run_args = sys.argv[1:]137        cmd_args = []138 139    parser = argparse.ArgumentParser(140        description="Unified runner for llama.cpp tools on Snapdragon (natively, via ADB, or via SSH)."141    )142    parser.add_argument("--target", help="Execution target (e.g. android[:serial]/adb[:serial], linux:[user@]host/lnx:[user@]host/ubuntu:[user@]host, windows/wos) (default: local run)")143    parser.add_argument("--target-dir", help="Target directory on the device (default: /data/local/tmp/llama.cpp for Android, ~/llama.cpp for Linux)")144    parser.add_argument("--install-dir", help="Install directory name (defaults to pkg-TARGET or pkg-TARGET-dbg prefix based on target)")145    parser.add_argument("--debug", action="store_true", help="Use debug build (defaults to pkg-TARGET-dbg folder)")146    parser.add_argument("--devices", "--device", "-d", help="Select execution devices (split into NPU and OpenCL GPUs automatically, default: HTP0)")147    parser.add_argument("--verbose", help="Verbose level (enables both Hexagon and OpenCL kernel cache debugging)")148    parser.add_argument("--profile", help="Profiling flag (enables Hexagon profiling and OpenCL autotuning)")149    parser.add_argument("--sched-debug", action="store_true", help="Enable GGML/llama.cpp scheduler debug output (GGML_SCHED_DEBUG=2)")150    parser.add_argument("--mtmd-device", help="Specify the backend device ID for Multi-Threaded Multi-Device setup (MTMD_BACKEND_DEVICE)")151 152    # Hexagon specific parameters153    parser.add_argument("--hex-verbose", help="Enable verbose logging (GGML_HEXAGON_VERBOSE)")154    parser.add_argument("--hex-profile", help="Enable NPU/Hexagon profiling and performance metrics print (GGML_HEXAGON_PROFILE)")155    parser.add_argument("--hex-nhvx", help="Number of HVX units to use (GGML_HEXAGON_NHVX)")156    parser.add_argument("--hex-nhmx", help="Number of HMX units to use. 0 disables HMX power-up (GGML_HEXAGON_NHMX)")157    parser.add_argument("--hex-hostbuf", help="Enable host buffers (GGML_HEXAGON_HOSTBUF)")158    parser.add_argument("--hex-opbatch", help="Maximum number of operations to batch into a single HTP execution (GGML_HEXAGON_OPBATCH)")159    parser.add_argument("--hex-opqueue", help="Size of the asynchronous NPU operation queue (GGML_HEXAGON_OPQUEUE)")160    parser.add_argument("--hex-oppoll", default="1", help="Enable (1) or Disable (0) polling for NPU opbatch completion (GGML_HEXAGON_OPPOLL) (default: 1)")161    parser.add_argument("--hex-opfilter", help="Regex pattern to filter/select which operators are offloaded to NPU (GGML_HEXAGON_OPFILTER)")162    parser.add_argument("--hex-opfusion", help="NPU graph node fusion optimization level (0: disabled, 1: enabled) (GGML_HEXAGON_OPFUSION)")163    parser.add_argument("--hex-vmem", help="Maximum NPU VMEM size limit in MB to allocate (GGML_HEXAGON_VMEM)")164    parser.add_argument("--hex-mbuf", help="Maximum host buffer size limit in MB to allocate (GGML_HEXAGON_MBUF)")165    parser.add_argument("--hex-mm-select", help="Select MUL_MAT and MUL_MAT_ID kernel (GGML_HEXAGON_MM_SELECT) 3:HMX,2:HVX-tiled,1:HVX-flat,0:disable")166    parser.add_argument("--hex-fa-select", help="Select Flash Attention kernel (GGML_HEXAGON_FA_SELECT) 2:HMX,1:HVX,0:disable")167    parser.add_argument("--hex-ar-select", help="Select All-Reduce kernel (GGML_HEXAGON_AR_SELECT) 1:enable,0:disable")168    parser.add_argument("--hex-etm", help="Enable Embedded Trace Macrocell hardware tracing / trace logging (GGML_HEXAGON_ETM)")169    parser.add_argument("--hex-arch", help="Target Hexagon NPU architecture version override (v73, v75, v79, v81, etc.) (GGML_HEXAGON_ARCH)")170    parser.add_argument("--hex-optrace", help="Trace buffer size in number of records (GGML_HEXAGON_OPTRACE)")171 172    # OpenCL specific parameters173    parser.add_argument("--cl-platform", help="Select OpenCL platform name/regex (e.g. Qualified Qualcomm OpenCL platform) (GGML_OPENCL_PLATFORM)")174    parser.add_argument("--cl-device", help="Select OpenCL device name/regex (e.g. Adreno GPU) (GGML_OPENCL_DEVICE)")175    parser.add_argument("--cl-opfilter", help="Regex pattern to filter/select which operators are offloaded to OpenCL (GGML_OPENCL_OPFILTER)")176    parser.add_argument("--cl-disable-fusion", action="store_true", help="Disable OpenCL kernel fusion optimizations (GGML_OPENCL_DISABLE_FUSION)")177    parser.add_argument("--cl-cache-dir", help="Directory path to store compiled OpenCL program binaries (GGML_OPENCL_KERNEL_CACHE_DIR)")178    parser.add_argument("--cl-cache-debug", help="Enable verbose debugging logs for the kernel caching system (GGML_OPENCL_KERNEL_CACHE_DEBUG)")179    parser.add_argument("--cl-fa-tune", action="store_true", help="Enable automatic Flash Attention kernel autotuning (GGML_OPENCL_FA_TUNE)")180    parser.add_argument("--cl-adreno-xmem", action="store_true", help="Enforce matmul using texture/image (xmem) memory paths on Adreno GPUs (GGML_OPENCL_ADRENO_XMEM_GEMM)")181    parser.add_argument("--cl-adreno-large-buffer", action="store_true", help="Allow allocating larger buffer sizes on Adreno GPUs (GGML_OPENCL_ADRENO_USE_LARGE_BUFFER)")182 183    args = parser.parse_args(run_args)184 185    if not cmd_args:186        parser.print_help()187        logger.error("\nError: No command specified after '--'")188        sys.exit(1)189 190    target_type = None191    target_val = None192    target_prefix = None193    if args.target:194        target_type, target_val = parse_target(args.target)195        if not target_type:196            logger.error(f"Error: Invalid target format '{args.target}'. Must be android[:serial]/adb[:serial], linux:[user@]host/lnx:[user@]host/ubuntu:[user@]host, or windows/wos.")197            sys.exit(1)198        target_prefix = args.target.split(":", 1)[0]199 200    # Resolve install directory201    install_dir = args.install_dir202    if not install_dir:203        if target_prefix:204            suffix = "-dbg" if args.debug else ""205            install_dir = f"pkg-{target_prefix}{suffix}"206        else:207            # Smart branch folder detection for local run if default is not set208            prefixes = ("wos", "windows", "lnx", "linux", "ubuntu", "adb", "android")209            suffixes = ("-dbg", "") if args.debug else ("", "-dbg")210            found = False211            for suffix in suffixes:212                for prefix in prefixes:213                    test_path = f"./pkg-{prefix}{suffix}/llama.cpp"214                    if os.path.exists(test_path):215                        install_dir = f"pkg-{prefix}{suffix}"216                        found = True217                        break218                if found:219                    break220            if not install_dir:221                install_dir = "pkg-android"  # Fallback default222 223    # Host side package path224    package_path = os.path.join(install_dir, "llama.cpp")225 226    # Environment variables to map227    env_vars = {}228 229    def set_env(env_name, opt_val):230        if opt_val is not None:231            env_vars[env_name] = str(opt_val)232 233    # Resolve and filter devices (HTP vs OpenCL)234    device_in_cmd = None235    for i, arg in enumerate(cmd_args):236        if arg == "--device" and i + 1 < len(cmd_args):237            device_in_cmd = cmd_args[i + 1]238            break239        elif arg.startswith("--device="):240            device_in_cmd = arg.split("=", 1)[1]241            break242 243    if args.devices is not None:244        devices_val = args.devices245    elif device_in_cmd is not None:246        devices_val = device_in_cmd247    else:248        devices_val = "HTP0"249 250    if devices_val.isdigit():251        hex_devices = devices_val252        cl_device = ""253    else:254        parts = split_device_list(devices_val)255        # Any device containing "htp" is Hexagon, rest is OpenCL256        hex_parts = [p for p in parts if "htp" in p.lower()]257        cl_parts = [258            p for p in parts259            if "htp" not in p.lower()260            and p.lower() not in ("none", "cpu")261            and not p.lower().startswith("gpuopencl")262        ]263        hex_devices = ",".join(hex_parts)264        cl_device = ",".join(cl_parts)265 266    # Set Hexagon devices267    if hex_devices:268        env_vars["GGML_HEXAGON_DEVICES"] = hex_devices269 270    normalize_cmd_device_args(cmd_args)271 272    # Set OpenCL device (unless overridden by --cl-device)273    final_cl_device = args.cl_device if args.cl_device is not None else cl_device274    if final_cl_device:275        env_vars["GGML_OPENCL_DEVICE"] = final_cl_device276 277    # Map shared & backend-specific parameters with correct overrides278 279    # Verbose logging mapping280    hex_verbose_val = args.hex_verbose if args.hex_verbose is not None else args.verbose281    set_env("GGML_HEXAGON_VERBOSE", hex_verbose_val)282 283    cl_cache_debug_val = args.cl_cache_debug if args.cl_cache_debug is not None else args.verbose284    set_env("GGML_OPENCL_KERNEL_CACHE_DEBUG", cl_cache_debug_val)285 286    # Profiling mapping287    hex_profile_val = args.hex_profile if args.hex_profile is not None else args.profile288    set_env("GGML_HEXAGON_PROFILE", hex_profile_val)289 290    if args.cl_fa_tune or args.profile is not None:291        env_vars["GGML_OPENCL_FA_TUNE"] = "1"292 293    # Other Hexagon environment variables294    set_env("GGML_HEXAGON_NHVX", args.hex_nhvx)295    set_env("GGML_HEXAGON_NHMX", args.hex_nhmx)296    set_env("GGML_HEXAGON_HOSTBUF", args.hex_hostbuf)297    set_env("GGML_HEXAGON_OPBATCH", args.hex_opbatch)298    set_env("GGML_HEXAGON_OPQUEUE", args.hex_opqueue)299    set_env("GGML_HEXAGON_OPPOLL", args.hex_oppoll)300    set_env("GGML_HEXAGON_OPFILTER", args.hex_opfilter)301    set_env("GGML_HEXAGON_OPFUSION", args.hex_opfusion)302    set_env("GGML_HEXAGON_VMEM", args.hex_vmem)303    set_env("GGML_HEXAGON_MBUF", args.hex_mbuf)304    set_env("GGML_HEXAGON_MM_SELECT", args.hex_mm_select)305    set_env("GGML_HEXAGON_FA_SELECT", args.hex_fa_select)306    set_env("GGML_HEXAGON_AR_SELECT", args.hex_ar_select)307    set_env("GGML_HEXAGON_ETM", args.hex_etm)308    set_env("GGML_HEXAGON_ARCH", args.hex_arch)309    set_env("GGML_HEXAGON_OPTRACE", args.hex_optrace)310    set_env("MTMD_BACKEND_DEVICE", args.mtmd_device)311 312    # OpenCL environment variables313    set_env("GGML_OPENCL_PLATFORM", args.cl_platform)314    set_env("GGML_OPENCL_OPFILTER", args.cl_opfilter)315    set_env("GGML_OPENCL_KERNEL_CACHE_DIR", args.cl_cache_dir)316 317    if args.cl_disable_fusion:318        env_vars["GGML_OPENCL_DISABLE_FUSION"] = "1"319 320    if args.cl_adreno_xmem:321        env_vars["GGML_OPENCL_ADRENO_XMEM_GEMM"] = "1"322 323    if args.cl_adreno_large_buffer:324        env_vars["GGML_OPENCL_ADRENO_USE_LARGE_BUFFER"] = "1"325 326    if args.sched_debug:327        env_vars["GGML_SCHED_DEBUG"] = "2"328 329    # Resolve executable path330    executable = cmd_args[0]331    known_binaries = ["llama-cli", "llama-bench", "llama-completion", "llama-mtmd-cli", "test-backend-ops"]332    if executable in known_binaries:333        if target_type in ("android", "linux"):334            resolved_exec = f"./bin/{executable}"335        else:336            if platform.system() == "Windows":337                resolved_exec = os.path.normpath(os.path.join(package_path, "bin", f"{executable}.exe"))338            else:339                resolved_exec = os.path.normpath(os.path.join(package_path, "bin", executable))340        cmd_args[0] = resolved_exec341 342    # Infer device string to pass to the tool343    basename = os.path.basename(executable)344    if basename.endswith(".exe"):345        basename = basename[:-4]346 347    device_val = None348    if basename == "test-backend-ops":349        for i in range(len(cmd_args)):350            if cmd_args[i] in ("-p", "--params") and i + 1 < len(cmd_args):351                val = cmd_args[i + 1]352                new_val = ""353                for j, char in enumerate(val):354                    if char in ('[', ']'):355                        if j > 0 and val[j - 1] == '\\':356                            new_val += char357                        else:358                            new_val += '\\' + char359                    else:360                        new_val += char361                cmd_args[i + 1] = new_val362 363        has_b = any(arg == "-b" for arg in cmd_args)364        if not has_b:365            if args.devices:366                device_val = device_arg_from_devices(args.devices)367            else:368                device_val = "HTP0"369            if device_val:370                cmd_args += ["-b", device_val]371    else:372        has_device = any(arg.startswith("--device") for arg in cmd_args)373        if not has_device:374            if args.devices:375                device_val = device_arg_from_devices(args.devices)376            else:377                device_val = "HTP0"378 379            if device_val:380                cmd_args += ["--device", device_val]381 382    # Automatically add -v to known llama tools if sched-debug, verbose, or profile are set383    verbose_trigger = (384        args.sched_debug385        or args.verbose is not None386        or args.profile is not None387        or args.hex_verbose is not None388        or args.hex_profile is not None389        or args.hex_optrace is not None390    )391    if verbose_trigger and basename in ("llama-cli", "llama-completion", "llama-bench", "llama-server", "llama-mtmd-cli"):392        if "-v" not in cmd_args and "--verbose" not in cmd_args:393            cmd_args.append("-v")394 395    # Inject defaults for llama-cli, llama-completion, and llama-server if not overridden by the user396    if basename in ("llama-cli", "llama-completion", "llama-server"):397        if "-ngl" not in cmd_args and "--n-gpu-layers" not in cmd_args:398            cmd_args += ["-ngl", "99"]399        if "-fa" not in cmd_args and "--flash-attn" not in cmd_args:400            cmd_args += ["-fa", "on"]401 402    # Use ubatch-size 1024 for hexagon backend (HTP devices)403    if hex_devices and basename in ("llama-cli", "llama-completion", "llama-server", "llama-bench"):404        if "--ubatch-size" not in cmd_args and "-ub" not in cmd_args:405            cmd_args += ["--ubatch-size", "1024"]406    elif basename in ("llama-cli", "llama-completion", "llama-server"):407        if "--ubatch-size" not in cmd_args and "-ub" not in cmd_args:408            cmd_args += ["--ubatch-size", "1024"]409 410    if basename in ("llama-cli", "llama-completion", "llama-server", "llama-bench"):411        if "-t" not in cmd_args and "--threads" not in cmd_args:412            cmd_args += ["-t", "6"]413 414    # Resolve target directory on device415    target_dir = args.target_dir416    if not target_dir:417        target_dir = "/data/local/tmp/llama.cpp" if target_type == "android" else "~/llama.cpp"418 419    if target_type == "android":420        # Run via ADB421        adb_base = ["adb"]422        if target_val: # serial423            adb_base += ["-s", target_val]424 425        env_parts = [426            "LD_LIBRARY_PATH=./lib",427            "ADSP_LIBRARY_PATH=./lib"428        ]429        for k, v in env_vars.items():430            env_parts.append(f"{k}={v}")431        env_str = " ".join(env_parts)432 433        cmd_str = shlex_join(cmd_args)434        adb_shell_cmd = f"cd {target_dir} && ulimit -c unlimited && {env_str} {cmd_str}"435        full_cmd = adb_base + ["shell", adb_shell_cmd]436 437        logger.info(f"+ {' '.join(full_cmd)}")438        res = subprocess.run(full_cmd)439        sys.exit(res.returncode)440 441    elif target_type == "linux":442        ssh_host = target_val443        if not ssh_host:444            logger.error("Error: SSH host not specified in target (e.g. use linux:user@host, lnx:user@host, or ubuntu:user@host). Cannot execute.")445            sys.exit(1)446 447        # Linux remote run via SSH448        env_parts = [449            "LD_LIBRARY_PATH=./lib",450            "ADSP_LIBRARY_PATH=./lib"451        ]452        for k, v in env_vars.items():453            env_parts.append(f"{k}={v}")454        env_str = " ".join(env_parts)455 456        cmd_str = shlex_join(cmd_args)457        ssh_shell_cmd = f"cd {target_dir} && ulimit -c unlimited && {env_str} {cmd_str}"458        full_cmd = ["ssh", ssh_host, ssh_shell_cmd]459 460        logger.info(f"+ {' '.join(full_cmd)}")461        res = subprocess.run(full_cmd)462        sys.exit(res.returncode)463 464    elif target_type == "windows":465        logger.info("Windows target execution is currently a stub.")466        sys.exit(0)467 468    else:469        # Run locally470        local_env = os.environ.copy()471        lib_dir = os.path.normpath(os.path.join(package_path, "lib"))472        local_env["ADSP_LIBRARY_PATH"] = lib_dir473        if platform.system() == "Windows":474            local_env["PATH"] = lib_dir + os.path.pathsep + local_env.get("PATH", "")475        else:476            local_env["LD_LIBRARY_PATH"] = lib_dir + os.path.pathsep + local_env.get("LD_LIBRARY_PATH", "")477 478        for k in MANAGED_ENV_NAMES:479            local_env.pop(k, None)480        for k, v in env_vars.items():481            local_env[k] = v482 483        logger.info(f"+ {shlex_join(cmd_args)}")484        res = subprocess.run(cmd_args, env=local_env)485        sys.exit(res.returncode)486 487 488if __name__ == "__main__":489    try:490        main()491    except KeyboardInterrupt:492        logger.info("\nInterrupted by user.")493        sys.exit(130)494