Skip to content

Commit 5231e91

Browse files
vbaddiquic-rishinr
andauthored
feat: Named graph specializations in specializations.json (Prefill/Decode/Vision/Encoder/Embedding) (quic#904)
## Summary The backend compiler team requested a new specializations.json format where each entry carries a meaningful graph name (e.g. "Prefill", "Decode") ## Changes - **`QEfficient/utils/_utils.py`** — new `_infer_specialization_name()` and `to_named_specializations()` helpers - **`QEfficient/base/modeling_qeff.py`** — `_compile()` uses new format - **`QEfficient/compile/qnn_compiler.py`** — QNN path uses new format - **`QEfficient/compile/compile_helper.py`** — legacy `create_and_dump_specializations()` uses new format ## Name inference rules | Keys present | Assigned name | |---|---| | `vision_size` / `img_size` / `grid_*`, no `seq_len` | `Vision` | | `encoder_ctx_len`, no `seq_len` | `Encoder` | | `sequence_length`, no `seq_len` | `Embedding` | | `seq_len != 1` | `Prefill` | | `seq_len == 1` | `Decode` | | anything else | `Graph_N` | ## Testing 21-unit tests added to `tests/unit_test/models/test_model_quickcheck.py` covering causal LM, continuous batching, VLM vision/language, Whisper, encoder/decoder, text embedding, and end-to-end JSON roundtrip. cc: @anujgupt-github @quic-rishinr --------- Signed-off-by: vbaddi <vbaddi@qti.qualcomm.com> Signed-off-by: Rishin Raj <rishinr@qti.qualcomm.com> Co-authored-by: Rishin Raj <rishinr@qti.qualcomm.com>
1 parent c0a405f commit 5231e91

8 files changed

Lines changed: 536 additions & 29 deletions

File tree

QEfficient/base/modeling_qeff.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
hash_dict_params,
4242
load_json,
4343
require_value,
44+
to_named_specializations,
4445
)
4546
from QEfficient.utils.export_utils import export_wrapper
4647

