aboutsummaryrefslogtreecommitdiffhomepage
path: root/scripts/bench
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-08-23 11:48:01 +0900
committernsfisis <nsfisis@gmail.com>2026-08-23 12:00:07 +0900
commit2f07bd12c0c77e8985646a9f21a62a91311d82c0 (patch)
tree92f995eee73d58b38a850f8ba9b0ab09d8a59f3a /scripts/bench
parent88b5c4b9c5f7941292b918b6eefe54947c4a0d96 (diff)
downloadphp-shirabe-2f07bd12c0c77e8985646a9f21a62a91311d82c0.tar.gz
php-shirabe-2f07bd12c0c77e8985646a9f21a62a91311d82c0.tar.zst
php-shirabe-2f07bd12c0c77e8985646a9f21a62a91311d82c0.zip
perf(filesystem): delete directories without spawning rm -rf
Composer shells out to `rm -rf` because PHP has no recursive directory removal. Rust has one, and every package installed from a dist archive pays for a removal: the extraction pipeline drops its temporary directory once per package, and uninstalling a package removes its whole tree. The asynchronous path goes through `tokio::fs` rather than `std::fs`, so the walk runs on a blocking thread and the sibling installs the reactor is driving keep making progress, the way they did while the subprocess was working. Installing laravel/laravel (109 packages) from a warm cache drops from 3.85 to 3.19 CPU seconds. The removals run concurrently, so on an idle 16-core machine they never reach the critical path and wall time is unchanged at 1.65 s; pinned to two cores it falls from 2.33 s to 2.20 s. Pruning the 33 dev packages with `install --no-dev`, where whole package trees are removed rather than empty temporary directories, drops from 844 ms to 806 ms even on 16 cores. Windows keeps the `rmdir /S /Q` subprocess, and both platforms keep falling back to `remove_directory_php` when the fast path does not clear the directory. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'scripts/bench')
-rwxr-xr-xscripts/bench/install.sh57
1 files changed, 57 insertions, 0 deletions
diff --git a/scripts/bench/install.sh b/scripts/bench/install.sh
new file mode 100755
index 00000000..ef26d273
--- /dev/null
+++ b/scripts/bench/install.sh
@@ -0,0 +1,57 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+REPO_ROOT="$(git -C "$(dirname "${BASH_SOURCE[0]}")" rev-parse --show-toplevel)"
+BIN="$REPO_ROOT/target/release/shirabe"
+COMPOSER_BIN="$REPO_ROOT/composer/bin/composer"
+
+source "$REPO_ROOT/scripts/bench/lib.sh"
+
+PACKAGE="laravel/laravel"
+for arg in "$@"; do
+ case "$arg" in
+ --package=*) PACKAGE="${arg#*=}" ;;
+ *) echo "unknown option: $arg" >&2; exit 2 ;;
+ esac
+done
+
+if ! command -v hyperfine >/dev/null 2>&1; then
+ echo "hyperfine not found; install it first (e.g. 'cargo install hyperfine')" >&2
+ exit 1
+fi
+
+cargo build --manifest-path "$REPO_ROOT/Cargo.toml" --release --bin shirabe
+
+apply_http3_workaround
+
+OUTDIR="${TMPDIR:-/tmp}/shirabe-bench-install-$$"
+mkdir -p "$OUTDIR"
+cd "$OUTDIR"
+
+TARGET_DIR="project-under-test"
+PACKAGE_SLUG="${PACKAGE//\//-}"
+
+# Resolve the dependencies once. What is measured is installing them, not solving them.
+"$COMPOSER_BIN" create-project --no-plugins --no-scripts --no-audit --no-interaction \
+ "$PACKAGE" "$OUTDIR/template" >/dev/null
+
+PREPARE_SCRIPT="$OUTDIR/prepare.sh"
+cat > "$PREPARE_SCRIPT" <<EOF
+for dir in '$TARGET_DIR-shirabe' '$TARGET_DIR-composer'; do
+ rm -rf "\$dir"
+ mkdir -p "\$dir"
+ cp '$OUTDIR/template/composer.json' '$OUTDIR/template/composer.lock' "\$dir/"
+done
+EOF
+
+hyperfine \
+ --warmup 1 \
+ --prepare "bash '$PREPARE_SCRIPT'" \
+ --export-json "$OUTDIR/results-$PACKAGE_SLUG.json" \
+ --export-markdown "$OUTDIR/results-$PACKAGE_SLUG.md" \
+ --command-name Shirabe "RUST_BACKTRACE=1 '$BIN' install --no-plugins --no-scripts --no-interaction --working-dir='$TARGET_DIR-shirabe'" \
+ --command-name Composer "'$COMPOSER_BIN' install --no-plugins --no-scripts --no-interaction --working-dir='$TARGET_DIR-composer'"
+
+echo ">> results: $OUTDIR/results-$PACKAGE_SLUG.json" >&2
+echo ">> $OUTDIR/results-$PACKAGE_SLUG.md" >&2