Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 141 additions & 19 deletions .github/workflows/benchmark.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,12 @@ permissions:

jobs:
benchmark:
name: Benchmark
name: Benchmark (Gungraun)
runs-on: ubuntu-latest
env:
GUNGRAUN_HOME: ${{ github.workspace }}/target/gungraun
GUNGRAUN_VALGRIND_BIN: /usr/bin/valgrind
GUNGRAUN_HAS_BASELINE: "0"
steps:
- name: Checkout Branch
uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2
Expand All @@ -41,31 +45,149 @@ jobs:

- uses: actions-rust-lang/setup-rust-toolchain@46268bd060767258de96ed93c1251119784f2ab6 # v1.16.1
with:
cache-key: benchmark
cache-key: benchmark-gungraun
cache-save-if: ${{ github.ref_name == 'main' }}

- uses: ./.github/actions/pnpm

- name: Install valgrind
run: sudo apt-get update && sudo apt-get install -y valgrind

- uses: taiki-e/install-action@055f5df8c3f65ea01cd41e9dc855becd88953486 # v2.75.18
with:
tool: cargo-codspeed
tool: gungraun-runner@0.18.2

- uses: ./.github/actions/pnpm
- name: Build Benchmark
- name: Run gungraun baseline benchmark (PR base)
if: github.event_name == 'pull_request'
timeout-minutes: 45
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
RUSTFLAGS: "-C debuginfo=1 -C strip=none -g"
run: cargo codspeed build
run: |
set -euo pipefail
WORKTREE_DIR=/tmp/gungraun-pr-base
cleanup() {
if [ -d "${WORKTREE_DIR}" ]; then
git worktree remove "${WORKTREE_DIR}" --force || true
fi
}
trap cleanup EXIT

- name: Run CPU benchmark
uses: CodSpeedHQ/action@281164b0f014a4e7badd2c02cecad9b595b70537 # https://github.com/CodSpeedHQ/action/releases/tag/v4.10.6
timeout-minutes: 30
with:
mode: simulation
run: cargo codspeed run
token: ${{ secrets.CODSPEED_TOKEN }}
echo "GUNGRAUN_HAS_BASELINE=0" >> "$GITHUB_ENV"
git fetch --no-tags --depth=1 origin "${BASE_SHA}"
git worktree add "${WORKTREE_DIR}" "${BASE_SHA}"
pushd "${WORKTREE_DIR}"
pnpm install --dir benches --ignore-workspace

if cargo bench --bench resolver_gungraun --no-run >/dev/null 2>&1; then
cargo bench --bench resolver_gungraun -- \
--home="${GUNGRAUN_HOME}" \
--save-baseline=pr_base \
--save-summary=pretty-json
echo "GUNGRAUN_HAS_BASELINE=1" >> "$GITHUB_ENV"
else
echo "Base commit ${BASE_SHA} has no resolver_gungraun bench target; skipping baseline compare."
fi

popd

- name: Run gungraun benchmark with baseline compare (PR head)
if: github.event_name == 'pull_request'
timeout-minutes: 45
env:
RUSTFLAGS: "-C debuginfo=1 -C strip=none -g"
run: |
set -euo pipefail
mkdir -p target/gungraun
if [ "${GUNGRAUN_HAS_BASELINE:-0}" = "1" ]; then
cargo bench --bench resolver_gungraun -- \
--home="${GUNGRAUN_HOME}" \
--baseline=pr_base \
--save-summary=pretty-json \
--output-format=json \
> target/gungraun/benchmark-diff.ndjson
else
cargo bench --bench resolver_gungraun -- \
--home="${GUNGRAUN_HOME}" \
--save-summary=pretty-json
fi

- name: Run gungraun benchmark (push/manual)
if: github.event_name != 'pull_request'
timeout-minutes: 45
env:
RUSTFLAGS: "-C debuginfo=1 -C strip=none -g"
run: |
set -euxo pipefail
cargo bench --bench resolver_gungraun -- \
--home="${GUNGRAUN_HOME}" \
--save-summary=pretty-json

- name: Publish benchmark and commit diff summary
if: always()
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.sha }}
run: |
set -euo pipefail

echo "## Gungraun Benchmark" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"

if [ "${{ github.event_name }}" = "pull_request" ]; then
echo "- Base commit: \`${BASE_SHA}\`" >> "$GITHUB_STEP_SUMMARY"
echo "- Head commit: \`${HEAD_SHA}\`" >> "$GITHUB_STEP_SUMMARY"
echo "- Baseline compare: $([ "${GUNGRAUN_HAS_BASELINE:-0}" = "1" ] && echo "enabled" || echo "skipped (base has no resolver_gungraun target)")" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "### Benchmark Diff (Estimated Cycles)" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "| Benchmark | Base | Head | Diff |" >> "$GITHUB_STEP_SUMMARY"
echo "|---|---:|---:|---:|" >> "$GITHUB_STEP_SUMMARY"