@@ -471,6 +472,7 @@ def _compile(
471472
enable_chunking: Optional[bool] = False,
472473
retain_full_kv: Optional[bool] = None,
473474
qaic_config: Optional[dict] = None,
475+
specialization_module_name: Optional[str] = None,
474476
**compiler_options,
475477
) -> str:
476478
"""
@@ -609,7 +611,7 @@ def _compile(
609611
if specializations is not None:
610612
specializations_json = compile_dir / "specializations.json"
611613
specializations_data = {
612-
"specializations": [{k: str(v) for k, v in spec.items()} for spec in specializations]
614+
"specializations": to_named_specializations(specializations, module_name=specialization_module_name)
613615
}
614616
create_json(str(specializations_json), specializations_data)
615617
command.append(f"-network-specialization-config={specializations_json}")

QEfficient/compile/compile_helper.py

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,35 +14,34 @@
1414

1515
from QEfficient.compile.qnn_compiler import compile as qnn_compile
1616
from QEfficient.utils import constants
17-
from QEfficient.utils._utils import load_json, load_yaml
17+
from QEfficient.utils._utils import load_json, load_yaml, to_named_specializations
1818
from QEfficient.utils.logging_utils import logger
1919

2020

2121
def create_and_dump_specializations(
2222
batch_size: int, prompt_len: int, ctx_len: int, path: str, full_batch_size: Optional[int] = None
2323
):
24-
# Create specialization file.
25-
specializations = {
26-
"specializations": [
27-
{
28-
"batch_size": str(batch_size),
29-
"seq_len": str(prompt_len),
30-
"ctx_len": str(ctx_len),
31-
},
32-
{"batch_size": str(batch_size), "seq_len": "1", "ctx_len": str(ctx_len)},
33-
]
34-
}
35-
# If continuous batching is enabled by proving full_batch_size we need to add FBS to the specialization file and update the batch size of decoder part to FBS
24+
# Build the base specialization entries first, then convert to named format.
25+
base_specializations = [
26+
{
27+
"batch_size": str(batch_size),
28+
"seq_len": str(prompt_len),
29+
"ctx_len": str(ctx_len),
30+
},
31+
{"batch_size": str(batch_size), "seq_len": "1", "ctx_len": str(ctx_len)},
32+
]
33+
# If continuous batching is enabled by providing full_batch_size we need to add FBS to the specialization file and update the batch size of decoder part to FBS
3634
if full_batch_size is not None:
37-
specializations["specializations"][0]["full_batch_size"] = str(full_batch_size)
38-
specializations["specializations"][1]["full_batch_size"] = str(full_batch_size)
39-
specializations["specializations"][1]["batch_size"] = str(full_batch_size)
35+
base_specializations[0]["full_batch_size"] = str(full_batch_size)
36+
base_specializations[1]["full_batch_size"] = str(full_batch_size)
37+
base_specializations[1]["batch_size"] = str(full_batch_size)
4038

41-
# To handle repetative input in specializations when prompt_len is 1
39+
# To handle repetitive input in specializations when prompt_len is 1
4240
if prompt_len == 1 and full_batch_size is None:
43-
specializations["specializations"].pop()
41+
base_specializations.pop()
4442

4543
# Dump
44+
specializations = {"specializations": to_named_specializations(base_specializations)}
4645
with open(path, "w") as file:
4746
json.dump(specializations, file, indent=4)
4847

QEfficient/compile/qnn_compiler.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import shutil
1212
from typing import Dict, List, Optional
1313

14-
from QEfficient.utils._utils import create_json, execute_command, load_json
14+
from QEfficient.utils._utils import create_json, execute_command, load_json, to_named_specializations
1515
from QEfficient.utils.constants import QnnConstants
1616
from QEfficient.utils.generate_qnn_network_specialization_config import (
1717
generate_data_format_config,
@@ -423,7 +423,7 @@ def compile(
423423
specializations_json = qpc_base_path / "specializations.json"
424424
with open(specializations_json, "w") as fp:
425425
json.dump(
426-
{"specializations": [{k: str(v) for k, v in spec.items()} for spec in specializations]},
426+
{"specializations": to_named_specializations(specializations)},
427427
fp,
428428
indent=4,
429429
)

QEfficient/diffusers/pipelines/pipeline_utils.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,12 @@ def _prepare_and_compile(module_name: str, module_obj: Any) -> None:
173173
else:
174174
specializations = [specializations]
175175

176+
# Tag each spec with the module name so _compile knows the graph name.
177+
specializations = [
178+
{**s, "_graph_name": f"{module_name}_model_type_{s['model_type']}" if "model_type" in s else module_name}
179+
for s in specializations
180+
]
181+
176182
if module_obj.qpc_path is None:
177183
# Compile with prepared specializations
178184
module_obj.compile(specializations=specializations, **compile_kwargs)
@@ -226,6 +232,12 @@ def compile_modules_sequential(
226232
else:
227233
specializations = [specializations]
228234

235+
# Tag each spec with the module name so _compile knows the graph name.
236+
specializations = [
237+
{**s, "_graph_name": f"{module_name}_model_type_{s['model_type']}" if "model_type" in s else module_name}
238+
for s in specializations
239+
]
240+
229241
if module_obj.qpc_path is None:
230242
# Compile with prepared specializations
231243
module_obj.compile(specializations=specializations, **compile_kwargs)

QEfficient/generation/text_generation_inference.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -188,9 +188,12 @@ def get_compilation_dims(qpc_path: str) -> Tuple[int, int, Optional[int]]:
188188
else:
189189
raise FileNotFoundError(f"expected specializations.json file at path, {qpc_base_path}")
190190

191-
compilation_batch_size = int(data["specializations"][0]["batch_size"])
192-
compilation_ctx_len = int(data["specializations"][0]["ctx_len"])
193-
if compilation_fbs := data["specializations"][0].get("full_batch_size", None):
191+
# Support both the legacy flat format and the new {name, symbols} format.
192+
first = data["specializations"][0]
193+
spec = first.get("symbols", first)
194+
compilation_batch_size = int(spec["batch_size"])
195+
compilation_ctx_len = int(spec["ctx_len"])
196+
if compilation_fbs := spec.get("full_batch_size", None):
194197
compilation_fbs = int(compilation_fbs)
195198
return compilation_batch_size, compilation_ctx_len, compilation_fbs
196199

QEfficient/transformers/models/modeling_auto.py

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -449,8 +449,14 @@ def compile(
449449
if isinstance(seq_len, list) and len(seq_len) >= 15:
450450
warnings.warn("Recommended: `seq_len` should contain fewer than 15 items.")
451451

452+
_seq_lens = seq_len if isinstance(seq_len, list) else [seq_len]
452453
specializations = [
453-
{"batch_size": batch_size, "seq_len": sl} for sl in (seq_len if isinstance(seq_len, list) else [seq_len])
454+
{
455+
"_graph_name": "Embedding" if len(_seq_lens) == 1 else f"Embedding_{i}",
456+
"batch_size": batch_size,
457+
"seq_len": sl,
458+
}
459+
for i, sl in enumerate(_seq_lens)
454460
]
455461

456462
target_dtype = getattr(self.model.config, "torch_dtype", torch.float32)
@@ -794,8 +800,14 @@ def compile(
794800
if isinstance(seq_len, list) and len(seq_len) >= 15:
795801
warnings.warn("Recommended: `seq_len` should contain fewer than 15 items.")
796802

803+
_seq_lens = seq_len if isinstance(seq_len, list) else [seq_len]
797804
specializations = [
798-
{"batch_size": batch_size, "seq_len": sl} for sl in (seq_len if isinstance(seq_len, list) else [seq_len])
805+
{
806+
"_graph_name": "SeqClassification" if len(_seq_lens) == 1 else f"SeqClassification_{i}",
807+
"batch_size": batch_size,
808+
"seq_len": sl,
809+
}
810+
for i, sl in enumerate(_seq_lens)
799811
]
800812
target_dtype = getattr(self.model.config, "torch_dtype", torch.float32)
801813
return self._compile(
@@ -1582,6 +1594,7 @@ def compile(
15821594
compile_dir=compile_dir,
15831595
compile_only=True,
15841596
specializations=specializations["vision"],
1597+
specialization_module_name="Vision",
15851598
convert_to_fp16=(CUSTOM_IO_DTYPE_MAP[target_dtype] == "float16"),
15861599
mxfp6_matmul=constants.VISION_MXFP6_MATMUL,
15871600
mdp_ts_num_devices=num_devices,
@@ -3256,7 +3269,9 @@ def build_prefill_specialization(
32563269
# TODO: remove this; not required
32573270
if full_batch_size:
32583271
spec["full_batch_exec_size"] = exec_batch_size
3259-
return {k: v for k, v in spec.items() if v is not None}
3272+
result = {k: v for k, v in spec.items() if v is not None}
3273+
result["_graph_name"] = "Prefill"
3274+
return result
32603275

32613276
def build_decode_specialization(
32623277
self,
@@ -3314,7 +3329,9 @@ def build_decode_specialization(
33143329
spec["full_batch_size"] = kv_cache_batch_size
33153330
else:
33163331
spec["batch_size"] = kv_cache_batch_size
3317-
return {k: v for k, v in spec.items() if v is not None}
3332+
result = {k: v for k, v in spec.items() if v is not None}
3333+
result["_graph_name"] = "Decode"
3334+
return result
33183335

33193336
def compile(
33203337
self,
@@ -4235,8 +4252,10 @@ def compile(
42354252
:str: Path of the compiled ``qpc`` package.
42364253
"""
42374254

