char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
eafe15a5e
char/aliceai-t5-moe
1#!/usr/bin/env python3 2# 3# Run llama.cpp tools on Snapdragon devices (natively, via ADB, or SSH). 4# 5 6import sys 7import os 8import argparse 9import subprocess 10import platform 11import shlex 12import logging 13 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 ): 54if not target_str : 55return None ,None 56if target_str .startswith ("adb" )or target_str .startswith ("android" ): 57parts = target_str .split (":" ,1 ) 58serial = parts [1 ]if len (parts )> 1 else None 59return "android" ,serial 60elif target_str .startswith ("lnx" )or target_str .startswith ("linux" )or target_str .startswith ("ubuntu" ): 61parts = target_str .split (":" ,1 ) 62host = parts [1 ]if len (parts )> 1 else None 63return "linux" ,host 64elif target_str in ("wos" ,"windows" ): 65return "windows" ,None 66else : 67return None ,None 68 69 70def shlex_join (args_list ): 71if hasattr (shlex ,'join' ): 72return shlex .join (args_list ) 73import pipes 74return " " .join (pipes .quote (x )for x in args_list ) 75 76 77def split_device_list (devices ): 78parts = [] 79curr = [] 80bracket_depth = 0 81 82for ch in devices : 83if ch == '[' : 84bracket_depth += 1 85curr .append (ch ) 86elif ch == ']' : 87if bracket_depth > 0 : 88bracket_depth -= 1 89curr .append (ch ) 90elif ch == ',' and bracket_depth == 0 : 91part = "" .join (curr ).strip () 92if part : 93parts .append (part ) 94curr = [] 95else : 96curr .append (ch ) 97 98part = "" .join (curr ).strip () 99if part : 100parts .append (part ) 101 102return parts 103 104 105def device_arg_from_devices (devices ): 106if devices .isdigit (): 107n = int (devices ) 108return "," .join (f"HTP { i } " for i in range (n )) 109 110names = [] 111for part in split_device_list (devices ): 112if "[" in part : 113part = part .split ("[" ,1 )[0 ].strip () 114if part : 115names .append (part ) 116 117return "," .join (names ) 118 119 120def normalize_cmd_device_args (cmd_args ): 121for i ,arg in enumerate (cmd_args ): 122if arg == "--device" and i + 1 < len (cmd_args ): 123cmd_args [i + 1 ]= device_arg_from_devices (cmd_args [i + 1 ]) 124elif arg .startswith ("--device=" ): 125cmd_args [i ]= "--device=" + device_arg_from_devices (arg .split ("=" ,1 )[1 ]) 126 127 128def main (): 129logging .basicConfig (level = logging .INFO ,format = '%(message)s' ) 130# Split arguments at '--' 131if '--' in sys .argv : 132idx = sys .argv .index ('--' ) 133run_args = sys .argv [1 :idx ] 134cmd_args = sys .argv [idx + 1 :] 135else : 136run_args = sys .argv [1 :] 137cmd_args = [] 138 139parser = argparse .ArgumentParser ( 140description = "Unified runner for llama.cpp tools on Snapdragon (natively, via ADB, or via SSH)." 141 ) 142parser .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)" ) 143parser .add_argument ("--target-dir" ,help = "Target directory on the device (default: /data/local/tmp/llama.cpp for Android, ~/llama.cpp for Linux)" ) 144parser .add_argument ("--install-dir" ,help = "Install directory name (defaults to pkg-TARGET or pkg-TARGET-dbg prefix based on target)" ) 145parser .add_argument ("--debug" ,action = "store_true" ,help = "Use debug build (defaults to pkg-TARGET-dbg folder)" ) 146parser .add_argument ("--devices" ,"--device" ,"-d" ,help = "Select execution devices (split into NPU and OpenCL GPUs automatically, default: HTP0)" ) 147parser .add_argument ("--verbose" ,help = "Verbose level (enables both Hexagon and OpenCL kernel cache debugging)" ) 148parser .add_argument ("--profile" ,help = "Profiling flag (enables Hexagon profiling and OpenCL autotuning)" ) 149parser .add_argument ("--sched-debug" ,action = "store_true" ,help = "Enable GGML/llama.cpp scheduler debug output (GGML_SCHED_DEBUG=2)" ) 150parser .add_argument ("--mtmd-device" ,help = "Specify the backend device ID for Multi-Threaded Multi-Device setup (MTMD_BACKEND_DEVICE)" ) 151 152# Hexagon specific parameters 153parser .add_argument ("--hex-verbose" ,help = "Enable verbose logging (GGML_HEXAGON_VERBOSE)" ) 154parser .add_argument ("--hex-profile" ,help = "Enable NPU/Hexagon profiling and performance metrics print (GGML_HEXAGON_PROFILE)" ) 155parser .add_argument ("--hex-nhvx" ,help = "Number of HVX units to use (GGML_HEXAGON_NHVX)" ) 156parser .add_argument ("--hex-nhmx" ,help = "Number of HMX units to use. 0 disables HMX power-up (GGML_HEXAGON_NHMX)" ) 157parser .add_argument ("--hex-hostbuf" ,help = "Enable host buffers (GGML_HEXAGON_HOSTBUF)" ) 158parser .add_argument ("--hex-opbatch" ,help = "Maximum number of operations to batch into a single HTP execution (GGML_HEXAGON_OPBATCH)" ) 159parser .add_argument ("--hex-opqueue" ,help = "Size of the asynchronous NPU operation queue (GGML_HEXAGON_OPQUEUE)" ) 160parser .add_argument ("--hex-oppoll" ,default = "1" ,help = "Enable (1) or Disable (0) polling for NPU opbatch completion (GGML_HEXAGON_OPPOLL) (default: 1)" ) 161parser .add_argument ("--hex-opfilter" ,help = "Regex pattern to filter/select which operators are offloaded to NPU (GGML_HEXAGON_OPFILTER)" ) 162parser .add_argument ("--hex-opfusion" ,help = "NPU graph node fusion optimization level (0: disabled, 1: enabled) (GGML_HEXAGON_OPFUSION)" ) 163parser .add_argument ("--hex-vmem" ,help = "Maximum NPU VMEM size limit in MB to allocate (GGML_HEXAGON_VMEM)" ) 164parser .add_argument ("--hex-mbuf" ,help = "Maximum host buffer size limit in MB to allocate (GGML_HEXAGON_MBUF)" ) 165parser .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" ) 166parser .add_argument ("--hex-fa-select" ,help = "Select Flash Attention kernel (GGML_HEXAGON_FA_SELECT) 2:HMX,1:HVX,0:disable" ) 167parser .add_argument ("--hex-ar-select" ,help = "Select All-Reduce kernel (GGML_HEXAGON_AR_SELECT) 1:enable,0:disable" ) 168parser .add_argument ("--hex-etm" ,help = "Enable Embedded Trace Macrocell hardware tracing / trace logging (GGML_HEXAGON_ETM)" ) 169parser .add_argument ("--hex-arch" ,help = "Target Hexagon NPU architecture version override (v73, v75, v79, v81, etc.) (GGML_HEXAGON_ARCH)" ) 170parser .add_argument ("--hex-optrace" ,help = "Trace buffer size in number of records (GGML_HEXAGON_OPTRACE)" ) 171 172# OpenCL specific parameters 173parser .add_argument ("--cl-platform" ,help = "Select OpenCL platform name/regex (e.g. Qualified Qualcomm OpenCL platform) (GGML_OPENCL_PLATFORM)" ) 174parser .add_argument ("--cl-device" ,help = "Select OpenCL device name/regex (e.g. Adreno GPU) (GGML_OPENCL_DEVICE)" ) 175parser .add_argument ("--cl-opfilter" ,help = "Regex pattern to filter/select which operators are offloaded to OpenCL (GGML_OPENCL_OPFILTER)" ) 176parser .add_argument ("--cl-disable-fusion" ,action = "store_true" ,help = "Disable OpenCL kernel fusion optimizations (GGML_OPENCL_DISABLE_FUSION)" ) 177parser .add_argument ("--cl-cache-dir" ,help = "Directory path to store compiled OpenCL program binaries (GGML_OPENCL_KERNEL_CACHE_DIR)" ) 178parser .add_argument ("--cl-cache-debug" ,help = "Enable verbose debugging logs for the kernel caching system (GGML_OPENCL_KERNEL_CACHE_DEBUG)" ) 179parser .add_argument ("--cl-fa-tune" ,action = "store_true" ,help = "Enable automatic Flash Attention kernel autotuning (GGML_OPENCL_FA_TUNE)" ) 180parser .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)" ) 181parser .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 183args = parser .parse_args (run_args ) 184 185if not cmd_args : 186parser .print_help () 187logger .error ("\nError: No command specified after '--'" ) 188sys .exit (1 ) 189 190target_type = None 191target_val = None 192target_prefix = None 193if args .target : 194target_type ,target_val = parse_target (args .target ) 195if not target_type : 196logger .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." ) 197sys .exit (1 ) 198target_prefix = args .target .split (":" ,1 )[0 ] 199 200# Resolve install directory 201install_dir = args .install_dir 202if not install_dir : 203if target_prefix : 204suffix = "-dbg" if args .debug else "" 205install_dir = f"pkg- { target_prefix } { suffix } " 206else : 207# Smart branch folder detection for local run if default is not set 208prefixes = ("wos" ,"windows" ,"lnx" ,"linux" ,"ubuntu" ,"adb" ,"android" ) 209suffixes = ("-dbg" ,"" )if args .debug else ("" ,"-dbg" ) 210found = False 211for suffix in suffixes : 212for prefix in prefixes : 213test_path = f"./pkg- { prefix } { suffix } /llama.cpp" 214if os .path .exists (test_path ): 215install_dir = f"pkg- { prefix } { suffix } " 216found = True 217break 218if found : 219break 220if not install_dir : 221install_dir = "pkg-android" # Fallback default 222 223# Host side package path 224package_path = os .path .join (install_dir ,"llama.cpp" ) 225 226# Environment variables to map 227env_vars = {} 228 229def set_env (env_name ,opt_val ): 230if opt_val is not None : 231env_vars [env_name ]= str (opt_val ) 232 233# Resolve and filter devices (HTP vs OpenCL) 234device_in_cmd = None 235for i ,arg in enumerate (cmd_args ): 236if arg == "--device" and i + 1 < len (cmd_args ): 237device_in_cmd = cmd_args [i + 1 ] 238break 239elif arg .startswith ("--device=" ): 240device_in_cmd = arg .split ("=" ,1 )[1 ] 241break 242 243if args .devices is not None : 244devices_val = args .devices 245elif device_in_cmd is not None : 246devices_val = device_in_cmd 247else : 248devices_val = "HTP0" 249 250if devices_val .isdigit (): 251hex_devices = devices_val 252cl_device = "" 253else : 254parts = split_device_list (devices_val ) 255# Any device containing "htp" is Hexagon, rest is OpenCL 256hex_parts = [p for p in parts if "htp" in p .lower ()] 257cl_parts = [ 258p for p in parts 259if "htp" not in p .lower () 260and p .lower ()not in ("none" ,"cpu" ) 261and not p .lower ().startswith ("gpuopencl" ) 262 ] 263hex_devices = "," .join (hex_parts ) 264cl_device = "," .join (cl_parts ) 265 266# Set Hexagon devices 267if hex_devices : 268env_vars ["GGML_HEXAGON_DEVICES" ]= hex_devices 269 270normalize_cmd_device_args (cmd_args ) 271 272# Set OpenCL device (unless overridden by --cl-device) 273final_cl_device = args .cl_device if args .cl_device is not None else cl_device 274if final_cl_device : 275env_vars ["GGML_OPENCL_DEVICE" ]= final_cl_device 276 277# Map shared & backend-specific parameters with correct overrides 278 279# Verbose logging mapping 280hex_verbose_val = args .hex_verbose if args .hex_verbose is not None else args .verbose 281set_env ("GGML_HEXAGON_VERBOSE" ,hex_verbose_val ) 282 283cl_cache_debug_val = args .cl_cache_debug if args .cl_cache_debug is not None else args .verbose 284set_env ("GGML_OPENCL_KERNEL_CACHE_DEBUG" ,cl_cache_debug_val ) 285 286# Profiling mapping 287hex_profile_val = args .hex_profile if args .hex_profile is not None else args .profile 288set_env ("GGML_HEXAGON_PROFILE" ,hex_profile_val ) 289 290if args .cl_fa_tune or args .profile is not None : 291env_vars ["GGML_OPENCL_FA_TUNE" ]= "1" 292 293# Other Hexagon environment variables 294set_env ("GGML_HEXAGON_NHVX" ,args .hex_nhvx ) 295set_env ("GGML_HEXAGON_NHMX" ,args .hex_nhmx ) 296set_env ("GGML_HEXAGON_HOSTBUF" ,args .hex_hostbuf ) 297set_env ("GGML_HEXAGON_OPBATCH" ,args .hex_opbatch ) 298set_env ("GGML_HEXAGON_OPQUEUE" ,args .hex_opqueue ) 299set_env ("GGML_HEXAGON_OPPOLL" ,args .hex_oppoll ) 300set_env ("GGML_HEXAGON_OPFILTER" ,args .hex_opfilter ) 301set_env ("GGML_HEXAGON_OPFUSION" ,args .hex_opfusion ) 302set_env ("GGML_HEXAGON_VMEM" ,args .hex_vmem ) 303set_env ("GGML_HEXAGON_MBUF" ,args .hex_mbuf ) 304set_env ("GGML_HEXAGON_MM_SELECT" ,args .hex_mm_select ) 305set_env ("GGML_HEXAGON_FA_SELECT" ,args .hex_fa_select ) 306set_env ("GGML_HEXAGON_AR_SELECT" ,args .hex_ar_select ) 307set_env ("GGML_HEXAGON_ETM" ,args .hex_etm ) 308set_env ("GGML_HEXAGON_ARCH" ,args .hex_arch ) 309set_env ("GGML_HEXAGON_OPTRACE" ,args .hex_optrace ) 310set_env ("MTMD_BACKEND_DEVICE" ,args .mtmd_device ) 311 312# OpenCL environment variables 313set_env ("GGML_OPENCL_PLATFORM" ,args .cl_platform ) 314set_env ("GGML_OPENCL_OPFILTER" ,args .cl_opfilter ) 315set_env ("GGML_OPENCL_KERNEL_CACHE_DIR" ,args .cl_cache_dir ) 316 317if args .cl_disable_fusion : 318env_vars ["GGML_OPENCL_DISABLE_FUSION" ]= "1" 319 320if args .cl_adreno_xmem : 321env_vars ["GGML_OPENCL_ADRENO_XMEM_GEMM" ]= "1" 322 323if args .cl_adreno_large_buffer : 324env_vars ["GGML_OPENCL_ADRENO_USE_LARGE_BUFFER" ]= "1" 325 326if args .sched_debug : 327env_vars ["GGML_SCHED_DEBUG" ]= "2" 328 329# Resolve executable path 330executable = cmd_args [0 ] 331known_binaries = ["llama-cli" ,"llama-bench" ,"llama-completion" ,"llama-mtmd-cli" ,"test-backend-ops" ] 332if executable in known_binaries : 333if target_type in ("android" ,"linux" ): 334resolved_exec = f"./bin/ { executable } " 335else : 336if platform .system ()== "Windows" : 337resolved_exec = os .path .normpath (os .path .join (package_path ,"bin" ,f" { executable } .exe" )) 338else : 339resolved_exec = os .path .normpath (os .path .join (package_path ,"bin" ,executable )) 340cmd_args [0 ]= resolved_exec 341 342# Infer device string to pass to the tool 343basename = os .path .basename (executable ) 344if basename .endswith (".exe" ): 345basename = basename [:- 4 ] 346 347device_val = None 348if basename == "test-backend-ops" : 349for i in range (len (cmd_args )): 350if cmd_args [i ]in ("-p" ,"--params" )and i + 1 < len (cmd_args ): 351val = cmd_args [i + 1 ] 352new_val = "" 353for j ,char in enumerate (val ): 354if char in ('[' ,']' ): 355if j > 0 and val [j - 1 ]== '\\' : 356new_val += char 357else : 358new_val += '\\' + char 359else : 360new_val += char 361cmd_args [i + 1 ]= new_val 362 363has_b = any (arg == "-b" for arg in cmd_args ) 364if not has_b : 365if args .devices : 366device_val = device_arg_from_devices (args .devices ) 367else : 368device_val = "HTP0" 369if device_val : 370cmd_args += ["-b" ,device_val ] 371else : 372has_device = any (arg .startswith ("--device" )for arg in cmd_args ) 373if not has_device : 374if args .devices : 375device_val = device_arg_from_devices (args .devices ) 376else : 377device_val = "HTP0" 378 379if device_val : 380cmd_args += ["--device" ,device_val ] 381 382# Automatically add -v to known llama tools if sched-debug, verbose, or profile are set 383verbose_trigger = ( 384args .sched_debug 385or args .verbose is not None 386or args .profile is not None 387or args .hex_verbose is not None 388or args .hex_profile is not None 389or args .hex_optrace is not None 390 ) 391if verbose_trigger and basename in ("llama-cli" ,"llama-completion" ,"llama-bench" ,"llama-server" ,"llama-mtmd-cli" ): 392if "-v" not in cmd_args and "--verbose" not in cmd_args : 393cmd_args .append ("-v" ) 394 395# Inject defaults for llama-cli, llama-completion, and llama-server if not overridden by the user 396if basename in ("llama-cli" ,"llama-completion" ,"llama-server" ): 397if "-ngl" not in cmd_args and "--n-gpu-layers" not in cmd_args : 398cmd_args += ["-ngl" ,"99" ] 399if "-fa" not in cmd_args and "--flash-attn" not in cmd_args : 400cmd_args += ["-fa" ,"on" ] 401 402# Use ubatch-size 1024 for hexagon backend (HTP devices) 403if hex_devices and basename in ("llama-cli" ,"llama-completion" ,"llama-server" ,"llama-bench" ): 404if "--ubatch-size" not in cmd_args and "-ub" not in cmd_args : 405cmd_args += ["--ubatch-size" ,"1024" ] 406elif basename in ("llama-cli" ,"llama-completion" ,"llama-server" ): 407if "--ubatch-size" not in cmd_args and "-ub" not in cmd_args : 408cmd_args += ["--ubatch-size" ,"1024" ] 409 410if basename in ("llama-cli" ,"llama-completion" ,"llama-server" ,"llama-bench" ): 411if "-t" not in cmd_args and "--threads" not in cmd_args : 412cmd_args += ["-t" ,"6" ] 413 414# Resolve target directory on device 415target_dir = args .target_dir 416if not target_dir : 417target_dir = "/data/local/tmp/llama.cpp" if target_type == "android" else "~/llama.cpp" 418 419if target_type == "android" : 420# Run via ADB 421adb_base = ["adb" ] 422if target_val :# serial 423adb_base += ["-s" ,target_val ] 424 425env_parts = [ 426"LD_LIBRARY_PATH=./lib" , 427"ADSP_LIBRARY_PATH=./lib" 428 ] 429for k ,v in env_vars .items (): 430env_parts .append (f" { k } = { v } " ) 431env_str = " " .join (env_parts ) 432 433cmd_str = shlex_join (cmd_args ) 434adb_shell_cmd = f"cd { target_dir } && ulimit -c unlimited && { env_str } { cmd_str } " 435full_cmd = adb_base + ["shell" ,adb_shell_cmd ] 436 437logger .info (f"+ { ' ' . join ( full_cmd ) } " ) 438res = subprocess .run (full_cmd ) 439sys .exit (res .returncode ) 440 441elif target_type == "linux" : 442ssh_host = target_val 443if not ssh_host : 444logger .error ("Error: SSH host not specified in target (e.g. use linux:user@host, lnx:user@host, or ubuntu:user@host). Cannot execute." ) 445sys .exit (1 ) 446 447# Linux remote run via SSH 448env_parts = [ 449"LD_LIBRARY_PATH=./lib" , 450"ADSP_LIBRARY_PATH=./lib" 451 ] 452for k ,v in env_vars .items (): 453env_parts .append (f" { k } = { v } " ) 454env_str = " " .join (env_parts ) 455 456cmd_str = shlex_join (cmd_args ) 457ssh_shell_cmd = f"cd { target_dir } && ulimit -c unlimited && { env_str } { cmd_str } " 458full_cmd = ["ssh" ,ssh_host ,ssh_shell_cmd ] 459 460logger .info (f"+ { ' ' . join ( full_cmd ) } " ) 461res = subprocess .run (full_cmd ) 462sys .exit (res .returncode ) 463 464elif target_type == "windows" : 465logger .info ("Windows target execution is currently a stub." ) 466sys .exit (0 ) 467 468else : 469# Run locally 470local_env = os .environ .copy () 471lib_dir = os .path .normpath (os .path .join (package_path ,"lib" )) 472local_env ["ADSP_LIBRARY_PATH" ]= lib_dir 473if platform .system ()== "Windows" : 474local_env ["PATH" ]= lib_dir + os .path .pathsep + local_env .get ("PATH" ,"" ) 475else : 476local_env ["LD_LIBRARY_PATH" ]= lib_dir + os .path .pathsep + local_env .get ("LD_LIBRARY_PATH" ,"" ) 477 478for k in MANAGED_ENV_NAMES : 479local_env .pop (k ,None ) 480for k ,v in env_vars .items (): 481local_env [k ]= v 482 483logger .info (f"+ { shlex_join ( cmd_args ) } " ) 484res = subprocess .run (cmd_args ,env = local_env ) 485sys .exit (res .returncode ) 486 487 488if __name__ == "__main__" : 489try : 490main () 491except KeyboardInterrupt : 492logger .info ("\nInterrupted by user." ) 493sys .exit (130 )