Skip to content

Commit a6ec762

Browse files
committed
Cache post-lowering ATen graphs to skip decomp on warm compiles.
Serialize the live FX node list and state_dict with torch.save so a hit can skip decomposition and post_lowering without retracing generated Python.
1 parent 579e79f commit a6ec762

5 files changed

Lines changed: 856 additions & 19 deletions

File tree

py/torch_tensorrt/dynamo/_compiler.py

Lines changed: 67 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@
2626
)
2727
from torch_tensorrt.dynamo._engine_cache import BaseEngineCache, DiskEngineCache
2828
from torch_tensorrt.dynamo._exporter import replace_execute_engine_no_op_node
29+
from torch_tensorrt.dynamo._lowering_cache import (
30+
DiskLoweringCache,
31+
LoweringCacheEntry,
32+
repropagate_graph_metadata,
33+
)
2934
from torch_tensorrt.dynamo.conversion import (
3035
CompilationSettings,
3136
UnsupportedOperatorException,
@@ -456,6 +461,9 @@ def compile(
456461
engine_cache_dir: str = _defaults.ENGINE_CACHE_DIR,
457462
engine_cache_size: int = _defaults.ENGINE_CACHE_SIZE,
458463
custom_engine_cache: Optional[BaseEngineCache] = _defaults.CUSTOM_ENGINE_CACHE,
464+
cache_lowered_graphs: bool = _defaults.CACHE_LOWERED_GRAPHS,
465+
reuse_cached_lowered_graphs: bool = _defaults.REUSE_CACHED_LOWERED_GRAPHS,
466+
lowering_cache_dir: str = _defaults.LOWERING_CACHE_DIR,
459467
use_fp32_acc: bool = _defaults.USE_FP32_ACC,
460468
refit_identical_engine_weights: bool = _defaults.REFIT_IDENTICAL_ENGINE_WEIGHTS,
461469
strip_engine_weights: bool = _defaults.STRIP_ENGINE_WEIGHTS,
@@ -550,6 +558,9 @@ def compile(
550558
engine_cache_dir (str): Directory to store the cached TRT engines
551559
engine_cache_size (int): Maximum hard-disk space (bytes) to use for the engine cache, default is 1GB. If the cache exceeds this size, the oldest engines will be removed by default
552560
custom_engine_cache (Optional[BaseEngineCache]): Engine cache instance to use for saving and loading engines. Users can provide their own engine cache by inheriting from BaseEngineCache. If used, engine_cache_dir and engine_cache_size will be ignored.
561+
cache_lowered_graphs (bool): Whether to save lowered and partitioned graphs for warm compilation
562+
reuse_cached_lowered_graphs (bool): Whether to reuse lowered and partitioned graphs before decomposition
563+
lowering_cache_dir (str): Directory used by the lowered graph cache
553564
use_fp32_acc (bool): Enable FP32 accumulation for FP16 matmul layers while retaining FP16
554565
inputs and outputs. When combined with ``decompose_attention=True``, the complete
555566
decomposed FP16 scaled dot product attention calculation runs in FP32 and only its
@@ -744,6 +755,8 @@ def compile(
744755
"lazy_engine_init": lazy_engine_init,
745756
"cache_built_engines": cache_built_engines,
746757
"reuse_cached_engines": reuse_cached_engines,
758+
"cache_lowered_graphs": cache_lowered_graphs,
759+
"reuse_cached_lowered_graphs": reuse_cached_lowered_graphs,
747760
"use_fp32_acc": use_fp32_acc,
748761
"refit_identical_engine_weights": refit_identical_engine_weights,
749762
"strip_engine_weights": strip_engine_weights,
@@ -773,25 +786,51 @@ def compile(
773786

774787
logger.info("Compilation Settings: %s\n", settings)
775788
exported_program = pre_export_lowering(exported_program, settings)
776-
exported_program = exported_program.run_decompositions(
777-
get_decompositions(
778-
enable_experimental_decompositions,
779-
decompose_attention,
780-
use_distributed_mode_trace,
781-
use_fp32_acc=use_fp32_acc,
789+
lowering_cache = None
790+
lowering_cache_key = None
791+
lowering_cache_entry = None
792+
if cache_lowered_graphs or reuse_cached_lowered_graphs:
793+
if not DiskLoweringCache.can_cache(settings):
794+
logger.warning(
795+
"Lowering cache bypassed: the initial implementation requires "
796+
"require_full_compilation=True, use_fast_partitioner=True, dryrun=False, "
797+
"enable_autocast=False, and use_distributed_mode_trace=False"
798+
)
799+
else:
800+
lowering_cache = DiskLoweringCache(lowering_cache_dir)
801+
lowering_cache_key = lowering_cache.get_hash(
802+
exported_program, trt_arg_inputs, trt_kwarg_inputs, settings
803+
)
804+
if reuse_cached_lowered_graphs:
805+
lowering_cache_entry = lowering_cache.load(lowering_cache_key)
806+
807+
if lowering_cache_entry is not None:
808+
gm = lowering_cache_entry.lowered_module
809+
lifted_buffers = list(lowering_cache_entry.lifted_buffers)
810+
gm = repropagate_graph_metadata(
811+
gm, trt_arg_inputs, trt_kwarg_inputs, settings.device
812+
)
813+
logger.info(
814+
"Reusing cached lowered graph; skipping decomposition and post-lowering"
815+
)
816+
else:
817+
exported_program = exported_program.run_decompositions(
818+
get_decompositions(
819+
enable_experimental_decompositions,
820+
decompose_attention,
821+
use_distributed_mode_trace,
822+
use_fp32_acc=use_fp32_acc,
823+
)
782824
)
783-
)
784825

785-
gm = exported_program.module()
786-
# Move the weights in the state_dict to CPU
787-
logger.debug("Input graph: " + str(gm.graph))
826+
gm = exported_program.module()
827+
logger.debug("Input graph: " + str(gm.graph))
828+
829+
# Lift mutated buffers from get_attr to placeholders BEFORE post_lowering's
830+
# constant_fold runs, so the engine sees them as input bindings.
831+
gm, lifted_buffers = lift_mutated_buffers(gm)
832+
gm = post_lowering(gm, settings)
788833

789-
# Lift mutated buffers from get_attr to placeholders BEFORE post_lowering's
790-
# constant_fold runs, so the engine sees them as input bindings (a
791-
# prerequisite for IKVCacheUpdateLayer / aliased I/O to fire on a
792-
# module-held cache). Returns a fresh GraphModule whose forward signature
793-
# reflects the new placeholders.
794-
gm, lifted_buffers = lift_mutated_buffers(gm)
795834
if lifted_buffers:
796835
# Append each lifted buffer as an engine input AFTER the user inputs.
797836
# Buffer tensors live on the gm's state; prepare an Input spec for
@@ -805,9 +844,17 @@ def compile(
805844
[b for _, b, _ in lifted_buffers],
806845
)
807846

808-
# Apply lowering on the graph module. Note: constant_fold runs inside post_lowering and requires
809-
# module parameters to still be on GPU, so we must not deallocate before this call.
810-
gm = post_lowering(gm, settings)
847+
if (
848+
lowering_cache_entry is None
849+
and cache_lowered_graphs
850+
and lowering_cache is not None
851+
and lowering_cache_key is not None
852+
):
853+
gm = lowering_cache.save(
854+
lowering_cache_key,
855+
LoweringCacheEntry(gm, tuple(lifted_buffers)),
856+
)
857+
811858
logger.debug(f"CPU memory usage after post_lowering: {get_cpu_memory_usage()} MB")
812859
logger.debug("Lowered Input graph: " + str(gm.graph))
813860

@@ -824,6 +871,7 @@ def compile(
824871
logger.warning(
825872
"Remaining GPU memory may not be enough to compile the TensorRT engine for this model resulting in an OOM error, Consider setting offload_module_to_cpu=True"
826873
)
874+
827875
trt_gm = compile_module(
828876
gm,
829877
trt_arg_inputs,

py/torch_tensorrt/dynamo/_defaults.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@
4444
ENGINE_CACHE_DIR = os.path.join(tempfile.gettempdir(), "torch_tensorrt_engine_cache")
4545
ENGINE_CACHE_SIZE = 5368709120 # 5GB
4646
CUSTOM_ENGINE_CACHE = None
47+
CACHE_LOWERED_GRAPHS = False
48+
REUSE_CACHED_LOWERED_GRAPHS = False
49+
LOWERING_CACHE_DIR = os.path.join(ENGINE_CACHE_DIR, "lowered_graphs")
4750
USE_FP32_ACC = False
4851
REFIT_IDENTICAL_ENGINE_WEIGHTS = False
4952
STRIP_ENGINE_WEIGHTS = False

0 commit comments

Comments
 (0)