Repository navigation
Qualcomm AI Engine Direct - [GenAI Pipeline Phase 2] PRB2 - Multi-graph Compilation - #24563
Open
qti-horodnic wants to merge 1 commit into
Open
qti-horodnic wants to merge 1 commit into
qti-horodnic wants to merge 1 commit into
Conversation
🔗 Helpful Links🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/24563
Note: Links to docs will display an error until the docs builds have been completed.
|
Contributor
Author
|
@pytorchbot label "release notes: qualcomm" |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR is the second of Phase 2 Stream B and completes the compilation stage. B1 lowered one graph to one
.pte. B2 adds the multi-graph and multi-component path:.pte;ExecutorchBackendConfig;Nothing under
examples/changes. The legacyllama.pyflow remains CI's reference until Phase 3, which is gated on PR-A2's parity tests.What's included
Group lowering (
default_compiler_adapter.py)compile_graph_group(): oneto_edge_transform_and_lower_to_qnncall with graph-name-keyed dicts formodule/inputs/compiler_specs, producing one multi-method.pte.KVManagerand RPC buffer between them with no requantization, so their KV scales must match. Weight sharing within one artifact guarantees that._validate_group()runs before any graph work. It rejects an empty group, a per-graph mapping (compile_specs,dep_table,passes_job) whose keys differ from the graph names, and a bundle with noinputs._needs_spill_fill()gatesupdate_spill_fill_sizeon the decoded specs showing HTP anduse_multi_contexts. The specs setuse_multi_contextsonly when the resolved shard count is above 1, so this is the legacynum_sharding > 1gate.compile_model()andcompile_graph_group()take a requiredexecutorch_config. The strategy chooses it per component; the adapter does not choose a config.LLM specialisation (
llm_compiler_adapter.py, new)LLMCompilerAdapter(DefaultCompilerAdapter), three overrides:_with_llm_skip_opsunionsllama.fallback.defaultinto any caller-suppliedskip_node_op_set. It marks the sharding boundary, so a caller's own skip set must not drop it._decode_constant_methodsdefaultsconstant_methodsto the decode graph'smeta, looked up byGRAPH_KV_FORWARDrather than by position._on_loweredprints per-graph delegation info whenverbose.Component spec policy (
compilation/component_spec_policy.py, new)ComponentSpecPolicy+get_component_policy()+build_component_compile_specs(), reproducing legacy'sgenerate_htp_compiler_speccall sites as a table keyed by artifact key. The decoder setsuse_mha2sha=Trueand honours sharding;tok_embeddingtakes defaults; encoders disable weight sharing and ignoreonline_prepare.use_fp16is per component: a component compiles in FP16 if and only if its graphs were not quantized (GraphBundle.quantized). This matches legacy, where FP16 followsskip_quantizeper component.build_executorch_config()builds each component'sExecutorchBackendConfigfromComponentSpecPolicy.uses_shared_buffer:tok_embeddingand the attention-sink evictor leave graph IO unallocated (alloc_graph_input/output=False), because the runner supplies RPC memory.Modality.compile.BuildQuantIo.Pass policy (
compilation/pass_policy.py, new) and model layout (compilation/model_layout.py, new)PassPolicytable +get_pass_policy()+build_pass_config()+build_component_pass_config(). Derives per component the base passes,SplitGraphplus both dependency edges (SplitGraph←FoldQDQ,TagQuantIO←SplitGraph), theI64toI32tokensskip, andTagQuantIOactivation with both_tag_iosequivalents transcribed rule-for-rule from legacy.meta,ControlArgsand the shard count.resolve_num_sharding()checks the requested count againstget_n_layers(legacyllm_wrappers.pyL1329). The strategy resolves it once per component and uses the same value for the specs and the passes. Above the layer count, it clamps with a WARNING. Legacy instead disables the split but still requests multi-contexts; here specs and passes cannot disagree.model_layout.py, not inpass_policy.py:ModelLayout(decoder/encoder layer prefixes, prefix offsets frommeta, extra head dim, gemma4 stack-trace and donor-K/V tagging rules) and a registry (get_model_layout,register_model_layout).pass_policy.pyhas no model-name conditionals. A new model, including HF module paths (model.layers), registers a layout only when it differs from the default.build_component_pass_config()logs one INFO line per component with the model name, the shard count and the resolvedModelLayout, so users can see which layout patterns the run applies.TagQuantIOdtypes come from the quantization recipe viaGraphBundle.quant_io_dtypes. Decoders carry{kv_type, io_type}; encoders carry{io_type}(uint16, as in Qualcomm AI Engine Direct - Tag multimodal encoder quantized I/O as uint16 #22924).Nonemeans quantization was skipped, soTagQuantIOstays inactive.uses_decoder_boundary). Compilation raises if the decode graph is absent.Component fan-out (
executorch_compilation_strategy.py)invoketakesgraphswhen quantization produced them. When quantization was skipped andmodelis keyed by component, it builds unquantized bundles (unquantized_bundles.py): drops the calibration graphs and flattens the decoder inputs. Both cases then use_compile_components._compile_single_graphremains for a true one-graph model. Result: LLM and MLLM on GPU / FP16 get one.pteper component, as in legacy.ExecutorchBackendConfig, callscompile_graph_grouponce, and merges the artifact paths. It rejects an unknown artifact key, an empty component, and a component that mixes quantized and unquantized graphs.Shared graph helpers (new, used by quantization and compilation)
graph_roles.py:GraphRole,GRAPH_ROLES,get_graph_role(),is_deployed(). Moved from A1's quantization strategy.export_inputs.py:to_export_inputs(), flattening the nested decoder inputs into the export signature. Moved from A1's quantization strategy.Quantization strategy changes (
executorch_quantization_strategy.py, from A1)GraphBundle.inputsnow holds the flattened export signature the graph was exported with, not the nested decoder inputs. Lowering re-exports withbundle.inputs.quantize_options["skip_quantize"]leaves encoders (components whose graphs are allSHARED) unquantized. A skipped encoder is exported but not prepared, still runs in float during MLLM calibration, and is bundled withquantized=False. Skipping the decoder this way raises; the decoder is skipped by skipping the stage (use_fp16).Config surface
GraphBundle.quantized: bool = True. An unquantized bundle cannot carryquant_io_dtypes.GraphBundle.executorch_configand theexecutorch_backend_configextra option (both from B1).build_executorch_config()replaces them.CompilerAdapter.compile_model/compile_graph_group: new requiredexecutorch_configparameter.CompilationInputConfig.graphs: Optional[Dict[str, Dict[str, GraphBundle]]]: artifact key, then graph name.CompilationInputConfig.num_sharding: Union[int, Dict[str, int]] = 1: per component. The orchestrator forwardsModelPreparationOutputConfig.num_shardings.CompilationInputConfig.meta: used to build unquantized bundles when quantization is skipped. Forwarded by the orchestrator.compile_spec_builder.py: docstring changes only (shared_buffermust followenable_x86_64, not the runner flagControlArgs.shared_buffer).Deliberately deferred
artifact_key == ARTIFACT_AUDIO_ENCODERrather than on the encoder config class as legacy does. Correct while GraniteSpeech is the only audio encoder.skip_quantizefor multi-image input (legacy: skip encoders whenlen(image_path) > 1). The CLI has noimage_pathargument yet; this lands in PR-A2.Unit tests (9 new files, 5 updated)
test_pass_policy.py,test_component_spec_policy.py,test_model_layout.py,test_compile_graph_group.py,test_llm_compiler_adapter.py,test_compilation_strategy_fan_out.py,test_unquantized_bundles.py,test_graph_roles.py,test_export_inputs.py.test_compilation_input_config.py,test_default_compiler_adapter.py(requiredexecutorch_config),test_executorch_compilation_strategy.py,test_graph_bundle.py,test_executorch_quantization_strategy.py(bundle inputs are the export signature; encoder skip).PR Review Checklist
examples/files are touched. Changes to merged Phase 2 code:executorch_configargument, and the B1executorch_configoverrides are removed.Related PRs
Phase 2 flow.
PR-A1andPR-B1are merged.PR-B2depends on both.PR-A2depends onPR-B2, and closes Phase 2 by wiring the runner and adding the parity test that gates the Phase-3 deletion of the legacy flow.Phase 1 (merged):
Phase 2:
skip_quantizeCLI wiring and thellama_stories_260kparity test; depends on B2 - pendingRelated upstream: #22924 (encoder quantized IO tagged as uint16) - merged.
Test plan
Run all
genai_pipelinetests:Result:
Run all tests with coverage:
Result for the files this PR adds or changes:
The remaining uncovered lines are mostly defensive paths: unrecognized artifact keys, missing graph roles, missing
get_use_kv_cache, and the GraniteSpeech attention-distance rule (see Deliberately deferred). The package-wide total (67%) includes A1 modules outside this PR. Coverage will be increased to sufficient levels (>=95%) by the end of phase 2.Confirm the legacy flow is unaffected:
Result: