1+ #!/usr/bin/env python3
2+
3+ import time
4+ import argparse
5+
6+ from jobspec .transformer .flux import FluxHierarchy
7+ from jobspec .logger import LogColors
8+
9+ def parse_args ():
10+ parser = argparse .ArgumentParser (description = "Run job throughput test on a Flux Hierarchy" )
11+ parser .add_argument ("config" , help = "Path to the FluxHierarchy YAML configuration file" )
12+ parser .add_argument ("-n" , "--njobs" , type = int , metavar = "N" , help = "Total number of jobs to run" , default = 100 )
13+ parser .add_argument ("-t" , "--runtime" , help = "Simulated runtime of each job (default=1ms)" , default = "0.001s" )
14+ parser .add_argument ("-x" , "--exec" , help = "Do not simulate, actually run jobs" , action = "store_true" )
15+ parser .add_argument ("-o" , "--setopt" , action = "append" , help = "Set shell option OPT or OPT=VAL" , metavar = "OPT" )
16+ parser .add_argument ("--setattr" , action = "append" , help = "Set job attribute ATTR=VAL" , metavar = "ATTR=VAL" )
17+ parser .add_argument ("command" , nargs = argparse .REMAINDER , default = ["true" ])
18+ return parser .parse_args ()
19+
20+ def main ():
21+ args = parse_args ()
22+
23+ # Instantiate, build, and connect to the Flux Hierarchy!
24+ hierarchy = FluxHierarchy (args .config )
25+ hierarchy .start (interactive = False )
26+
27+ # Default to true if not set.
28+ if not args .command :
29+ args .command = ["true" ]
30+
31+ # Run the throughput test using the specialized 'throughput' method
32+ time0 = time .time ()
33+ jobs = hierarchy .throughput (args .command , args .njobs )
34+
35+ if not jobs :
36+ print (f"{ LogColors .RED } No jobs were tracked. Cannot calculate throughput.{ LogColors .ENDC } " )
37+ return
38+
39+ # 3. Analyze and print results (logic is unchanged)
40+ first = jobs [min (jobs .keys (), key = lambda x : jobs [x ].get ("submit" , type ("o" , (), {"timestamp" : float ('inf' )})()).timestamp )]
41+ last = jobs [max (jobs .keys (), key = lambda x : jobs [x ].get ("clean" , type ("o" , (), {"timestamp" : float ('-inf' )})()).timestamp )]
42+ lastsubmit = jobs [max (jobs .keys (), key = lambda x : jobs [x ]["t_submit" ])]
43+
44+ submit_time = lastsubmit ["t_submit" ] - time0
45+ sjps = args .njobs / submit_time if submit_time > 0 else float ('inf' )
46+ script_runtime = time .time () - time0
47+
48+ job_runtime = last ["clean" ].timestamp - first ["submit" ].timestamp
49+ jps = args .njobs / job_runtime if job_runtime > 0 else float ('inf' )
50+ jpsb = args .njobs / script_runtime if script_runtime > 0 else float ('inf' )
51+
52+ print (f"\n --- Throughput Results ---" )
53+ print (f"number of jobs: { args .njobs } (on { len (hierarchy .handles )} workers)" )
54+ print (f" submit time: { submit_time :<6.3f} s ({ sjps :5.1f} job/s)" )
55+ print (f"script runtime: { script_runtime :<6.3f} s" )
56+ print (f" job runtime: { job_runtime :<6.3f} s" )
57+ print (f" throughput: { jps :<.1f} job/s (script: { jpsb :5.1f} job/s)" )
58+
59+
60+ if __name__ == "__main__" :
61+ main ()
0 commit comments