Skip to content

Commit d1556c4

Browse files
authored
Tune run_tests script (flagos-ai#2409)
1 parent e802f80 commit d1556c4

1 file changed

Lines changed: 54 additions & 101 deletions

File tree

tools/run_tests.py

100755100644
Lines changed: 54 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,12 @@
99
import shutil
1010
import subprocess
1111
import sys
12-
from concurrent.futures import ThreadPoolExecutor, as_completed
1312
from decimal import Decimal, getcontext
1413
from importlib import metadata
14+
from multiprocessing import Process
1515
from pathlib import Path
1616

1717
import distro
18-
import openpyxl
1918
import yaml
2019

2120
# increase decimal precision
@@ -29,6 +28,7 @@
2928
ROOT = Path(__file__).parent.parent
3029
OUPUT_DIR = None
3130
OP_LIST = []
31+
TIMEOUT = -100
3232

3333
NO_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):
298303
def 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-
578523
def 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

Comments
 (0)