From 81f9f91ecbd74b6209ed04bd57475e782d7fb856 Mon Sep 17 00:00:00 2001 From: jdx <216188+jdx@users.noreply.github.com> Date: Sun, 25 May 2025 11:19:11 -0500 Subject: [PATCH 1/2] chore: improve shfmt linter (#5181) --- .github/workflows/hyperfine.yml | 28 ++++-- .gitignore | 3 + .shellcheckrc | 1 + e2e/run_all_tests | 3 + e2e/run_test | 1 + tasks.md | 14 +++ tasks.toml | 5 + xtasks/test/build-perf-workspace | 47 +++++++++ xtasks/test/perf | 160 +++++++++++++++++++++++++++++++ 9 files changed, 255 insertions(+), 7 deletions(-) create mode 100755 xtasks/test/build-perf-workspace create mode 100755 xtasks/test/perf diff --git a/.github/workflows/hyperfine.yml b/.github/workflows/hyperfine.yml index 6c9a7e2ecd..5cc0a17108 100644 --- a/.github/workflows/hyperfine.yml +++ b/.github/workflows/hyperfine.yml @@ -4,6 +4,9 @@ on: branches: ["main"] pull_request: branches: ["main"] + paths: + - ".github/workflows/hyperfine.yml" + - "Cargo.toml" workflow_dispatch: concurrency: @@ -22,7 +25,6 @@ jobs: benchmark: runs-on: ubuntu-latest timeout-minutes: 20 - if: github.head_ref == 'release' || github.head_ref == 'hyperfine' steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 with: @@ -56,6 +58,15 @@ jobs: ~/.cache/mise - run: mise x wait-for-gh-rate-limit -- wait-for-gh-rate-limit - run: mise install + - run: mise run test:perf + env: + NUM_TOOLS: 200 + NUM_TASKS: 2000 + MISE_ALT: mise-${{ steps.versions.outputs.release }} + - uses: actions/upload-artifact@v4 + with: + name: flamegraphs + path: flamegraphs - run: | CMDS=( "x -- echo" @@ -63,21 +74,24 @@ jobs: "hook-env" "ls" ) - echo "# Hyperfine Performance" >> comment.md + echo "## Hyperfine Performance" >> comment.md for cmd in "${CMDS[@]}"; do - #mise x hyperfine -- hyperfine -N -w 10 -r 500 --export-markdown out.md --reference "mise-main $cmd" "mise-${{ steps.versions.outputs.release }} $cmd" "mise $cmd" - mise x hyperfine -- hyperfine -N -w 10 -r 500 --export-markdown out.md --reference "mise-${{ steps.versions.outputs.release }} $cmd" "mise $cmd" + if [ -n "${MISE_ALT:-}" ]; then + mise x hyperfine -- hyperfine -N -w 10 -r 500 --export-markdown out.md --reference "$MISE_ALT $cmd" "mise $cmd" + else + mise x hyperfine -- hyperfine -N -w 10 -r 500 --export-markdown out.md --reference "mise-${{ steps.versions.outputs.release }} $cmd" "mise $cmd" + fi echo "### \`mise $cmd\`" >> comment.md cat out.md >> comment.md done - cat comment.md >> "$GITHUB_STEP_SUMMARY" env: SHELL: zsh + - run: cp comment.md "$GITHUB_STEP_SUMMARY" + if: always() && github.event_name == 'pull_request' - name: Comment on PR uses: thollander/actions-comment-pull-request@24bffb9b452ba05a4f3f77933840a6a841d1b32b # v3 - if: github.event_name == 'pull_request' + if: always() && github.event_name == 'pull_request' continue-on-error: true - #if: "startsWith(github.event.pull_request.title, 'perf:') || startsWith(github.event.pull_request.title, 'chore: release')" with: file-path: comment.md comment-tag: hyperfine diff --git a/.gitignore b/.gitignore index ff58044da7..16df7b0c31 100644 --- a/.gitignore +++ b/.gitignore @@ -4,10 +4,13 @@ package-lock.json .mise.lock /mise.local.toml +/perf-workspace *.log *.profraw *.lcov +flamegraph.svg +cargo-flamegraph.trace **/snapshots/*.snap.new diff --git a/.shellcheckrc b/.shellcheckrc index 06d39494eb..0300b4f3fa 100644 --- a/.shellcheckrc +++ b/.shellcheckrc @@ -1,4 +1,5 @@ disable=SC1008 disable=SC2088 +disable=SC2129 disable=SC2164 disable=SC2317 diff --git a/e2e/run_all_tests b/e2e/run_all_tests index eed63b8058..3212e5c78b 100755 --- a/e2e/run_all_tests +++ b/e2e/run_all_tests @@ -52,6 +52,9 @@ for index in "${!FILES[@]}"; do test_count=$((test_count + 1)) done +if [[ -v GITHUB_STEP_SUMMARY ]] && [[ -f "$GITHUB_STEP_SUMMARY-extra" ]]; then + cat "$GITHUB_STEP_SUMMARY-extra" >>"$GITHUB_STEP_SUMMARY" +fi echo "E2E: ran $test_count tests, skipped $skipped_count tests" >&2 exit "$status" diff --git a/e2e/run_test b/e2e/run_test index 94f5d7ffff..1e91dff46d 100755 --- a/e2e/run_test +++ b/e2e/run_test @@ -55,6 +55,7 @@ within_isolated_env() { CARGO_LLVM_COV_SHOW_ENV="${CARGO_LLVM_COV_SHOW_ENV:-}" \ CARGO_LLVM_COV_TARGET_DIR="${CARGO_LLVM_COV_TARGET_DIR:-}" \ GITHUB_ACTION="${GITHUB_ACTION:-}" \ + GITHUB_STEP_SUMMARY="${GITHUB_STEP_SUMMARY:-}" \ GITHUB_TOKEN="${GITHUB_TOKEN:-}" \ GOPROXY="${GOPROXY:-}" \ HOME="$TEST_HOME" \ diff --git a/tasks.md b/tasks.md index 00feea541e..c3a9eec0f7 100644 --- a/tasks.md +++ b/tasks.md @@ -90,6 +90,10 @@ User to run as - **Usage**: `filetask.bat` +## `flamegraph` + +- **Usage**: `flamegraph` + ## `install-dev` - **Usage**: `install-dev` @@ -213,6 +217,12 @@ update test snapshots run all tests +## `test:build-perf-workspace` + +- **Usage**: `test:build-perf-workspace` + +task description + ## `test:coverage` - **Usage**: `test:coverage` @@ -228,6 +238,10 @@ run all tests with coverage report run end-to-end tests +## `test:perf` + +- **Usage**: `test:perf` + ## `test:shuffle` - **Usage**: `test:shuffle` diff --git a/tasks.toml b/tasks.toml index 57b2f307d2..44c2d850b5 100644 --- a/tasks.toml +++ b/tasks.toml @@ -163,3 +163,8 @@ description = "a task for testing" [pre-commit] env = { PRE_COMMIT = 1 } run = ["mise run lint"] + +[flamegraph] +tools = { "cargo:flamegraph" = "latest" } +env = { CARGO_PROFILE_RELEASE_DEBUG = "true" } +run = "cargo flamegraph" diff --git a/xtasks/test/build-perf-workspace b/xtasks/test/build-perf-workspace new file mode 100755 index 0000000000..b579c134a9 --- /dev/null +++ b/xtasks/test/build-perf-workspace @@ -0,0 +1,47 @@ +#!/usr/bin/env bash +set -euo pipefail + +mkdir -p perf-workspace +cd perf-workspace +MISE_DATA_DIR="${MISE_DATA_DIR:-$HOME/.local/share/mise}" +mkdir -p .mise-tasks "$MISE_DATA_DIR/plugins" + +num_tasks=${NUM_TASKS:-1000} +num_tools=${NUM_TOOLS:-100} + +create_tasks() { + cat <.mise-tasks/perf-0 +#!/bin/sh +#MISE description="task description" +echo running task +EOF + chmod +x .mise-tasks/perf-0 + for i in $(seq 1 "$num_tasks"); do + ln -sf ./perf-0 ".mise-tasks/perf-$i" + done +} + +create_tools() { + # shellcheck disable=SC2207 + tools=($(seq 0 "$num_tools")) + if [ ! -d "$MISE_DATA_DIR/plugins/tiny" ]; then + git clone https://github.com/jdx/mise-tiny.git "$MISE_DATA_DIR/plugins/tiny" + fi + { + echo "[tools]" + for i in "${tools[@]}"; do + echo "\"perf-$i\" = \"$i\"" + done + echo "[alias]" + for i in "${tools[@]}"; do + echo "\"perf-$i\" = \"asdf:jdx/mise-tiny\"" + done + } >mise.toml + + for i in "${tools[@]}"; do + ln -sf ./tiny "$MISE_DATA_DIR/plugins/perf-$i" + done +} + +create_tasks +create_tools diff --git a/xtasks/test/perf b/xtasks/test/perf new file mode 100755 index 0000000000..ada499f8c7 --- /dev/null +++ b/xtasks/test/perf @@ -0,0 +1,160 @@ +#!/usr/bin/env bash +#MISE depends=["test:build-perf-workspace"] +# shellcheck disable=SC2086,SC2129 +set -xeuo pipefail + +num_tools="${NUM_TOOLS:-100}" +num_tasks="${NUM_TASKS:-1000}" +cd perf-workspace +mkdir -p flamegraphs +MISE_DATA_DIR="${MISE_DATA_DIR:-$HOME/.local/share/mise}" +declare -A benchmarks +declare -A recent_benchmarks +declare -A maximums +declare -A minimums +declare -A alt_benchmarks +names=() +errors=() + +recent_benchmarks["install-uncached"]=2900 +recent_benchmarks["install-cached"]=2400 +recent_benchmarks["ls-uncached"]=2360 +recent_benchmarks["ls-cached"]=2403 +recent_benchmarks["bin-paths-uncached"]=2400 +recent_benchmarks["bin-paths-cached"]=2300 +recent_benchmarks["task-ls-uncached"]=2400 +recent_benchmarks["task-ls-cached"]=2400 + +for name in "${!recent_benchmarks[@]}"; do + maximums["$name"]=$(("${recent_benchmarks["$name"]}" * 12 / 10)) + minimums["$name"]=$(("${recent_benchmarks["$name"]}" * 8 / 10)) +done + +benchmark_error() { + local name="$1" + local error="$2" + local cmd="$3" + mise cache clear + CLICOLOR_FORCE=1 MISE_TIMINGS=1 timeout -v 10 mise $4 >/dev/null || true + CLICOLOR_FORCE=1 MISE_TIMINGS=1 timeout -v 10 mise $4 >/dev/null || true + # TODO: mise task + if [ "$name" = "*-uncached" ]; then + mise cache clear + fi + CARGO_PROFILE_RELEASE_DEBUG=true timeout -v 120 \ + mise x cargo:flamegraph -- \ + cargo flamegraph --verbose -o "flamegraphs/$1.svg" --title "$1" --notes "$error" -- $cmd \ + >/dev/null || true + errors+=("::error file=xtasks/test/perf,title=$1::$2") +} + +time_command() { + local cmd="$1" + local start_time + local end_time + local duration + shift + start_time=$(date +%s%N) + echo "running $cmd $*..." >&2 + timeout -v 10 $cmd "$@" >/dev/null || true + end_time=$(date +%s%N) + duration=$(echo "($end_time - $start_time) / 1000" | bc) + echo "$duration" +} + +benchmark() { + local name="$1" + local uncached_duration + local cached_duration + shift + mise cache clear -q + uncached_duration=$(time_command mise "$@") + cached_duration=$(time_command mise "$@") + benchmarks["$name-uncached"]=$uncached_duration + benchmarks["$name-cached"]=$cached_duration + + if [ -n "${MISE_ALT:-}" ]; then + mise cache clear + alt_uncached_duration=$(time_command "$MISE_ALT" "$@") + alt_cached_duration=$(time_command "$MISE_ALT" "$@") + alt_benchmarks["$name-uncached"]=$alt_uncached_duration + alt_benchmarks["$name-cached"]=$alt_cached_duration + fi + + check_maximum "$name" "$uncached_duration" "$cached_duration" "$@" + names+=("$name") +} + +check_maximum() { + local name="$1" + local uncached_duration="$2" + local cached_duration="$3" + local cmd="$4" + if [[ ${maximums["$name-uncached"]} -lt $uncached_duration ]]; then + benchmark_error "$name-uncached" "maximum for $name-uncached is ${maximums["$name-uncached"]}, got $uncached_duration" "$name" "$cmd" + elif [[ ${maximums["$name-cached"]} -lt $cached_duration ]]; then + benchmark_error "$name-cached" "maximum for $name-cached is ${maximums["$name-cached"]}, got $cached_duration" "$name" "$cmd" + elif [[ ${minimums["$name-uncached"]} -gt $uncached_duration ]]; then + benchmark_error "$name-uncached" "(yay!) minimum for $name-uncached is ${minimums["$name-uncached"]}, got $uncached_duration" "$name" "$cmd" + elif [[ ${minimums["$name-cached"]} -gt $cached_duration ]]; then + benchmark_error "$name-cached" "(yay!) minimum for $name-cached is ${minimums["$name-cached"]}, got $cached_duration" "$name" "$cmd" + fi +} + +benchmark install install +benchmark ls ls +benchmark bin-paths bin-paths +benchmark task-ls task ls + +if [ -n "${MISE_ALT:-}" ]; then + echo "| Command | Uncached mise | Cached mise | Uncached $MISE_ALT | Cached $MISE_ALT |" + echo "|------------|---------------|-------------|--------------------|------------------|" + for name in "${names[@]}"; do + printf "| %-10s | %11dms | %10dms | %15dms | %13dms |\n" \ + "$name" \ + "${benchmarks["$name-uncached"]}" \ + "${benchmarks["$name-cached"]}" \ + "${alt_benchmarks["$name-uncached"]}" \ + "${alt_benchmarks["$name-cached"]}" + done +else + echo "| Command | Uncached | Cached |" + echo "|------------|----------|----------|" + for name in "${names[@]}"; do + printf "| %-10s | %6dms | %6dms |\n" "$name" "${benchmarks["$name-uncached"]}" "${benchmarks["$name-cached"]}" + done +fi + +if [ -v GITHUB_STEP_SUMMARY ]; then + echo "## e2e/perf/test_many_tasks" >>comment.md + echo "" >>comment.md + echo "- NUM_TASKS: $num_tasks" >>comment.md + echo "- NUM_TOOLS: $num_tools" >>comment.md + echo "" >>comment.md + + if [ -n "${MISE_ALT:-}" ]; then + echo "| Command | Uncached mise | Cached mise | Uncached $MISE_ALT | Cached $MISE_ALT |" >>comment.md + echo "|------------|---------------|-------------|--------------------|------------------|" >>comment.md + for name in "${names[@]}"; do + printf "| %-10s | %11dms | %10dms | %15dms | %13dms |\n" \ + "$name" \ + "${benchmarks["$name-uncached"]}" \ + "${benchmarks["$name-cached"]}" \ + "${alt_benchmarks["$name-uncached"]}" \ + "${alt_benchmarks["$name-cached"]}" >>comment.md + done + else + echo "| Command | Uncached | Cached |" >>comment.md + echo "|------------|----------|----------|" >>comment.md + for name in "${names[@]}"; do + printf "| %-10s | %6dms | %6dms |\n" "$name" "${benchmarks["$name-uncached"]}" "${benchmarks["$name-cached"]}" >>comment.md + done + fi +fi + +if [ ${#errors[@]} -gt 0 ]; then + for error in "${errors[@]}"; do + echo "$error" >&2 + done + exit 1 +fi From 4779b9d16cae4e57ddd1c09d4677753a662156f4 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Sun, 25 May 2025 19:11:18 +0000 Subject: [PATCH 2/2] [autofix.ci] apply automated fixes --- tasks.md | 2 ++ xtasks/test/perf | 48 +++++++++++++++++++++++++++--------------------- 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/tasks.md b/tasks.md index c3a9eec0f7..8fa1f50ee1 100644 --- a/tasks.md +++ b/tasks.md @@ -240,6 +240,8 @@ run end-to-end tests ## `test:perf` +- Depends: test:build-perf-workspace + - **Usage**: `test:perf` ## `test:shuffle` diff --git a/xtasks/test/perf b/xtasks/test/perf index ada499f8c7..958ca4c429 100755 --- a/xtasks/test/perf +++ b/xtasks/test/perf @@ -16,14 +16,19 @@ declare -A alt_benchmarks names=() errors=() -recent_benchmarks["install-uncached"]=2900 -recent_benchmarks["install-cached"]=2400 -recent_benchmarks["ls-uncached"]=2360 -recent_benchmarks["ls-cached"]=2403 -recent_benchmarks["bin-paths-uncached"]=2400 -recent_benchmarks["bin-paths-cached"]=2300 -recent_benchmarks["task-ls-uncached"]=2400 -recent_benchmarks["task-ls-cached"]=2400 +if [ -v MISE_ALT ]; then + which mise + which "$MISE_ALT" +fi + +recent_benchmarks["install-uncached"]=678 +recent_benchmarks["install-cached"]=186 +recent_benchmarks["ls-uncached"]=372 +recent_benchmarks["ls-cached"]=57 +recent_benchmarks["bin-paths-uncached"]=490 +recent_benchmarks["bin-paths-cached"]=61 +recent_benchmarks["task-ls-uncached"]=10000 +recent_benchmarks["task-ls-cached"]=10000 for name in "${!recent_benchmarks[@]}"; do maximums["$name"]=$(("${recent_benchmarks["$name"]}" * 12 / 10)) @@ -58,7 +63,7 @@ time_command() { echo "running $cmd $*..." >&2 timeout -v 10 $cmd "$@" >/dev/null || true end_time=$(date +%s%N) - duration=$(echo "($end_time - $start_time) / 1000" | bc) + duration=$(((end_time - start_time) / 1000000)) echo "$duration" } @@ -67,7 +72,7 @@ benchmark() { local uncached_duration local cached_duration shift - mise cache clear -q + mise cache clear uncached_duration=$(time_command mise "$@") cached_duration=$(time_command mise "$@") benchmarks["$name-uncached"]=$uncached_duration @@ -101,6 +106,7 @@ check_maximum() { fi } +mise install benchmark install install benchmark ls ls benchmark bin-paths bin-paths @@ -126,28 +132,28 @@ else fi if [ -v GITHUB_STEP_SUMMARY ]; then - echo "## e2e/perf/test_many_tasks" >>comment.md - echo "" >>comment.md - echo "- NUM_TASKS: $num_tasks" >>comment.md - echo "- NUM_TOOLS: $num_tools" >>comment.md - echo "" >>comment.md + echo "## e2e/perf/test_many_tasks" >>../comment.md + echo "" >>../comment.md + echo "- NUM_TASKS: $num_tasks" >>../comment.md + echo "- NUM_TOOLS: $num_tools" >>../comment.md + echo "" >>../comment.md if [ -n "${MISE_ALT:-}" ]; then - echo "| Command | Uncached mise | Cached mise | Uncached $MISE_ALT | Cached $MISE_ALT |" >>comment.md - echo "|------------|---------------|-------------|--------------------|------------------|" >>comment.md + echo "| Command | Uncached mise | Cached mise | Uncached $MISE_ALT | Cached $MISE_ALT |" >>../comment.md + echo "|------------|---------------|-------------|--------------------|------------------|" >>../comment.md for name in "${names[@]}"; do printf "| %-10s | %11dms | %10dms | %15dms | %13dms |\n" \ "$name" \ "${benchmarks["$name-uncached"]}" \ "${benchmarks["$name-cached"]}" \ "${alt_benchmarks["$name-uncached"]}" \ - "${alt_benchmarks["$name-cached"]}" >>comment.md + "${alt_benchmarks["$name-cached"]}" >>../comment.md done else - echo "| Command | Uncached | Cached |" >>comment.md - echo "|------------|----------|----------|" >>comment.md + echo "| Command | Uncached | Cached |" >>../comment.md + echo "|------------|----------|----------|" >>../comment.md for name in "${names[@]}"; do - printf "| %-10s | %6dms | %6dms |\n" "$name" "${benchmarks["$name-uncached"]}" "${benchmarks["$name-cached"]}" >>comment.md + printf "| %-10s | %6dms | %6dms |\n" "$name" "${benchmarks["$name-uncached"]}" "${benchmarks["$name-cached"]}" >>../comment.md done fi fi