Skip to content

Commit 6fae18b

Browse files
committed
add early stop for libtuner
1 parent aac5805 commit 6fae18b

2 files changed

Lines changed: 49 additions & 5 deletions

File tree

src/flag_gems/ops/mm.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
@libtuner(
1717
configs=runtime.get_tuned_config("mm"),
1818
key=["M", "N", "K"],
19-
strategy=["align32", None, None],
19+
# strategy=["align32", None, None],
2020
)
2121
@triton.heuristics(runtime.get_heuristic_config("mm"))
2222
@triton.jit

src/flag_gems/utils/libentry.py

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,8 @@ def __init__(
167167
do_bench=None,
168168
strategy=None,
169169
share=None,
170+
early_stop_threshold=0.03,
171+
early_stop_min_configs=5,
170172
):
171173
if major_version == 2:
172174
super().__init__(
@@ -209,6 +211,33 @@ def __init__(
209211
while not inspect.isfunction(self.base_fn):
210212
self.base_fn = self.base_fn.fn
211213
self.kernel_hash = get_kernel_hash(self.base_fn)
214+
self.early_stop_threshold = early_stop_threshold
215+
self.early_stop_min_configs = early_stop_min_configs
216+
217+
def _should_early_stop(self, timings, configs_tested):
218+
if configs_tested < self.early_stop_min_configs:
219+
return False
220+
221+
if len(timings) < 2:
222+
return False
223+
224+
def extract_timing(timing_value):
225+
if isinstance(timing_value, (list, tuple)):
226+
return float(timing_value[0])
227+
return float(timing_value)
228+
229+
timing_values = [extract_timing(t) for t in timings.values()]
230+
best_time = min(timing_values)
231+
sorted_times = sorted(timing_values)
232+
233+
if configs_tested >= self.early_stop_min_configs:
234+
if len(sorted_times) >= 2:
235+
second_best = sorted_times[1]
236+
improvement_ratio = (second_best - best_time) / best_time
237+
if improvement_ratio < self.early_stop_threshold:
238+
return True
239+
240+
return False
212241

213242
def get_key(self, args):
214243
if self.strategy is None:
@@ -241,10 +270,21 @@ def run(self, *args, **kwargs):
241270
used_cached_result = False
242271
pruned_configs = self.prune_configs(kwargs)
243272
bench_start = time.time()
244-
timings = {
245-
config: self._bench(*args, config=config, **kwargs)
246-
for config in pruned_configs
247-
}
273+
timings = {}
274+
configs_tested = 0
275+
for config in pruned_configs:
276+
timing = self._bench(*args, config=config, **kwargs)
277+
timings[config] = timing
278+
configs_tested += 1
279+
280+
if self._should_early_stop(timings, configs_tested):
281+
if os.getenv("TRITON_PRINT_AUTOTUNING", None) == "1":
282+
print(
283+
f"Early stopping after {configs_tested}/{len(pruned_configs)} "
284+
f"configs for {self.__name__}"
285+
)
286+
break
287+
248288
bench_end = time.time()
249289
self.bench_time = bench_end - bench_start
250290
self.cache[key] = builtins.min(timings, key=timings.get)
@@ -290,6 +330,8 @@ def libtuner(
290330
do_bench=None,
291331
strategy=None,
292332
share=None,
333+
early_stop_threshold=0.05,
334+
early_stop_min_configs=3,
293335
):
294336
"""
295337
Decorator for triton library autotuner.
@@ -312,6 +354,8 @@ def decorator(fn):
312354
do_bench=do_bench,
313355
strategy=strategy,
314356
share=share,
357+
early_stop_threshold=early_stop_threshold,
358+
early_stop_min_configs=early_stop_min_configs,
315359
)
316360

317361
return decorator

0 commit comments

Comments
 (0)