Summary
geniex-qairt never requests multiple HTP (NSP) cores, so QAIRT models always execute on a single core. There is no way — from the CLI, the SDK, or the model bundle — to ask for multicore execution.
Repro
User reported that when running Qwen3-VL-4B-Instruct through the QAIRT plugin, the QNN backend reports:
238.9ms [VERBOSE] Error code 1000 returned for property query with key = 304
...
240.8ms [VERBOSE] <V> Multicore support is unavailable
and the model runs on one core only.
The bundle's HTP config declares a single core, and there is no documented way to add more:
{
"devices": [
{
"soc_model": 77,
"dsp_arch": "v73",
"cores": [
{ "core_id": 0, "perf_profile": "burst", "rpc_control_latency": 100 }
]
}
],
"memory": { "mem_type": "shared_buffer" },
"context": { "weight_sharing_enabled": true }
}
Slack discussion is here: https://qualcomm-ai-hub.slack.com/archives/C0BDK2Y7W6T/p1785836958145259
Where the gap is
-
No graph config is ever set. QNN_HTP_GRAPH_CONFIG_OPTION_NUM_CORES is present in the vendored header (third-party/geniex-qairt/qnn-api/include/HTP/QnnHtpGraph.h:186) but is referenced nowhere in core/ or qnn-api/src/. Same for QNN_HTP_GRAPH_CONFIG_OPTION_PARALLEL_GRAPH_EXECUTION_CONFIG.
-
initializeHtp is called with hardcoded arguments. core/src/model_init.cpp:98-117 passes a fixed literal for every parameter after ext_cfg (spill-fill size, mmap budget, etc.) and never builds a graph-config list. The prebuilt-context path goes through contextCreateFromBinary, so the graph inherits whatever core count the context binary was compiled with.
-
The HTP JSON is opaque to GenieX. modelConfigFromDirectory picks up htp_backend_ext_config.json when it sits beside the bundle (core/src/llm/llm_spec_loader.cpp:547-548) and forwards the path to BackendExtensionsConfigs (model_init.cpp:96). Parsing happens inside the closed-source QnnHtpNetRunExtensions library, so there is no GenieX-side validation, no defaulting, and no diagnostic when a requested core is unavailable.
-
The reported core count is read but unused for scheduling. qnn-api/src/QnnApi.cpp:1379 reads platformInfo->v1.hwDevices->v1.numCores solely to disable I/O memory estimation. It is never logged and never used to pick a core count.
Asks
Notes / open questions
The Error code 1000 ... key = 304 line immediately preceding Multicore support is unavailable is the HTP backend querying a DSP property the skel doesn't answer, i.e. the runtime concluded multicore is unavailable on this device/driver — independent of the JSON contents. Worth confirming whether multicore for this class of bundle is achievable at all at load time, or whether it strictly requires regenerating the context binaries with a multicore graph config on a SoC with more than one NSP core. If it's generation-time only, the fix is primarily documentation plus a clear diagnostic.
Summary
geniex-qairtnever requests multiple HTP (NSP) cores, so QAIRT models always execute on a single core. There is no way — from the CLI, the SDK, or the model bundle — to ask for multicore execution.Repro
User reported that when running Qwen3-VL-4B-Instruct through the QAIRT plugin, the QNN backend reports:
and the model runs on one core only.
The bundle's HTP config declares a single core, and there is no documented way to add more:
{ "devices": [ { "soc_model": 77, "dsp_arch": "v73", "cores": [ { "core_id": 0, "perf_profile": "burst", "rpc_control_latency": 100 } ] } ], "memory": { "mem_type": "shared_buffer" }, "context": { "weight_sharing_enabled": true } }Slack discussion is here: https://qualcomm-ai-hub.slack.com/archives/C0BDK2Y7W6T/p1785836958145259
Where the gap is
No graph config is ever set.
QNN_HTP_GRAPH_CONFIG_OPTION_NUM_CORESis present in the vendored header (third-party/geniex-qairt/qnn-api/include/HTP/QnnHtpGraph.h:186) but is referenced nowhere incore/orqnn-api/src/. Same forQNN_HTP_GRAPH_CONFIG_OPTION_PARALLEL_GRAPH_EXECUTION_CONFIG.initializeHtpis called with hardcoded arguments.core/src/model_init.cpp:98-117passes a fixed literal for every parameter afterext_cfg(spill-fill size, mmap budget, etc.) and never builds a graph-config list. The prebuilt-context path goes throughcontextCreateFromBinary, so the graph inherits whatever core count the context binary was compiled with.The HTP JSON is opaque to GenieX.
modelConfigFromDirectorypicks uphtp_backend_ext_config.jsonwhen it sits beside the bundle (core/src/llm/llm_spec_loader.cpp:547-548) and forwards the path toBackendExtensionsConfigs(model_init.cpp:96). Parsing happens inside the closed-sourceQnnHtpNetRunExtensionslibrary, so there is no GenieX-side validation, no defaulting, and no diagnostic when a requested core is unavailable.The reported core count is read but unused for scheduling.
qnn-api/src/QnnApi.cpp:1379readsplatformInfo->v1.hwDevices->v1.numCoressolely to disable I/O memory estimation. It is never logged and never used to pick a core count.Asks
qairt-plugin(ModelConfig→ graph configs →QNN_HTP_GRAPH_CONFIG_OPTION_NUM_CORES), rather than leaving it entirely to the prebuilt binary.numCoresand the effective core count at init, so "1 core" is visible without a verbose QNN trace.htp_backend_ext_config.jsonrequests cores the device doesn't expose.third-party/geniex-qairt/docs/README.md(which listshtp_backend_ext_config.jsonwithout documenting its schema) andnotes/run.md.Notes / open questions
The
Error code 1000 ... key = 304line immediately precedingMulticore support is unavailableis the HTP backend querying a DSP property the skel doesn't answer, i.e. the runtime concluded multicore is unavailable on this device/driver — independent of the JSON contents. Worth confirming whether multicore for this class of bundle is achievable at all at load time, or whether it strictly requires regenerating the context binaries with a multicore graph config on a SoC with more than one NSP core. If it's generation-time only, the fix is primarily documentation plus a clear diagnostic.