99import shutil
1010import subprocess
1111import sys
12- from concurrent .futures import ThreadPoolExecutor , as_completed
1312from decimal import Decimal , getcontext
1413from importlib import metadata
14+ from multiprocessing import Process
1515from pathlib import Path
1616
1717import distro
18- import openpyxl
1918import yaml
2019
2120# increase decimal precision
2928ROOT = Path (__file__ ).parent .parent
3029OUPUT_DIR = None
3130OP_LIST = []
31+ TIMEOUT = - 100
3232
3333NO_CPU_LIST = [
3434 "flash_attention_forward" ,
@@ -210,7 +210,12 @@ def run_cmd_capture(cmd, cwd=None, env=None):
210210 stderr = subprocess .PIPE ,
211211 text = True ,
212212 )
213- out , err = p .communicate ()
213+ try :
214+ out , err = p .communicate (timeout = 300 )
215+ except subprocess .TimeoutExpired :
216+ p .kill ()
217+ out , err = p .communicate ()
218+ return out or "" , err or "" , TIMEOUT
214219 return out or "" , err or "" , p .returncode
215220
216221
@@ -298,21 +303,30 @@ def dedup(fn):
298303def run_accuracy (gpu_id , start , index , count ):
299304 op = OP_LIST [start + index ].strip ()
300305 n = (index + 1 ) * 10 // count
301- total = len (OP_LIST )
302306 prog = "█" * n + " " * (10 - n )
303- pinfo (
304- f"[GPU { gpu_id :2d} ][{ start + index :3d} /{ total :3d} ][{ prog } ] "
305- f"Running accuracy tests for '{ op } '"
306- )
307+ nums = f"{ index + 1 } /{ count } "
308+ pinfo (f"[GPU { gpu_id :2d} ][{ nums :>7} ][{ prog } ] Running accuracy tests for '{ op } '" )
309+
307310 env = get_env (str (gpu_id ))
308311
309312 if op in NO_CPU_LIST :
310313 cmd = f'pytest -m "{ op } " -vs'
311314 else :
312315 cmd = f'pytest -m "{ op } " --ref cpu -vs'
313- out , err , code = run_cmd_capture (cmd , cwd = ROOT .joinpath ("tests" ), env = env )
316+ stdout , stderr , code = run_cmd_capture (cmd , cwd = ROOT .joinpath ("tests" ), env = env )
314317
315- combined = out + "\n " + err
318+ if code == TIMEOUT : # Timeout
319+ return {
320+ "passed" : 0 ,
321+ "failed" : 0 ,
322+ "skipped" : 0 ,
323+ "errors" : 0 ,
324+ "total" : 0 ,
325+ "status" : "TIMEOUT" ,
326+ "exit_code" : TIMEOUT ,
327+ }
328+
329+ combined = stdout + "\n ---\n " + stderr
316330 op_dir = OUTPUT_DIR .joinpath (op )
317331 log_file = op_dir .joinpath ("accuracy.log" )
318332 with open (log_file , "w" ) as f :
@@ -418,13 +432,10 @@ def run_benchmark(gpu_id, start, index, count):
418432 This returns a dict as report summary.
419433 """
420434 op = OP_LIST [start + index ].strip ()
421- total = len (OP_LIST )
422435 n = (index + 1 ) * 10 // count
423436 prog = "█" * n + " " * (10 - n )
424- pinfo (
425- f"[GPU { gpu_id :2d} ][{ start + index :3d} /{ total :3d} ][{ prog } ] "
426- f"Running perf benchmark for '{ op } '"
427- )
437+ nums = f"{ index + 1 } /{ count } "
438+ pinfo (f"[GPU { gpu_id :2d} ][{ nums :>7} ][{ prog } ] Running perf benchmark for '{ op } '" )
428439
429440 env = get_env (str (gpu_id ))
430441
@@ -437,13 +448,13 @@ def run_benchmark(gpu_id, start, index, count):
437448 pass
438449
439450 cmd = f'pytest -m "{ op } " --level core --record log'
440- out , err , code = run_cmd_capture (cmd , cwd = benchmark_dir , env = env )
451+ stdout , stderr , code = run_cmd_capture (cmd , cwd = benchmark_dir , env = env )
441452
442453 # Write raw command output
443454 op_dir = OUTPUT_DIR .joinpath (op )
444455 output_file = op_dir .joinpath ("performance_output.log" )
445456 with open (output_file , "w" ) as f :
446- f .write (out + "\n ---\n " + err )
457+ f .write (stdout + "\n ---\n " + stderr )
447458
448459 # Search for record logs which may and may not be there
449460 result_file = None
@@ -453,11 +464,14 @@ def run_benchmark(gpu_id, start, index, count):
453464
454465 # Not found
455466 if not result_file :
467+ status = "No Result"
468+ if code == TIMEOUT :
469+ status = "TIMEOUT"
456470 return {
457- "status" : "No Result" ,
471+ "status" : status ,
458472 "log" : str (output_file .relative_to (OUTPUT_DIR )),
459473 "result" : None ,
460- "exit_code" : code ,
474+ "exit_code" : TIMEOUT ,
461475 "data" : [],
462476 }
463477
@@ -506,75 +520,6 @@ def worker_proc(gpu_id, start, count):
506520 return
507521
508522
509- def write_xlsx (path ):
510- xlsx_path = path / "summary.xlsx"
511- wb = openpyxl .Workbook ()
512- ws = wb .active
513- ws .title = "Summary"
514-
515- ws .append (
516- [
517- "operator" ,
518- "acc_status" ,
519- "passed" ,
520- "failed" ,
521- "skipped" ,
522- "errors" ,
523- "total" ,
524- "acc_exit_code" ,
525- "func_name" ,
526- "avg_speedup" ,
527- "float16" ,
528- "float32" ,
529- "bfloat16" ,
530- "int16" ,
531- "int32" ,
532- "bool" ,
533- "cfloat" ,
534- "perf_status" ,
535- "perf_console_log" ,
536- "perf_result_file" ,
537- "parsed_summary" ,
538- ]
539- )
540-
541- for op , info in GLOBAL_RESULTS .items ():
542- acc = info ["accuracy" ]
543- perf = info ["performance" ]
544- rows = perf ["performance_rows" ] or [{}]
545- first = True
546-
547- for r in rows :
548- ws .append (
549- [
550- op if first else "" ,
551- acc ["status" ] if first else "" ,
552- acc ["passed" ] if first else "" ,
553- acc ["failed" ] if first else "" ,
554- acc ["skipped" ] if first else "" ,
555- acc ["errors" ] if first else "" ,
556- acc ["total" ] if first else "" ,
557- acc ["exit_code" ] if first else "" ,
558- r .get ("func_name" , "" ),
559- r .get ("avg_speedup" , "" ),
560- r .get ("float16" , "" ),
561- r .get ("float32" , "" ),
562- r .get ("bfloat16" , "" ),
563- r .get ("int16" , "" ),
564- r .get ("int32" , "" ),
565- r .get ("bool" , "" ),
566- r .get ("cfloat" , "" ),
567- perf ["status" ],
568- perf ["log" ],
569- perf ["result" ],
570- perf ["summary" ],
571- ]
572- )
573- first = False
574-
575- wb .save (str (xlsx_path ))
576-
577-
578523def main ():
579524 global OUTPUT_DIR
580525 global OP_LIST
@@ -628,24 +573,32 @@ def main():
628573 if gpu_count == 1 :
629574 worker_proc (gpu_ids [0 ], 0 , op_count )
630575 else :
631- with ThreadPoolExecutor (max_workers = gpu_count ) as exe :
632- futures = []
633- m , n = divmod (op_count , gpu_count )
634- start = 0
635- for i , gpu in enumerate (gpu_ids ):
636- if i < n :
637- count = m + 1
638- else :
639- count = m
640- futures .append (exe .submit (worker_proc , gpu , start , count ))
641- start += count
642- for f in as_completed (futures ):
643- f .result ()
576+ # with ThreadPoolExecutor(max_workers=gpu_count) as exe:
577+ # futures = []
578+ processes = []
579+ m , n = divmod (op_count , gpu_count )
580+ start = 0
581+ for i , gpu in enumerate (gpu_ids ):
582+ if i < n :
583+ count = m + 1
584+ else :
585+ count = m
586+ # futures.append(exe.submit(worker_proc, gpu, start, count))
587+ p = Process (target = worker_proc , args = (gpu , start , count ))
588+ p .start ()
589+ processes .append (p )
590+ start += count
591+
592+ for p in processes :
593+ p .join ()
644594
645595 timestamp = datetime .datetime .now ().strftime ("%Y-%m-%d %H:%M:%S" )
646596 op_data = {}
647597 for gpu_id in gpu_ids :
648598 gpu_file = OUTPUT_DIR .joinpath (f"summary{ gpu_id } .json" )
599+ if not gpu_file .exists ():
600+ perror (f"GPU { gpu_id } failed to produce a summary, recovery needed." )
601+ continue
649602 with gpu_file .open ("r" ) as f :
650603 result = json .load (f )
651604 op_data .update (result )
0 commit comments