Skip to content

Commit 524c3a6

Browse files
author
zihugithub
committed
[Benchmark] Add benchmark output parser and update metrics upload.
1 parent 431d2f9 commit 524c3a6

2 files changed

Lines changed: 123 additions & 2 deletions

File tree

.github/workflows/functional_tests_benchmark.yml

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -235,12 +235,34 @@ jobs:
235235
exit $exit_code
236236
timeout-minutes: 60
237237

238+
- name: Parse benchmark output to JSON
239+
if: always() && steps.benchmark_test.outcome == 'success'
240+
run: |
241+
set -euo pipefail
242+
cd $PROJECT_ROOT
243+
244+
TASK='${{ matrix.test_config.task }}'
245+
MODEL='${{ matrix.test_config.model }}'
246+
CASE='${{ matrix.test_config.case }}'
247+
248+
LOG_FILE="tests/functional_tests/${TASK}/${MODEL}/test_results/${CASE}/logs/host_0_localhost.output"
249+
GOLD_FILE="tests/functional_tests/${TASK}/${MODEL}/gold_values/${CASE}.json"
250+
OUTPUT_FILE="tests/functional_tests/${TASK}/${MODEL}/test_results/${CASE}/logs/benchmark_metrics.json"
251+
252+
echo "Parsing benchmark output to JSON"
253+
echo "Log file: $LOG_FILE"
254+
echo "Gold values: $GOLD_FILE"
255+
echo "Output: $OUTPUT_FILE"
256+
257+
python tests/test_utils/runners/parse_benchmark_output.py \
258+
"$LOG_FILE" "$GOLD_FILE" "$OUTPUT_FILE"
259+
238260
- name: Upload benchmark data to backend
239261
if: always() && steps.benchmark_test.outcome == 'success'
240262
uses: flagos-ai/FlagOps/actions/post-benchmark-report@main
241263
with:
242-
backend_url: 'http://10.1.4.167:30181/flagcicd-files/'
243-
results_dir: ${{ env.PROJECT_ROOT }}/tests/functional_tests/${{ matrix.test_config.task }}/${{ matrix.test_config.model }}/test_results/${{ matrix.test_config.case }}
264+
backend_url: 'http://10.1.4.167:30180/flagcicd-backend/metrics/'
265+
report_path: ${{ env.PROJECT_ROOT }}/tests/functional_tests/${{ matrix.test_config.task }}/${{ matrix.test_config.model }}/test_results/${{ matrix.test_config.case }}/logs/benchmark_metrics.json
244266
job_name: 'benchmark_${{ matrix.test_config.model }}_${{ matrix.test_config.case }}'
245267
fail_on_error: 'false'
246268

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
"""Parse host_0_localhost.output log file and produce benchmark.json.
2+
3+
Usage:
4+
python parse_benchmark_output.py <log_file> <gold_values_file> <output_json>
5+
6+
The script extracts benchmark metrics from the training log using metric keys
7+
defined in the gold values file, then writes a structured JSON report.
8+
"""
9+
10+
import json
11+
import re
12+
import sys
13+
14+
15+
# Default benchmark metric keys if no gold values file is provided
16+
DEFAULT_METRIC_KEYS = [
17+
"elapsed time per iteration (ms):",
18+
"throughput per GPU (TFLOP/s/GPU):",
19+
]
20+
21+
22+
def extract_metrics_from_log(lines, metric_keys):
23+
"""Extract metrics from training log lines.
24+
25+
Log format (pipe-separated):
26+
" [2026-01-15 09:13:30] iteration 4/10 | ... | lm loss: 1.161108E+01 | ... |"
27+
"""
28+
results = {key: [] for key in metric_keys}
29+
30+
for line in lines:
31+
if "iteration" not in line:
32+
continue
33+
34+
parts = line.split("|")
35+
for part in parts:
36+
part = part.strip()
37+
for key in metric_keys:
38+
if part.startswith(key.rstrip(":")):
39+
match = re.search(
40+
r":\s*([+-]?\d+\.?\d*(?:[eE][+-]?\d+)?)", part
41+
)
42+
if match:
43+
try:
44+
results[key].append(float(match.group(1)))
45+
except ValueError:
46+
continue
47+
48+
return results
49+
50+
51+
def main():
52+
if len(sys.argv) < 3:
53+
print(
54+
f"Usage: {sys.argv[0]} <log_file> <gold_values_file> [output_json]",
55+
file=sys.stderr,
56+
)
57+
sys.exit(1)
58+
59+
log_file = sys.argv[1]
60+
gold_values_file = sys.argv[2]
61+
output_json = sys.argv[3] if len(sys.argv) > 3 else "benchmark.json"
62+
63+
# Read log file
64+
with open(log_file, "r") as f:
65+
lines = f.readlines()
66+
67+
# Determine metric keys from gold values file
68+
try:
69+
with open(gold_values_file, "r") as f:
70+
gold_data = json.load(f)
71+
metric_keys = list(gold_data.keys())
72+
except (FileNotFoundError, json.JSONDecodeError):
73+
print(
74+
f"Warning: Could not load gold values from {gold_values_file}, "
75+
f"using default metric keys",
76+
file=sys.stderr,
77+
)
78+
metric_keys = DEFAULT_METRIC_KEYS
79+
80+
# Extract metrics
81+
metrics = extract_metrics_from_log(lines, metric_keys)
82+
83+
# Build benchmark.json with values structure matching gold values format
84+
benchmark = {}
85+
for key in metric_keys:
86+
benchmark[key] = {"values": metrics.get(key, [])}
87+
88+
# Write output
89+
with open(output_json, "w") as f:
90+
json.dump(benchmark, f, indent=4)
91+
92+
print(f"Benchmark results written to {output_json}")
93+
print(f"Metrics extracted: {list(benchmark.keys())}")
94+
for key, data in benchmark.items():
95+
print(f" {key}: {len(data['values'])} values")
96+
97+
98+
if __name__ == "__main__":
99+
main()

0 commit comments

Comments
 (0)