Skip to content

Commit 04d93a4

Browse files
authored
Merge pull request #28 from Milerius/feat/venue-decoder-crates
feat: venue decoder crates + SPSC benchmark overhaul
2 parents d48029a + 1c472ad commit 04d93a4

75 files changed

Lines changed: 5542 additions & 2297 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/scripts/bench-report.sh

Lines changed: 7 additions & 225 deletions
Original file line numberDiff line numberDiff line change
@@ -5,216 +5,12 @@ set -euo pipefail
55
# Usage: bench-report.sh <linux-json> <macos-json>
66
# Outputs markdown to stdout.
77

8-
linux_json="${1:?usage: bench-report.sh <linux-spsc> <macos-spsc> [<linux-seqlock> <macos-seqlock>] [<linux-fixed> <macos-fixed>] [<linux-market-state> <macos-market-state>]}"
9-
macos_json="${2:?usage: bench-report.sh <linux-spsc> <macos-spsc> [<linux-seqlock> <macos-seqlock>] [<linux-fixed> <macos-fixed>] [<linux-market-state> <macos-market-state>]}"
10-
linux_seqlock="${3:-}"
11-
macos_seqlock="${4:-}"
12-
linux_fixed="${5:-}"
13-
macos_fixed="${6:-}"
14-
linux_market_state="${7:-}"
15-
macos_market_state="${8:-}"
16-
17-
# Normalize workload names into (impl, pattern, element) and extract metrics.
18-
# Output: JSON array of {impl, pattern, element, ns_per_op, ...}
19-
normalize='
20-
[.results[] | {
21-
workload,
22-
impl: (
23-
if .workload | startswith("spsc/inline/") then "mantis/inline"
24-
elif .workload | startswith("copy/") then "mantis/copy"
25-
elif .workload | startswith("general/") then "mantis/general"
26-
elif .workload | startswith("spsc/rtrb/") then "rtrb"
27-
elif .workload | startswith("spsc/crossbeam/") then "crossbeam"
28-
elif .workload | startswith("spsc/rigtorp/") then "rigtorp-cpp"
29-
elif .workload | startswith("spsc/drogalis/") then "drogalis-cpp"
30-
else "other"
31-
end
32-
),
33-
pattern: (
34-
if (.workload | test("single_item/|single/")) then "single"
35-
elif (.workload | test("burst_100/|burst/100/")) then "burst_100"
36-
elif (.workload | test("burst_1000/|burst/1000/")) then "burst_1000"
37-
elif (.workload | test("batch/100/")) then "batch_100"
38-
elif (.workload | test("batch/1000/")) then "batch_1000"
39-
elif (.workload | test("full_drain/")) then "full_drain"
40-
else "other"
41-
end
42-
),
43-
element: (.workload | split("/") | last),
44-
ns_per_op: (.ns_per_op | . * 100 | round / 100),
45-
cycles: (.cycles_per_op // null | if . == null then null elif . < 0.1 then 0 else (. * 10 | round / 10) end),
46-
insns: (.instructions_per_op // null | if . == null then null else round end),
47-
bmiss: (.branch_misses_per_op // null | if . == null then null elif . < 0.1 then 0 else (. * 10 | round / 10) end)
48-
}]
49-
'
50-
51-
# Build a comparison table for a given pattern.
52-
# Args: $1=json_file $2=pattern $3=title
53-
render_comparison() {
54-
local json="$1" pattern="$2" title="$3"
55-
local data impls elements
56-
57-
data=$(jq -r --arg p "$pattern" "$normalize | map(select(.pattern == \$p))" "$json")
58-
elements=$(echo "$data" | jq -r '[.[].element] | unique | .[]')
59-
impls=$(echo "$data" | jq -r '[.[].impl] | unique | .[]')
60-
61-
# Skip if no data for this pattern
62-
if [ -z "$elements" ]; then
63-
return
64-
fi
65-
66-
# Build header
67-
local header="| Element |"
68-
local separator="|:--------|"
69-
for impl in $impls; do
70-
header="$header $impl |"
71-
separator="$separator------:|"
72-
done
73-
74-
echo "#### $title"
75-
echo ""
76-
echo "$header"
77-
echo "$separator"
78-
79-
# Build rows
80-
for elem in $elements; do
81-
local row="| \`$elem\` |"
82-
# Find the best (lowest) ns/op for this element
83-
local best
84-
best=$(echo "$data" | jq -r --arg e "$elem" \
85-
'[.[] | select(.element == $e) | .ns_per_op] | min')
86-
87-
for impl in $impls; do
88-
local cell
89-
cell=$(echo "$data" | jq -r --arg i "$impl" --arg e "$elem" \
90-
'.[] | select(.impl == $i and .element == $e) | .ns_per_op // empty' 2>/dev/null)
91-
if [ -z "$cell" ]; then
92-
row="$row - |"
93-
else
94-
# Bold the best value with trophy emoji
95-
if [ "$cell" = "$best" ]; then
96-
row="$row **${cell}** 🏆 |"
97-
else
98-
row="$row $cell |"
99-
fi
100-
fi
101-
done
102-
echo "$row"
103-
done
104-
echo ""
105-
}
106-
107-
# Build an insns/op comparison table for a given pattern.
108-
render_insns_comparison() {
109-
local json="$1" pattern="$2" title="$3"
110-
local data impls elements has_any
111-
112-
data=$(jq -r --arg p "$pattern" "$normalize | map(select(.pattern == \$p))" "$json")
113-
elements=$(echo "$data" | jq -r '[.[].element] | unique | .[]')
114-
impls=$(echo "$data" | jq -r '[.[].impl] | unique | .[]')
115-
116-
# Check if any insns data exists
117-
has_any=$(echo "$data" | jq -r '[.[].insns | select(. != null)] | length')
118-
if [ "$has_any" = "0" ] || [ -z "$elements" ]; then
119-
return
120-
fi
121-
122-
local header="| Element |"
123-
local separator="|:--------|"
124-
for impl in $impls; do
125-
header="$header $impl |"
126-
separator="$separator------:|"
127-
done
128-
129-
echo "#### $title"
130-
echo ""
131-
echo "$header"
132-
echo "$separator"
133-
134-
for elem in $elements; do
135-
local row="| \`$elem\` |"
136-
local best
137-
best=$(echo "$data" | jq -r --arg e "$elem" \
138-
'[.[] | select(.element == $e) | .insns | select(. != null)] | min // empty')
139-
140-
for impl in $impls; do
141-
local cell
142-
cell=$(echo "$data" | jq -r --arg i "$impl" --arg e "$elem" \
143-
'.[] | select(.impl == $i and .element == $e) | .insns // empty' 2>/dev/null)
144-
if [ -z "$cell" ] || [ "$cell" = "null" ]; then
145-
row="$row - |"
146-
elif [ -n "$best" ] && [ "$cell" = "$best" ]; then
147-
row="$row **${cell}** 🏆 |"
148-
else
149-
row="$row $cell |"
150-
fi
151-
done
152-
echo "$row"
153-
done
154-
echo ""
155-
}
156-
157-
# Render full detailed table in a collapsible section.
158-
render_full_table() {
159-
local json="$1"
160-
161-
echo "| Workload | ns/op | p50 | p99 | cycles | insns | bmiss | l1d | llc |"
162-
echo "|:---------|------:|----:|----:|-------:|------:|------:|----:|----:|"
163-
164-
jq -r '.results[] |
165-
[
166-
.workload,
167-
(.ns_per_op | . * 100 | round / 100 | tostring),
168-
(.p50_ns | . * 10 | round / 10 | tostring),
169-
(.p99_ns | . * 10 | round / 10 | tostring),
170-
(.cycles_per_op // null | if . == null then "-" elif . < 0.1 then "<0.1" else (. * 10 | round / 10 | tostring) end),
171-
(.instructions_per_op // null | if . == null then "-" else (. | round | tostring) end),
172-
(.branch_misses_per_op // null | if . == null then "-" elif . < 0.1 then "<0.1" else (. * 10 | round / 10 | tostring) end),
173-
(.l1_misses_per_op // null | if . == null then "-" elif . == 0 then "0" else (. * 10 | round / 10 | tostring) end),
174-
(.llc_misses_per_op // null | if . == null then "-" elif . == 0 then "0" else (. * 10 | round / 10 | tostring) end)
175-
] | "| " + join(" | ") + " |"
176-
' "$json"
177-
}
178-
179-
# Render one platform section.
180-
render_platform() {
181-
local json="$1" label="$2"
182-
local cpu arch compiler
183-
184-
if [ ! -f "$json" ]; then
185-
echo "*${label} benchmark results not available.*"
186-
echo ""
187-
return
188-
fi
189-
190-
cpu=$(jq -r '.cpu' "$json")
191-
arch=$(jq -r '.arch' "$json")
192-
compiler=$(jq -r '.compiler' "$json")
193-
194-
echo "**CPU:** \`${cpu}\` | **Arch:** \`${arch}\` | **Compiler:** \`${compiler}\`"
195-
echo ""
196-
197-
echo "##### Latency (ns/op, lower is better)"
198-
echo ""
199-
render_comparison "$json" "single" "Single Push+Pop"
200-
render_comparison "$json" "burst_100" "Burst 100"
201-
render_comparison "$json" "burst_1000" "Burst 1000"
202-
render_comparison "$json" "batch_100" "Batch 100"
203-
render_comparison "$json" "batch_1000" "Batch 1000"
204-
render_comparison "$json" "full_drain" "Full Drain"
205-
206-
echo "##### Instructions per Op (lower is better)"
207-
echo ""
208-
render_insns_comparison "$json" "single" "Single Push+Pop"
209-
render_insns_comparison "$json" "burst_100" "Burst 100"
210-
211-
echo "<details>"
212-
echo "<summary>Full results (all fields)</summary>"
213-
echo ""
214-
render_full_table "$json"
215-
echo ""
216-
echo "</details>"
217-
}
8+
linux_seqlock="${1:?usage: bench-report.sh <linux-seqlock> <macos-seqlock> [<linux-fixed> <macos-fixed>] [<linux-market-state> <macos-market-state>]}"
9+
macos_seqlock="${2:?}"
10+
linux_fixed="${3:-}"
11+
macos_fixed="${4:-}"
12+
linux_market_state="${5:-}"
13+
macos_market_state="${6:-}"
21814

21915
# Render grouped benchmark results from Criterion JSON report.
22016
# Args: $1=json_file $2=label $3=suite_name (e.g. "fixed-point", "seqlock")
@@ -266,22 +62,8 @@ cat <<HEADER
26662
26763
HEADER
26864

269-
echo "<details open>"
270-
echo "<summary><strong>Linux</strong></summary>"
271-
echo ""
272-
render_platform "$linux_json" "Linux"
273-
echo "</details>"
274-
echo ""
275-
276-
echo "<details open>"
277-
echo "<summary><strong>macOS</strong></summary>"
278-
echo ""
279-
render_platform "$macos_json" "macOS"
280-
echo "</details>"
281-
282-
# Seqlock benchmarks (optional)
65+
# Seqlock benchmarks
28366
if [ -n "$linux_seqlock" ] || [ -n "$macos_seqlock" ]; then
284-
echo ""
28567
echo "### Sequence Lock (mantis-seqlock)"
28668
echo ""
28769

.github/scripts/criterion-to-json.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ for est in "${matched_ests[@]}"; do
7070
'. + [{"workload": $w, "ns_per_op": ($ns | . * 100 | round / 100)}]')
7171
done
7272

73-
# Wrap in the same schema as spsc report
73+
# Wrap in the standard bench report schema
7474
cpu="unknown"
7575
arch="unknown"
7676
compiler="unknown"

.github/workflows/bench.yml

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -39,16 +39,6 @@ jobs:
3939
- name: Allow perf_event_open (Linux)
4040
if: runner.os == 'Linux'
4141
run: sudo sysctl -w kernel.perf_event_paranoid=1
42-
# ── SPSC benchmarks (custom harness, outputs JSON directly) ──
43-
- name: Run SPSC benchmarks
44-
run: cargo +nightly bench --bench spsc --all-features
45-
env:
46-
RUSTFLAGS: "-C target-cpu=native"
47-
- name: Convert SPSC report for CI tracking
48-
run: |
49-
jq '[.results[] | {name: .workload, unit: "ns/op", value: .ns_per_op}]' \
50-
target/bench-report-spsc.json > bench-output-spsc.json
51-
5242
# ── Seqlock benchmarks (Criterion) ──
5343
# IMPORTANT: Each Criterion bench must be converted IMMEDIATELY after running,
5444
# before the next `rm -rf target/criterion`. The criterion-to-json.sh script
@@ -97,7 +87,7 @@ jobs:
9787
target/bench-report-market_state.json > bench-output-market_state.json
9888
- name: Merge benchmark outputs
9989
run: |
100-
jq -s 'add' bench-output-spsc.json bench-output-seqlock.json bench-output-fixed.json bench-output-market_state.json > bench-output.json
90+
jq -s 'add' bench-output-seqlock.json bench-output-fixed.json bench-output-market_state.json > bench-output.json
10191
- name: Restore Cargo.lock before gh-pages switch
10292
run: git checkout -- Cargo.lock
10393
- name: Store benchmark result
@@ -117,7 +107,6 @@ jobs:
117107
with:
118108
name: benchmark-results-${{ matrix.os }}
119109
path: |
120-
target/bench-report-spsc.json
121110
target/bench-report-seqlock.json
122111
target/bench-report-fixed.json
123112
target/bench-report-market_state.json
@@ -149,15 +138,16 @@ jobs:
149138
continue-on-error: true
150139
- name: Generate markdown report
151140
run: |
152-
linux_spsc="results/linux/target/bench-report-spsc.json"
153-
macos_spsc="results/macos/target/bench-report-spsc.json"
154141
linux_seqlock="results/linux/target/bench-report-seqlock.json"
155142
macos_seqlock="results/macos/target/bench-report-seqlock.json"
156143
linux_fixed="results/linux/target/bench-report-fixed.json"
157144
macos_fixed="results/macos/target/bench-report-fixed.json"
158145
linux_market_state="results/linux/target/bench-report-market_state.json"
159146
macos_market_state="results/macos/target/bench-report-market_state.json"
160-
bash .github/scripts/bench-report.sh "$linux_spsc" "$macos_spsc" "$linux_seqlock" "$macos_seqlock" "$linux_fixed" "$macos_fixed" "$linux_market_state" "$macos_market_state" > report.md
147+
bash .github/scripts/bench-report.sh \
148+
"$linux_seqlock" "$macos_seqlock" \
149+
"$linux_fixed" "$macos_fixed" \
150+
"$linux_market_state" "$macos_market_state" > report.md
161151
- name: Post PR comment
162152
uses: marocchino/sticky-pull-request-comment@52423e01640425a022ef5fd42c6fb5f633a02728 # v2.9.1
163153
with:

0 commit comments

Comments
 (0)