Skip to content

Commit ea5e700

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 b287f3a commit ea5e700

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,
@@ -455,6 +460,9 @@ def compile(
455460
engine_cache_dir: str = _defaults.ENGINE_CACHE_DIR,
456461
engine_cache_size: int = _defaults.ENGINE_CACHE_SIZE,
457462
custom_engine_cache: Optional[BaseEngineCache] = _defaults.CUSTOM_ENGINE_CACHE,
463+
cache_lowered_graphs: bool = _defaults.CACHE_LOWERED_GRAPHS,
464+
reuse_cached_lowered_graphs: bool = _defaults.REUSE_CACHED_LOWERED_GRAPHS,
465+
lowering_cache_dir: str = _defaults.LOWERING_CACHE_DIR,
458466
use_fp32_acc: bool = _defaults.USE_FP32_ACC,
459467
refit_identical_engine_weights: bool = _defaults.REFIT_IDENTICAL_ENGINE_WEIGHTS,
460468
strip_engine_weights: bool = _defaults.STRIP_ENGINE_WEIGHTS,
@@ -549,6 +557,9 @@ def compile(
549557
engine_cache_dir (str): Directory to store the cached TRT engines
550558
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
551559
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.
560+
cache_lowered_graphs (bool): Whether to save lowered and partitioned graphs for warm compilation
561+
reuse_cached_lowered_graphs (bool): Whether to reuse lowered and partitioned graphs before decomposition
562+
lowering_cache_dir (str): Directory used by the lowered graph cache
552563
use_fp32_acc (bool): Enable FP32 accumulation for FP16 matmul layers while retaining FP16
553564
inputs and outputs. When combined with ``decompose_attention=True``, the complete
554565
decomposed FP16 scaled dot product attention calculation runs in FP32 and only its
@@ -743,6 +754,8 @@ def compile(
743754
"lazy_engine_init": lazy_engine_init,
744755
"cache_built_engines": cache_built_engines,
745756
"reuse_cached_engines": reuse_cached_engines,
757+
"cache_lowered_graphs": cache_lowered_graphs,
758+
"reuse_cached_lowered_graphs": reuse_cached_lowered_graphs,
746759
"use_fp32_acc": use_fp32_acc,
747760
"refit_identical_engine_weights": refit_identical_engine_weights,
748761
"strip_engine_weights": strip_engine_weights,
@@ -772,25 +785,51 @@ def compile(
772785

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

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

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

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

@@ -823,6 +870,7 @@ def compile(
823870
logger.warning(
824871
"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"
825872
)
873+
826874
trt_gm = compile_module(
827875
gm,
828876
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)