Skip to content

Commit 5e0910b

Browse files
rpuneetclaude
andcommitted
refactor(bench): simplify metrics and improve time formatting
Simplified benchmark metrics and improved readability per user feedback: **Removed Metrics**: - Removed ops/sec (iterations show throughput) - Removed MB/s (didn't make sense for all benchmarks) - Removed total_time_sec (benchtime is fixed at 2s) **Improved Time Formatting**: - Changed "ns/op" to "Latency/op" - Added intelligent time unit formatting: - <1µs: displays as nanoseconds (e.g., "0.24ns", "7.18ns") - <1ms: displays as microseconds (e.g., "1.06µs", "95.19µs") - <1s: displays as milliseconds (e.g., "1.23ms") - >=1s: displays as seconds (e.g., "2.45s") - Removed scientific notation for small numbers (now "0.24" instead of "2.4e-01") **Number Formatting Improvements**: - All numbers now use 2 decimal places for consistency - Better K/M/B suffix formatting (e.g., "2.25M", "15.58K") **Configuration**: - Added -benchtime=2s for consistent results across runs - All benchmarks now run for exactly 2 seconds **Updated Columns**: - "Ops/Sec" → "Iterations" (shows actual iteration count) - "ns/op" → "Latency/op" (with smart time units) - Removed "MB/s" column - Kept "B/op" and "allocs/op" Example output: ``` Benchmark Iterations Latency/op B/op allocs/op MetadataFromFile 8.88K 267.93µs 569.54KB 3.62K Metadata_Tag 334.43M 7.18ns - - Parser_Parse 1.52M 1.60µs 4.53KB 54.00 ``` Much more readable and easier to compare performance across benchmarks! 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent b25442c commit 5e0910b

File tree

1 file changed

+31
-34
lines changed

1 file changed

+31
-34
lines changed

scripts/bench.py

Lines changed: 31 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,8 @@ class BenchmarkMetrics:
3535
package: str # Go package name
3636
iterations: int
3737
ns_per_op: float
38-
ops_per_sec: float # Calculated from ns_per_op
3938
bytes_per_op: int
40-
mb_per_sec: float # Throughput (if applicable)
4139
allocs_per_op: int
42-
total_time_sec: float
4340

4441

4542
@dataclass
@@ -63,7 +60,7 @@ def run_current_benchmarks(self) -> Tuple[bool, List[BenchmarkMetrics], str]:
6360
"""Run benchmarks on current commit"""
6461
try:
6562
result = subprocess.run(
66-
["go", "test", "-bench=.", "-benchmem", "-run=^$",
63+
["go", "test", "-bench=.", "-benchmem", "-benchtime=2s", "-run=^$",
6764
".", "./internal/meta/...", "./internal/format/..."],
6865
cwd=self.repo_path,
6966
capture_output=True,
@@ -89,13 +86,6 @@ def _parse_benchmark_output(self, output: str) -> List[BenchmarkMetrics]:
8986
# Pattern: BenchmarkName-12 1000000 1234 ns/op 5678 B/op 90 allocs/op
9087
pattern = r'Benchmark(\S+)-\d+\s+(\d+)\s+([\d.]+)\s+ns/op\s+([\d.]+)\s+B/op\s+([\d.]+)\s+allocs/op'
9188

92-
# Extract total time from "ok" line
93-
time_pattern = r'ok\s+\S+\s+([\d.]+)s'
94-
total_time = 0
95-
time_match = re.search(time_pattern, output)
96-
if time_match:
97-
total_time = float(time_match.group(1))
98-
9989
# Parse benchmarks line by line to track current package
10090
current_pkg = "unknown"
10191
for line in output.split("\n"):
@@ -110,20 +100,13 @@ def _parse_benchmark_output(self, output: str) -> List[BenchmarkMetrics]:
110100
bytes_per_op = int(float(match.group(4)))
111101
allocs_per_op = int(float(match.group(5)))
112102

113-
# Calculate derived metrics
114-
ops_per_sec = 1_000_000_000 / ns_per_op if ns_per_op > 0 else 0
115-
mb_per_sec = (bytes_per_op * ops_per_sec) / (1024 * 1024) if bytes_per_op > 0 else 0
116-
117103
metrics.append(BenchmarkMetrics(
118104
name=name,
119105
package=current_pkg,
120106
iterations=iterations,
121107
ns_per_op=ns_per_op,
122-
ops_per_sec=ops_per_sec,
123108
bytes_per_op=bytes_per_op,
124-
mb_per_sec=mb_per_sec,
125-
allocs_per_op=allocs_per_op,
126-
total_time_sec=total_time
109+
allocs_per_op=allocs_per_op
127110
))
128111

129112
return metrics
@@ -139,15 +122,30 @@ def format_number(n: float) -> str:
139122
return "-"
140123

141124
if n >= 1_000_000_000:
142-
return f"{n/1_000_000_000:.1f}B"
125+
return f"{n/1_000_000_000:.2f}B"
143126
elif n >= 1_000_000:
144-
return f"{n/1_000_000:.1f}M"
127+
return f"{n/1_000_000:.2f}M"
145128
elif n >= 1_000:
146-
return f"{n/1_000:.1f}K"
129+
return f"{n/1_000:.2f}K"
147130
elif n >= 1:
148-
return f"{n:.1f}"
131+
return f"{n:.2f}"
149132
else:
150-
return f"{n:.2e}"
133+
return f"{n:.2f}"
134+
135+
@staticmethod
136+
def format_time(ns: float) -> str:
137+
"""Format time with appropriate unit (ns, μs, ms, s)"""
138+
if ns == 0:
139+
return "-"
140+
141+
if ns >= 1_000_000_000: # >= 1 second
142+
return f"{ns/1_000_000_000:.2f}s"
143+
elif ns >= 1_000_000: # >= 1 millisecond
144+
return f"{ns/1_000_000:.2f}ms"
145+
elif ns >= 1_000: # >= 1 microsecond
146+
return f"{ns/1_000:.2f}µs"
147+
else: # nanoseconds
148+
return f"{ns:.2f}ns"
151149

152150
@staticmethod
153151
def format_bytes(b: int) -> str:
@@ -156,9 +154,9 @@ def format_bytes(b: int) -> str:
156154
return "-"
157155

158156
if b >= 1024*1024:
159-
return f"{b/(1024*1024):.1f}MB"
157+
return f"{b/(1024*1024):.2f}MB"
160158
elif b >= 1024:
161-
return f"{b/1024:.1f}KB"
159+
return f"{b/1024:.2f}KB"
162160
else:
163161
return f"{b}B"
164162

@@ -201,17 +199,16 @@ def format_current_results(metrics: List[BenchmarkMetrics], output: str) -> str:
201199

202200
lines.append(f"\n{category}")
203201
lines.append("-" * 100)
204-
lines.append(f"{'Benchmark':<50} {'Ops/Sec':>12} {'ns/op':>12} {'MB/s':>12} {'B/op':>12} {'allocs/op':>12}")
202+
lines.append(f"{'Benchmark':<50} {'Iterations':>12} {'Latency/op':>12} {'B/op':>12} {'allocs/op':>12}")
205203
lines.append("-" * 100)
206204

207205
for m in cat_metrics:
208-
ops_str = ReportFormatter.format_number(m.ops_per_sec)
209-
ns_str = ReportFormatter.format_number(m.ns_per_op)
210-
mb_str = f"{ReportFormatter.format_number(m.mb_per_sec)}/s" if m.mb_per_sec > 0 else "-"
206+
iters_str = ReportFormatter.format_number(m.iterations)
207+
latency_str = ReportFormatter.format_time(m.ns_per_op)
211208
bytes_str = ReportFormatter.format_bytes(m.bytes_per_op)
212209
allocs_str = ReportFormatter.format_number(m.allocs_per_op) if m.allocs_per_op > 0 else "-"
213210

214-
lines.append(f"{m.name:<50} {ops_str:>12} {ns_str:>12} {mb_str:>12} {bytes_str:>12} {allocs_str:>12}")
211+
lines.append(f"{m.name:<50} {iters_str:>12} {latency_str:>12} {bytes_str:>12} {allocs_str:>12}")
215212

216213
lines.append("")
217214
lines.append("=" * 100)
@@ -226,12 +223,12 @@ def format_current_results(metrics: List[BenchmarkMetrics], output: str) -> str:
226223
slowest = max(metrics, key=lambda m: m.ns_per_op) if metrics else None
227224

228225
lines.append(f"Total Benchmarks: {total_benchmarks}")
229-
lines.append(f"Average Time: {ReportFormatter.format_number(avg_ns)} ns/op")
226+
lines.append(f"Average Latency: {ReportFormatter.format_time(avg_ns)}/op")
230227
lines.append(f"Total Memory Allocated: {ReportFormatter.format_bytes(total_allocs)} across all benchmarks")
231228
if fastest:
232-
lines.append(f"Fastest: {fastest.name} ({ReportFormatter.format_number(fastest.ns_per_op)} ns/op)")
229+
lines.append(f"Fastest: {fastest.name} ({ReportFormatter.format_time(fastest.ns_per_op)}/op)")
233230
if slowest:
234-
lines.append(f"Slowest: {slowest.name} ({ReportFormatter.format_number(slowest.ns_per_op)} ns/op)")
231+
lines.append(f"Slowest: {slowest.name} ({ReportFormatter.format_time(slowest.ns_per_op)}/op)")
235232
lines.append("")
236233

237234
return "\n".join(lines)

0 commit comments

Comments
 (0)