4545# One worker per GPU, kept alive while idle so we can read its measured perf.
4646ENDPOINT_CONFIG = {"cold_workers" : 1 , "max_workers" : 1 , "min_load" : 1.0 }
4747
48- # Timing knobs, in the order they fire during a run.
49- RENTAL_TIMEOUT = 120 # give up if the autoscaler hasn't rented a worker by now
50- DEFAULT_BENCHMARK_TIMEOUT = 30 * 60 # default --timeout: max wait for a measured result
51- WORKER_POLL_INTERVAL = 10.0 # base seconds between worker-status polls (jitter added at call site)
48+ RENTAL_TIMEOUT = 120
49+ DEFAULT_BENCHMARK_TIMEOUT = 30 * 60
50+ WORKER_POLL_INTERVAL = 10.0
5251PARALLEL_SUBMIT_DELAY = 4.0 # spacing between parallel endpoint submits
5352MAX_ABANDONS = 3 # bail after this many workers vanish without one reaching idle
5453
55- # Worker states the autoscaler does NOT recover from; a run fails if every worker
56- # sits in one for >TERMINAL_GRACE_SECONDS without any reaching idle. `error` is
57- # excluded since the autoscaler retries it (error -> rebooting -> model_loading).
58- TERMINAL_STATES = {"stopped" , "destroying" , "unavail" }
54+ # states in which the autoscaler shouldnt retry the worker
55+ TERMINAL_STATES = {"stopped" , "destroying" , "unavail" }
5956TERMINAL_GRACE_SECONDS = 30
60-
61- # Per-spec statuses that freeze the live-table elapsed timer (work is over).
6257TERMINAL_STATUSES = {"done" , "skipped" , "timeout" , "failed" , "no_worker" }
6358
6459
@@ -69,20 +64,17 @@ def format_time_elapsed(seconds):
6964
7065def parse_gpu_spec (token , default_num_gpus ):
7166 """Parse a GPU token into (canonical_gpu_name, num_gpus).
72-
73- "2x RTX_4090" -> ("RTX 4090", 2). Accepts rtx_4090 / RTX 4090 / Rtx_4090 and
74- canonicalizes to the marketplace's exact name. An inline Nx prefix wins over
75- --num_gpus; with neither, falls back to default_num_gpus.
67+ Example: 2x RTX_4090" -> ("RTX 4090", 2).
7668 """
7769
78- # so users can input "rtx_4090" or "RTX 4090" or "Rtx_4090" and all resolves to "RTX 4090"
70+ # so "rtx_4090", "RTX 4090", "Rtx_4090" all map to "RTX 4090"
7971 canonical = {
8072 v .lower (): v for k , v in vars (_query_consts ).items ()
8173 if isinstance (v , str ) and not k .startswith ("_" )
8274 }
8375
8476 token = token .strip ()
85- # converts "2x RTX_4090" to ("RTX 4090", 2)
77+ # string to tuple. "2x RTX_4090" to ("RTX 4090", 2)
8678 m = re .match (r"^(\d+)[\s_]*x[\s_]*(.+)$" , token , re .IGNORECASE )
8779 if m :
8880 raw_name , n = m .group (2 ).strip ().replace ("_" , " " ), int (m .group (1 ))
@@ -596,11 +588,11 @@ def benchmark_gpu(vast, *, gpu_name, num_gpus, timeout,
596588
597589@parser .command (
598590 argument ("--template_hash" , type = str , default = None ,
599- help = "(required, one of --template_hash or --template_id) template hash to benchmark " ),
591+ help = "the template to benchmark; provide either --template_hash or --template_id" ),
600592 argument ("--template_id" , type = int , default = None ,
601- help = "(required, one of --template_hash or --template_id) template id " ),
593+ help = "the template to benchmark; provide either --template_hash or --template_id" ),
602594 argument ("--gpus" , type = str ,
603- help = "comma-separated GPU names (e.g. RTX_4090,RTX_3090); optional Nx prefix takes precedence over --num_gpus ( e.g. \" 2x RTX_4090\" )" ),
595+ help = "comma-separated GPU names to benchmark (e.g. RTX_4090,RTX_3090). Prefix a name with a count to set GPUs per instance, e.g. \" 2x RTX_4090\" (overrides --num_gpus )" ),
604596 argument ("--num_gpus" , type = int , default = None ,
605597 help = "GPUs per instance for tokens without an Nx prefix (default 1); overridden by inline Nx in --gpus" ),
606598 argument ("--timeout" , type = int , default = DEFAULT_BENCHMARK_TIMEOUT ,
@@ -651,18 +643,19 @@ def run__benchmarks(args):
651643 "(run `vastai run benchmarks --help` for usage)" ,
652644 file = sys .stderr )
653645 return 1
654- # bump retry budget so parallel polls don't exhaust VastClient's tiny default (3 attempts, ~0.7s) under autoscaler rate limits.
655- retry = max (args .retry , 8 )
656- vast = VastAI (api_key = args .api_key , server_url = args .url , retry = retry ,
657- explain = getattr (args , 'explain' , False ),
658- curl = getattr (args , 'curl' , False ))
659-
660646 if args .template_id is not None :
661647 query = {"id" : {"eq" : args .template_id }}
662648 ident = f"id={ args .template_id } "
663649 else :
664650 query = {"hash_id" : {"eq" : args .template_hash }}
665651 ident = f"hash={ args .template_hash } "
652+
653+ # bump retry budget so parallel polls don't exhaust VastClient's tiny default (3 attempts, ~0.7s) under autoscaler rate limits.
654+ retry = max (args .retry , 8 )
655+ vast = VastAI (api_key = args .api_key , server_url = args .url , retry = retry ,
656+ explain = getattr (args , 'explain' , False ),
657+ curl = getattr (args , 'curl' , False ))
658+
666659 templates = vast .search_templates (query = query )
667660 if not templates :
668661 print (f"error: template not found ({ ident } )" , file = sys .stderr )
0 commit comments