ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

GenieX SDK 深度解析:统一 C ABI + 动态插件后端在骁龙平台运行 LLM/VLM

GenieX SDK 深度解析:统一 C ABI + 动态插件后端在骁龙平台运行 LLM/VLM GenieX SDK 深度解析统一 C ABI 动态插件后端在骁龙平台运行 LLM/VLM【免费下载链接】GenieXRun frontier LLMs and VLMs locally on Qualcomm devices across NPU, GPU, and CPU with a few lines of code项目地址: https://gitcode.com/GitHub_Trending/ne/GenieX导读GenieX SDK 是整个 GenieX 项目的原生核心它通过一个统一的 C ABIsdk/include/geniex.h把 LLM大语言模型与 VLM多模态模型的推理能力抽象成稳定接口再以动态插件方式接入 llama.cpp 与 Qualcomm QNN/QAIRT 两套后端使同一个二进制能在 Windows ARM64、Linux ARM64、Android 三种骁龙平台上运行于 NPU、GPU、CPU。阅读本文后你将掌握 SDK 的双后端架构、设备别名cpu/gpu/npu/hybrid解析规则、C ABI 核心 API 的调用方式与数据结构、插件加载机制、多平台构建流程以及各语言绑定Go CLI / Python / Android如何全部收敛到这个库之上。SDK 是什么原生核心与统一 C ABI按 sdk/README.md 的定位GenieX SDK 是 GenieX 的原生核心native core。它对外只暴露一份 C 头文件geniex.h其中声明了统一的 C ABI用于在 Qualcomm 平台Windows ARM64、Linux ARM64、Android上跨多个推理后端运行 LLM 与 VLM。其设计关键点在于后端以动态插件形式加载一次构建只链接实际用到的引擎避免把 llama.cpp 与 QNN 两套运行时强行静态绑定在一起Go CLI、Python、Android 绑定都是这个库的薄封装thin wrappers所有语言入口最终都走到同一份 C ABI内存管理遵循 RAII 语义凡是库分配的堆内存统一用geniex_free()释放凡是创建的模型句柄统一用对应的 destroy 函数销毁。在顶层 README.md 的模型章节中两套运行时被描述为既要广泛的模型覆盖、又要骁龙平台峰值性能的组合llama.cpp 负责自带 GGUF场景Qualcomm AI Engine Directqairt负责预编译、峰值 NPU 性能场景。双后端引擎llama_cpp 与 qairtSDK 当前内置两个后端插件二者都面向 NPU但走的是相互独立的用户态软件栈、消费不同的模型格式因此不可互相替代runtime 按模型选择而非按性能自动切换插件引擎计算单元模型格式llama_cppllama.cpp / ggmlCPU、Adreno GPU、Hexagon NPUGGUFqairtQualcomm QNN / QAIRTHexagon NPUQAIRT.bin具体到实现见 notes/run.md 的 Runtimes 术语约定llama_cpp跑 GGUF 模型通过ggml-hexagon上 Hexagon NPU、通过 OpenCL 上 Adreno GPU、或纯 CPU支持device_id如HTP0、GPUOpenCL与config.n_gpu_layers两个输入共同决定计算单元qairt跑 QAIRT.bin分片模型仅通过 Qualcomm QNN 运行时上 Hexagon NPU。SDK 目录下的架构关系可以用官方 README 中的 mermaid 图表示从这张图可以直观看出 SDK 的插件→统一库→多语言绑定三层结构。顶层的libgeniex即 SDK 构建产物是唯一被各绑定依赖的组件后端差异被完全封装在插件层之下。SDK 目录布局官方 README 给出的目录职责划分如下均相对仓库根目录路径内容sdk/include/公共 C ABIgeniex.h与插件接口sdk/include/plugin/sdk/src/核心库——设备解析、LLM/VLM 桥接、插件注册表、日志桥接sdk/plugins/后端插件llama_cpp、qairtsdk/model-manager/Rust 实现的模型拉取器/缓存被 CLI 与各绑定共享sdk/benchmark/驱动公共 API 的 C 推理基准测试其中计算单元的别名解析cpu/gpu/npu/hybrid集中在 sdk/src/device.cpp 的geniex_resolve_device中实现——这是所有绑定的唯一事实来源single source of truth任何语言前端都不允许各自重新实现这张别名表。设备别名解析cpu / gpu / npu / hybridnotes/run.md 对别名表做了完整定义。调用方通过geniex_resolve_device声明见 sdk/include/geniex.h把面向用户的别名解析为具体后端期望的(device_id, n_gpu_layers)二元组别名发给 SDK 的device_idn_gpu_layers覆盖用途cpu空0纯 CPUgpuGPUOpenCL--ngl默认 -1Adreno 经 OpenCLnpuHTP0--ngl默认 -1固定单会话 HTP确定性更好但 LLM 上通常更慢hybrid空--ngl默认 -1llama.cpp 的逐张量 HTPCPU 调度器结合 sdk/src/device.cpp 的实现可以确认几条关键语义匹配是大小写不敏感、去空白后进行的to_lower_trim空字符串与auto都表示让 SDK 选插件默认值而两个插件默认都是npu路径qairt 只认 NPU对 qairt 插件cpu/gpu/hybrid或显式设备列表都会被强制收敛为device_idNPU且ngl0同时通过warning字段返回一条提示如qairt plugin only supports NPU inference; ignoring devicecpu and running on NPU但不会报错从而不破坏已有的 shell 管道llama_cpp 的--ngl默认 -1llama.cpp 语义为全部层都卸载因此gpu/npu/hybrid默认全量 offload只有cpu强制ngl0支持显式设备列表mode除了别名还可以是HTP0,HTP1,HTP2,HTP3这类逗号分隔的合法设备名列表is_device_token校验llama_cpp 会原样透传给 llama.cpp适合需要多 DSP 的配方qairt 场景下则被收敛并告警非法别名非空、非 auto、不在四张表内、也不是设备列表才会返回GENIEX_ERROR_COMMON_INVALID_DEVICE。llama_cpp 的两条 NPU 路径notes/run.md 还指出llama.cpp 插件根据device_id是否非空会产生性能差异显著的两条运行时路径实现见 sdk/plugins/llama_cpp/src/llm.cpphybriddevice_id为空 ngl-1走 llama.cpp 的逐张量调度器逐个张量检查由哪个已注册后端执行HTP 可算的算子放 HTP回退算子放 CPU是文档标注的快速路径fast pathnpudevice_idHTP0ngl-1运行时调用ggml_backend_dev_by_name(HTP0)并把模型钉在单一计算单元布局上禁用逐张量混合分配适合需要确定性布局或调试放置的场景。文档给出的经验法则追求最高吞吐用hybrid或不传--device需要确定性与可调试性用npu。此外当device_id以HTP0开头时运行时还会顺带把 KV cache 切到 Q8_0 并开启 flash-attnllm.cpp对应行但这与路径2的性能结论正交——钉住 HTP0 的路径即便开启这些优化仍比 hybrid 慢。如何确认实际走了哪条路径notes/run.md 给出了几条实用的自检手段Python设置GENIEX_LOGINFOPython 绑定安装的geniex_set_log回调会把Found device: HTP0、Using N device(s)等 SDK 消息路由到 stderr看到Found device: …说明走的是钉住 HTP0 的npu路径Windows 任务管理器hybrid 会点亮 NPU 曲线钉住 HTP0 时 CPU 曲线被占满宿主线程在整段推理中忙等 HTP线程池日志除cpu外的卸载路径会打印threadpool tuned for offload: 6 threads pinned to cores [2, 8), strict, poll1000对应插件里镜像上游的固定-t 6 --cpu-mask 0xfcsdk/plugins/llama_cpp/src/threadpool.cpp可用geniex_ModelConfig.n_threads覆盖若看到Device … not found, skipping说明运行时已加载但 GGML 后端 DLL 缺失——HTP 场景需确认测试签名仍开启OpenCL 场景需确认ggml-opencl.dll位于sdk/pkg-geniex/lib/llama_cpp/。C ABI 核心 API 面geniex.h 是 SDK 对外唯一必须包含的头文件全部函数以geniex_前缀导出GENIEX_API宏处理 Windows DLL 导入导出与 Unix visibility。以下按官方头文件的分节梳理核心 API。生命周期与运行时API说明geniex_init()/geniex_deinit()初始化/反初始化运行时定义库的生命周期非线程安全geniex_register_plugin()以函数指针注册自定义插件线程安全geniex_set_log(callback)在 init 之前安装自定义日志回调geniex_get_plugin_list()查询已注册插件列表返回的plugin_ids用geniex_free释放geniex_get_device_list()查询某插件的可用设备如HTP0geniex_resolve_device()设备别名解析见上一节geniex_set_qairt_runtime_path(path)让 qairt 插件改用指定路径的 QAIRT 运行时而非随插件捆绑的版本必须在geniex_init之前调用geniex_version()/geniex_get_plugin_version(plugin_id)库版本 / 各插件自带版本如 qairt 运行时版本、llama.cpp 构建 commitgeniex_free(ptr)释放库分配的内存值得强调geniex_set_qairt_runtime_path的语义对应 notes/run.md 的 Using a custom QNN library 一节由于 QNN 库每个进程只加载一次且从不卸载运行时的切换锁定在geniex_init之后再调用会返回GENIEX_ERROR_COMMON_ALREADY_INITIALIZEDgeniex_deinit/geniex_init循环也不会解锁。该 API 优先于环境变量GENIEX_QAIRT_LIB优先级API 环境变量 捆绑运行时而且是 Android 上唯一可行的注入途径JVM 无法setenv。路径既可以是 QAIRT SDK 根目录自动取lib/triple下的宿主库并把ADSP_LIBRARY_PATH指向每个 Hexagon DSP skel 目录也可以是平铺放着QnnHtp.dll/QnnSystem.dll的目录。可用--log info观察host libs:一行确认解析结果。错误码体系所有函数以错误码返回负值为错误。错误码按段划分GENIEX_SUCCESS 0公共错误-100xxx非法输入、非法设备、内存分配失败、文件未找到、网络失败、调用方取消、未初始化/重复初始化、Hub 鉴权401/403、模型未找到404、限流429、Hub 服务器错误5xx、不支持的操作、清单解析失败、芯片组不可用、插件不支持参数、磁盘空间不足等模型加载错误-1002xx模型加载失败、模型格式非法插件错误-1003xx插件加载失败、插件非法LLM 错误-200xxx分词失败、上下文长度超限、生成失败、提示词过长VLM 错误-201xxx图像加载/格式错误、音频加载/格式错误、多模态生成失败、VLM prefix 缓存复用失败。可用geniex_get_error_message(error_code)获取错误码对应的可读字符串。LLM 接口LLM 侧的核心数据结构与调用链如下geniex_LlmCreateInputmodel_path、tokenizer_path、configgeniex_ModelConfig、plugin_id、device_idgeniex_ModelConfign_ctx0 表示取自模型、n_threads/n_threads_batch、n_batch/n_ubatch、n_seq_max、n_gpu_layers卸载层数-1 全部、可选 chat template 路径/内容以及**投机解码speculative decoding仅 llama_cpp**配置spec_typedraft-mtp/draft-eagle3/draft-simple需要草稿模型ngram-simple/ngram-map-k等为无草稿模型的自投机、spec_draft_model、spec_n_max/spec_n_min/spec_p_min生命周期geniex_llm_create→geniex_llm_generate流式/geniex_llm_reset→geniex_llm_destroyKV Cachegeniex_llm_save_kv_cache/geniex_llm_load_kv_cache输入输出结构当前只携带路径字段输出保留字段传 NULL 即可Chat Templategeniex_llm_apply_chat_template输入为geniex_LlmChatMessage数组支持user/assistant/system/tool角色与geniex_ToolCall工具调用结构、可选 tools JSON、enable_thinking、add_generation_prompt输出formatted_text需geniex_free流式生成geniex_LlmGenerateInput可传prompt_utf8或预分词input_ids二者互斥都不传返回GENIEX_ERROR_COMMON_INVALID_INPUT用input_ids时调用方负责自行携带 BOS/EOS 特殊 token。回调geniex_token_callback每次收到一个 tokenuser_data透传模型元数据geniex_llm_get_model_info返回vocab_size、bos_token、add_bos供需要自行构造input_ids的调用方如做随机 id prefill 的基准在绕过分词器时使用前向 logitsgeniex_llm_forward_logits做单次非自回归前向并直接返回 LM head 输出不做采样、不跑 decode 循环用于困惑度/MMLU/MMMU 这类按 logits 打分的离线精度指标top_n0时每行只保留降序 top-N logits避免全词表输出达到数百 MB不支持该能力的插件返回GENIEX_ERROR_COMMON_PARAM_NOT_SUPPORTEDILlm.h 的默认实现即如此。geniex_LlmGenerateOutput除full_text需geniex_free外还携带geniex_ProfileData。采样与生成配置geniex_SamplerConfigtemperature0.0–2.0、top_p0.0–1.0、top_k、min_p、repetition_penalty、presence_penalty、frequency_penalty、seed-1 为随机、grammar_path/grammar_stringBNF 风格文法约束geniex_GenerationConfigmax_tokens、stop序列数组stop_count个、sampler_config以及多模态支持image_paths/image_count、audio_paths/audio_countVLM 用可 NULL还有上下文长度溢出处理sliding_window与sliding_window_n_keep仅 qairt 生效llama_cpp 总是做 context-shift忽略该字段。开启滑动窗口后超长时驱逐最老的上下文 token 而不是报错sliding_window_n_keep指定保留的锚定 token 数0 插件默认 4。ProfileData性能指标字段geniex_ProfileData定义了推理各阶段的可量化指标与 notes/run.md 的 Performance metrics 一节一一对应指标字段测量阶段ttftttft生成开始 → 首个采样 tokenVLM 场景包含媒体编码器不可与纯 prefill 数字直接比较媒体时间media_time仅视觉/音频编码器耗时纯文本运行时为 0prefill 时间prompt_time提示词 token 过模型的 prefillVLM 含媒体softtoken 的 prefill、不含编码器prefill 速度prefill_speedprompt_tokens / prompt_timedecode 时间/速度decode_time/decoding_speed首个 token → 最后一个 token 的生成阶段此外还有prompt_tokensVLM 下文本 媒体 token 合计、generated_tokens、投机解码统计draft_n_total/draft_n_accepted以及stop_reasoneos/length/user/stop_sequence/context_length。对 VLM 而言近似有ttft ≈ media_time prompt_time。VLM 接口VLM 与 LLM 保持对称的 API 面geniex_vlm_create/destroy/reset、geniex_vlm_generate、geniex_vlm_apply_chat_template差异在于geniex_VlmCreateInput多了mmproj_path多模态投影权重与可选的vit_device_id视觉编码器的设备覆盖消息体为geniex_VlmChatMessage内容采用geniex_VlmContent的(type, text)数组type可取text/image/audio等text承载实际文本、URL 或特殊 tokengeniex_vlm_get_capabilities返回geniex_VlmCapabilitiessupports_vision/supports_audiollama.cpp 插件反映mtmd_support_vision/audio不暴露模态探测的插件如 QAIRT两个标志默认 falsegeniex_VlmApplyChatTemplateInput额外支持grounding为 deepseek-ocr 添加 grounding token。插件机制动态加载与注册SDK 的动态插件设计在源码层面体现为清晰的接口与加载器插件接口sdk/include/plugin/Plugin.hC 抽象类geniex::Plugin提供version()、get_device_list()、create_llm()、create_vlm()并规定每个插件动态库必须导出两个 C 符号plugin_id()返回插件标识字符串与create_plugin()返回插件实例。on_foreign_plugin_load()钩子在 Registry 派发到另一插件 id 时被调用用于释放可能与对方冲突的进程级资源如 HTP FastRPC 通道LLM/VLM 抽象sdk/include/plugin/ILlm.h、IVlm.hILlm::create/generate/reset/save_kv_cache/apply_chat_template等纯虚方法构成插件实现的上限契约get_model_info与forward_logits提供默认返回GENIEX_ERROR_COMMON_PARAM_NOT_SUPPORTED的实现让不支持的插件保持可构建注册表sdk/src/registry.cppRegistry::scan_plugins()从GENIEX_PLUGIN_PATH环境变量未设置时回退到共享库所在目录并回写环境变量出发递归扫描子目录寻找名为geniex_plugin.dllWindows或libgeniex_plugin.soLinux/Android的插件库加载成功后以plugin_id为 key 存入注册表单个插件加载失败只记录到failed_plugins不影响其余插件。加载失败的库可通过geniex_get_plugin_list前的诊断路径排查CMake 开关sdk/CMakeLists.txtGENIEX_PLUGIN_LLAMA_CPP默认 ON、GENIEX_PLUGIN_QAIRT默认 ON、GENIEX_BENCHMARK默认 OFF、GENIEX_CPU_ONLY默认 OFF。此外 configure 阶段会自动把 sdk/patches/ 下的 llama.cpp 补丁Hexagon release-sessions、Adreno OpenCL 编译器规避、zero-free-mem 拆分、xmem GEMM 关闭应用到third-party/llama.cpp通过git apply --reverse --check探测已打补丁状态以防重复应用。构建 SDKCMake 预设与 pkg-geniex 产物构建与安装的整体顺序详见 notes/build.md先构建并安装 SDK 到sdk/pkg-geniex/再构建 CLI 与绑定。默认的 local-SDK 模式下CLI 直接链接sdk/pkg-geniex/lib/libgeniex.soLinux或geniex.dllWindows。以 Windows ARM64骁龙为例cd sdk cmake --preset arm64-windows-snapdragon-release -B build cmake --build build -j cmake --install build --prefix pkg-geniexLinux/Android 侧在官方工具链容器内交叉编译arm64-linux-snapdragon-debug/arm64-android-snapdragon-debug预设另有面向无 NPU 的 ARMv8.0 基线板的arm64-linux-cpu-*与arm64-android-cpu-*变体CPU-only无 QAIRT/Hexagon/OpenCL用于规避 LSE 原子指令陷阱见 build.md 对上游 issue 的说明。cmake --install之后库与头文件统一落在pkg-geniex/供各绑定与 CLI 消费Android 的assembleRelease会原样打包sdk/pkg-geniex/lib/的内容因此同一 Gradle 工程即可产出 CPU-only 的 AAR。SDK 构建还内嵌了 Rust 编写的 model-managersdk/model-manager/CMake 在 configure 时以cargo build生成静态库Windows 上为geniex_model.lib其余平台为libgeniex_model.a并妥善处理 Android/交叉编译下的链接器与CC/AR环境。model-manager 通过geniex_model_*C API头文件见 sdk/model-manager/include/geniex_model.h对外提供模型拉取、缓存、查询与来源抽象Hugging Face、ModelScope、DockerHub、AI Hub、本地文件系统等。基准测试geniex-benchsdk/benchmark/ 提供名为geniex-bench的 C 推理基准直接驱动公共 C API详见 sdk/benchmark/README.md。一次调用跑一个(plugin, device, model)单元warmup 多次测量输出/写出 TTFT、prefill_tps、decode_tps、gen_tokens。参数命名对齐 llama.cpp 的llama-bench-r/--repetitions、-n/--n-gen、-c/--ctx-size、-t/--threads、-m/--model、--n-gpu-layers、--no-warmup方便两个工具间迁移模型参数既可以是本地路径geniex bundle 目录或.gguf文件/所在目录也可以是 model-manager idorg/repo[:quant]后者首次使用时自动下载并复用缓存由GENIEX_BENCHMARKCMake 开关控制骁龙预设的 debug/release 均默认开启同一份二进制运行于 Windows、Android、Linux也被 Geniex Bench 使用。构建示例Windowscd sdk cmake --preset arm64-windows-snapdragon-release -B build cmake --build build -j --target geniex-bench # → build\benchmark\geniex-bench.exe cmake --install build --prefix pkg-geniex # 可选安装到 pkg-geniex\bin\geniex-bench.exe语言绑定全部收敛到同一个库SDK README 强调Go CLI、Python、Android 绑定都是这一个库的薄封装Python绑定文档见 bindings/python/README.mdFFI 层bindings/python/geniex/_ffi/_api.py直接透传geniex_resolve_device等 C 函数Android绑定文档见 bindings/android/README.mdJNI 层如 bindings/android/app/src/main/cpp/jniutils.cpp 的resolve_device同样是薄 shimGo CLIbindings/go/device.go 包装geniex_resolve_device。正因如此notes/run.md 明确指出修改别名语义 修改 sdk/src/device.cpp 重建 SDK 桥接层 必要时同步三处 FFI 桩struct 形状变化时。三层前端共享一份语义实现杜绝了语言间行为漂移。总结GenieX SDK 用一份稳定的 C ABIsdk/include/geniex.h统一了 llama.cpp 与 QNN/QAIRT 两套骁龙后端动态插件加载让只链接需要的引擎成为可能geniex_resolve_device让cpu/gpu/npu/hybrid的语义在 sdk/src/device.cpp 中唯一落地而 LLM/VLM 对称的 API 面含 KV cache、chat template、前向 logits、ProfileData、投机解码覆盖了从对话推理到精度基准的完整使用场景。无论是直接用 C/C 集成、还是借道 Python/Android/Go 绑定最终都汇聚到libgeniex这一层——这也是理解整个 GenieX 项目时最值得首先精读的模块。【免费下载链接】GenieXRun frontier LLMs and VLMs locally on Qualcomm devices across NPU, GPU, and CPU with a few lines of code项目地址: https://gitcode.com/GitHub_Trending/ne/GenieX创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表