@@ -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