Files
2026-07-13 13:33:03 +08:00

213 lines
7.2 KiB
Python

#!/usr/bin/python
import sys
import json
import os
import subprocess
import json
import concurrent.futures
import multiprocessing
import resource
post_treat = {}
qnn_sdk = os.environ["QNN_SDK_ROOT"]
print(qnn_sdk)
with open(sys.argv[1]) as f:
post_treat = json.load(f)
soc_id = int(sys.argv[2])
dsp_arch = sys.argv[3]
print('soc_id:', soc_id, "; dsp_arch:", dsp_arch)
qnn_bin_path = os.path.join(qnn_sdk, 'bin', 'x86_64-linux-clang')
qnnModelLibGenerator = os.path.join(qnn_bin_path, 'qnn-model-lib-generator')
qnnContextBinaryGenerator = os.path.join(qnn_bin_path, 'qnn-context-binary-generator')
merges = post_treat["merge"]
cache_dir = 'res'
if 'cache' in post_treat:
cache_dir = post_treat['cache']
clean_tmp = True
def raise_stack_limit():
try:
soft, hard = resource.getrlimit(resource.RLIMIT_STACK)
if soft == resource.RLIM_INFINITY:
return
target = hard
if hard == resource.RLIM_INFINITY:
target = resource.RLIM_INFINITY
if target > soft or target == resource.RLIM_INFINITY:
resource.setrlimit(resource.RLIMIT_STACK, (target, hard))
print(f"[QNN] raise stack limit: {soft} -> {target}", flush=True)
except Exception as e:
print(f"[QNN] skip raising stack limit: {e}", flush=True)
def run_subprocess(cmd, cwd=None, retries=3):
result = None
for attempt in range(1, retries + 1):
result = subprocess.run(cmd, cwd=cwd, capture_output=True, text=True)
if result.stdout:
print(result.stdout, flush=True)
if result.stderr:
print(result.stderr, flush=True)
if result.returncode == 0:
return result
print(f"[Retry {attempt}/{retries}] command failed: {' '.join(cmd)}", flush=True)
return result
def process_src(task):
i, src = task
graphname = src.split('/')[-1]
workdir = os.path.join(os.getcwd(), src)
raw_files = sorted(
file_name for file_name in os.listdir(workdir)
if file_name.endswith(".raw")
)
if not raw_files:
raise RuntimeError(f"No .raw files found for src={src}")
tar_cmd = ["tar", "-cf", graphname + ".bin", *raw_files]
tar_result = run_subprocess(tar_cmd, cwd=workdir, retries=1)
if tar_result.returncode != 0:
raise RuntimeError(f"Tar failed for src={src}: {' '.join(tar_cmd)}")
if clean_tmp:
rm_raw_result = run_subprocess(["rm", *raw_files], cwd=workdir, retries=1)
if rm_raw_result.returncode != 0:
raise RuntimeError(f"Remove raw failed for src={src}")
compile_cmd = [
"python3",
qnnModelLibGenerator,
"-c", os.path.join(workdir, graphname + '.cpp'),
"-b", os.path.join(workdir, graphname + '.bin'),
"-t", "x86_64-linux-clang",
"-o", workdir,
]
compile_result = run_subprocess(compile_cmd, retries=3)
if compile_result.returncode != 0:
raise RuntimeError(f"Compile failed for src={src}: {' '.join(compile_cmd)}")
if clean_tmp:
rm_bin_result = run_subprocess(
["rm", os.path.join(workdir, graphname + '.bin')],
retries=1,
)
if rm_bin_result.returncode != 0:
raise RuntimeError(f"Remove bin failed for src={src}")
lib_path = os.path.join(workdir, 'x86_64-linux-clang', 'lib' + graphname + '.so')
return i, graphname, workdir, lib_path
context_config = {
"backend_extensions": {
"shared_library_path": os.path.join(qnn_sdk, "lib","x86_64-linux-clang","libQnnHtpNetRunExtensions.so"),
"config_file_path": "./htp_backend_extensions.json"
}
}
htp_so = os.path.join(qnn_sdk, 'lib','x86_64-linux-clang','libQnnHtp.so')
htp_backend_extensions = {
"graphs": [
{
"vtcm_mb": 8,
"O": 3.0,
"fp16_relaxed_precision": 1,
"hvx_threads": 4
}
],
"devices": [
{
"soc_id": soc_id,
"dsp_arch": dsp_arch,
"cores": [
{
"core_id": 0,
"perf_profile": "burst",
"rpc_control_latency": 100
}
]
}
],
"context": {
"weight_sharing_enabled": True
}
}
def process_merge(key, merge_index):
srcs = merges[key]
dstname = key.split('/')[-1].replace('.bin', '')
graphs = [None] * len(srcs)
libs = [None] * len(srcs)
workdirs = [None] * len(srcs)
src_workers = max(1, min(os.cpu_count()//2 or 1, len(srcs)))
with concurrent.futures.ProcessPoolExecutor(
max_workers=src_workers,
mp_context=multiprocessing.get_context("fork"),
) as executor:
futures = {
executor.submit(process_src, (i, src)): src
for i, src in enumerate(srcs)
}
for future in concurrent.futures.as_completed(futures):
src = futures[future]
try:
i, graphname, workdir, lib_path = future.result()
except Exception as e:
raise RuntimeError(f"Process src failed for {src}: {e}") from e
graphs[i] = graphname
libs[i] = lib_path
workdirs[i] = workdir
local_htp_backend_extensions = json.loads(json.dumps(htp_backend_extensions))
local_context_config = json.loads(json.dumps(context_config))
htp_config_path = os.path.abspath(f'htp_backend_extensions_{merge_index}.json')
context_config_path = os.path.abspath(f'context_config_{merge_index}.json')
local_htp_backend_extensions['graphs'][0]['graph_names'] = graphs
local_context_config['backend_extensions']['config_file_path'] = htp_config_path
with open(htp_config_path, 'w') as f:
f.write(json.dumps(local_htp_backend_extensions, indent=4))
with open(context_config_path, 'w') as f:
f.write(json.dumps(local_context_config, indent=4))
libs_str = ",".join(libs)
context_cmd = [
qnnContextBinaryGenerator,
"--model", libs_str,
"--backend", htp_so,
"--binary_file", dstname,
"--config_file", context_config_path,
"--output_dir", cache_dir,
]
context_result = run_subprocess(context_cmd, retries=3)
if context_result.returncode != 0:
raise RuntimeError(f"Context binary generation failed for key={key}: {' '.join(context_cmd)}")
if clean_tmp:
for workdir in workdirs:
rm_workdir_result = run_subprocess(["rm", "-rf", workdir], retries=1)
if rm_workdir_result.returncode != 0:
raise RuntimeError(f"Remove workdir failed: {workdir}")
merge_keys = list(post_treat["merge"])
raise_stack_limit()
merge_workers = max(1, min(os.cpu_count()//2 or 1, len(merge_keys)))
print(f"[Merge Parallel] running {len(merge_keys)} merge task(s), max_workers={merge_workers}", flush=True)
with concurrent.futures.ProcessPoolExecutor(
max_workers=merge_workers,
mp_context=multiprocessing.get_context("fork"),
) as executor:
futures = {
executor.submit(process_merge, key, merge_index): key
for merge_index, key in enumerate(merge_keys)
}
for future in concurrent.futures.as_completed(futures):
key = futures[future]
try:
future.result()
except Exception as e:
raise RuntimeError(f"Merge failed for key {key}: {e}") from e