shopt -s nullglob
summary_files=(target/gungraun/rspack_resolver/resolver_gungraun/resolver_group/*/summary.json)
if [ "${#summary_files[@]}" -eq 0 ]; then
echo "| (no benchmark summary found) | - | - | - |" >> "$GITHUB_STEP_SUMMARY"
else
for f in "${summary_files[@]}"; do
if [ "${GUNGRAUN_HAS_BASELINE:-0}" = "1" ]; then
jq -r '
def n: (.Int // .Float // 0);
def m($k): .profiles[0].summaries.total.summary.Callgrind[$k];
"| \(.id) | \((m("EstimatedCycles").metrics.Both[1] | n)) | \((m("EstimatedCycles").metrics.Both[0] | n)) | \((m("EstimatedCycles").diffs.diff_pct | tonumber) | if . > 0 then "+" + (.|tostring) else (.|tostring) end)% |"
' "$f" >> "$GITHUB_STEP_SUMMARY"
else
jq -r '
def n: (.Int // .Float // 0);
def m($k): .profiles[0].summaries.total.summary.Callgrind[$k];
"| \(.id) | - | \((m("EstimatedCycles").metrics.Both[0] | n)) | - |"
' "$f" >> "$GITHUB_STEP_SUMMARY"
fi
done
fi

echo "" >> "$GITHUB_STEP_SUMMARY"
echo "### Code Diff (Base..Head)" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
echo '```text' >> "$GITHUB_STEP_SUMMARY"
git diff --stat --no-color "${BASE_SHA}..${HEAD_SHA}" >> "$GITHUB_STEP_SUMMARY"
echo '```' >> "$GITHUB_STEP_SUMMARY"
else
echo "- Commit: \`${HEAD_SHA}\`" >> "$GITHUB_STEP_SUMMARY"
fi

echo "" >> "$GITHUB_STEP_SUMMARY"
if [ -d target/gungraun ]; then
flamegraph_count="$(find target/gungraun -name '*.flamegraph*.svg' | wc -l | tr -d ' ')"
echo "Flamegraph SVG files: ${flamegraph_count}" >> "$GITHUB_STEP_SUMMARY"
else
echo "Flamegraph SVG files: 0 (target/gungraun not generated)" >> "$GITHUB_STEP_SUMMARY"
fi

- name: Run memory benchmark
uses: CodSpeedHQ/action@281164b0f014a4e7badd2c02cecad9b595b70537 # https://github.com/CodSpeedHQ/action/releases/tag/v4.10.6
timeout-minutes: 30
- name: Upload gungraun result files (including flamegraphs)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
mode: memory
run: cargo codspeed run
token: ${{ secrets.CODSPEED_TOKEN }}
name: gungraun-target
path: target/gungraun
if-no-files-found: ignore
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ jobs:
cache: false
- uses: taiki-e/install-action@055f5df8c3f65ea01cd41e9dc855becd88953486 # v2.75.18
with:
tool: cargo-deny
tool: cargo-deny@0.19.4

- if: steps.filter.outputs.src == 'true'
run: cargo deny check
Expand Down
99 changes: 99 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ doctest = false
harness = false
name = "resolver"

[[bench]]
harness = false
name = "resolver_gungraun"

[lints.clippy]
all = { level = "warn", priority = -1 }
cargo = { level = "warn", priority = -1 }
Expand Down Expand Up @@ -107,6 +111,7 @@ tokio = { version = "1.48.0", default-features = false, features = ["sync", "rt"
criterion = { version = "4.3.0", package = "codspeed-criterion-compat", default-features = false, features = [
"async_tokio",
] }
gungraun = "0.18.2"

normalize-path = { version = "0.2.1" }
rayon = { version = "1.11.0" }
Expand Down
27 changes: 27 additions & 0 deletions benches/resolver_gungraun.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use gungraun::{
binary_benchmark, binary_benchmark_group, main, BinaryBenchmarkConfig, Callgrind, Command,
FlamegraphConfig, FlamegraphKind,
};

#[binary_benchmark]
#[bench::resolve_dependencies("deps")]
#[bench::resolve_many_extensions("many_extensions")]
#[bench::resolve_dependencies_pnp("pnp")]
Comment on lines +7 to +9
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Restore dropped symlink and threaded benchmark cases

The new workflow now runs only cargo bench --bench resolver_gungraun, but this Gungraun bench registers only deps, many_extensions, and pnp. The previous continuous benchmark also covered multi-thread resolution plus the two symlink-resolution cases in benches/resolver.rs, so regressions in concurrent cache behavior or symlink handling will no longer be measured by CI after this switch.

Useful? React with 👍 / 👎.

fn bench_resolver(scenario: &str) -> Command {
Command::new(env!("CARGO_BIN_EXE_resolver_bench_driver"))
.arg(scenario)
.build()
}

binary_benchmark_group!(name = resolver_group, benchmarks = bench_resolver);

main!(
config = BinaryBenchmarkConfig::default().env_clear(false).tool(
Callgrind::default().flamegraph(
FlamegraphConfig::default()
.kind(FlamegraphKind::All)
.normalize_differential(true),
),
),
binary_benchmark_groups = resolver_group
);
1 change: 1 addition & 0 deletions deny.toml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ ignore = [
#{ id = "RUSTSEC-0000-0000", reason = "you can specify a reason the advisory is ignored" },
#"a-crate-that-is-yanked@0.1.1", # you can also ignore yanked crate versions if you wish
#{ crate = "a-crate-that-is-yanked@0.1.1", reason = "you can specify why you are ignoring the yanked crate" },
{ id = "RUSTSEC-2025-0141", reason = "Transitive dependency from gungraun benchmark tooling; no safe upgrade path in current upstream release." },
]
# If this is true, then cargo deny will use the git executable to fetch advisory database.
# If this is false, then it uses a built-in git library.
Expand Down
Loading
Loading