Skip to content

Commit d9efbff

Browse files
authored
feat: add clean cache option to forecaster (#358)
2 parents f302b8b + 6100367 commit d9efbff

2 files changed

Lines changed: 38 additions & 0 deletions

File tree

tests/test_forecaster.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,3 +173,20 @@ def test_mixed_models_unique_aliases():
173173
# This should not raise an error
174174
forecaster = TimeCopilotForecaster(models=[model1, model2, model3])
175175
assert len(forecaster.models) == 3
176+
177+
178+
def test_clean_cache_runs_after_each_model(monkeypatch, models):
179+
calls = []
180+
181+
monkeypatch.setattr(
182+
TimeCopilotForecaster,
183+
"_clean_model_cache",
184+
staticmethod(lambda: calls.append("cleaned")),
185+
)
186+
187+
df = generate_series(n_series=1, freq="D", min_length=10)
188+
forecaster = TimeCopilotForecaster(models=models, clean_cache=True)
189+
190+
forecaster.forecast(df=df, h=2, freq="D")
191+
192+
assert calls == ["cleaned"] * len(models)

timecopilot/forecaster.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ def __init__(
4747
self,
4848
models: list[Forecaster],
4949
fallback_model: Forecaster | None = None,
50+
clean_cache: bool = False,
5051
):
5152
"""
5253
Initialize the TimeCopilotForecaster with a list of models.
@@ -59,13 +60,17 @@ def __init__(
5960
compatible signatures.
6061
fallback_model (Forecaster, optional):
6162
Model to use as a fallback when a model fails.
63+
clean_cache (bool):
64+
If True, run Python garbage collection and clear the CUDA cache
65+
after each model call. Useful for memory-heavy foundation models.
6266
6367
Raises:
6468
ValueError: If duplicate model aliases are found in the models list.
6569
"""
6670
self._validate_unique_aliases(models)
6771
self.models = models
6872
self.fallback_model = fallback_model
73+
self.clean_cache = clean_cache
6974

7075
def _validate_unique_aliases(self, models: list[Forecaster]) -> None:
7176
"""
@@ -88,6 +93,20 @@ def _validate_unique_aliases(self, models: list[Forecaster]) -> None:
8893
f"same class."
8994
)
9095

96+
@staticmethod
97+
def _clean_model_cache() -> None:
98+
"""Release temporary Python and CUDA memory between model calls."""
99+
import gc
100+
101+
gc.collect()
102+
try:
103+
import torch
104+
105+
if torch.cuda.is_available():
106+
torch.cuda.empty_cache()
107+
except ImportError:
108+
pass
109+
91110
@staticmethod
92111
def _is_distributed_df(df: AnyDataFrame) -> bool:
93112
"""
@@ -155,6 +174,8 @@ def _call_models(
155174
# (the initial model)
156175
res_df_model = res_df_model.drop(columns=["y"])
157176
res_df = res_df.merge(res_df_model, on=merge_on, how="left")
177+
if self.clean_cache:
178+
self._clean_model_cache()
158179
return res_df
159180

160181
def _forecast_pandas(

0 commit comments

Comments
 (0)