4255+
_seq_lens = seq_len if isinstance(seq_len, list) else [seq_len]
42384256
specializations = [
4239-
{"batch_size": batch_size, "seq_len": sl} for sl in (seq_len if isinstance(seq_len, list) else [seq_len])
4257+
{"_graph_name": "CTC" if len(_seq_lens) == 1 else f"CTC_{i}", "batch_size": batch_size, "seq_len": sl}
4258+
for i, sl in enumerate(_seq_lens)
42404259
]
42414260

42424261
target_dtype = getattr(self.model.config, "torch_dtype", torch.float32)

QEfficient/transformers/models/whisper/modeling_whisper.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -835,6 +835,7 @@ def get_specializations(self, batch_size: int, encoder_ctx_len, ctx_len, **compi
835835
feature_len = encoder_ctx_len * 2
836836

837837
encoder_specializations = {
838+
"_graph_name": "Encoder",
838839
"batch_size": batch_size,
839840
"seq_len": 1,
840841
"encoder_ctx_len": encoder_ctx_len,
@@ -843,6 +844,7 @@ def get_specializations(self, batch_size: int, encoder_ctx_len, ctx_len, **compi
843844
}
844845

845846
decoder_specializations = {
847+
"_graph_name": "Decode",
846848
"batch_size": batch_size,
847849
"seq_len": 1,
848850
"encoder_ctx_len": encoder_ctx_len,

0 commit comments

Comments
 (0)