at 24.05-pre 4.3 kB view raw
1#!/usr/bin/env nix-shell 2#!nix-shell -i bash -p sta jq bc nix -I nixpkgs=../.. 3# shellcheck disable=SC2016 4 5# Benchmarks lib.fileset 6# Run: 7# [nixpkgs]$ lib/fileset/benchmark.sh HEAD 8 9set -euo pipefail 10shopt -s inherit_errexit dotglob 11 12if (( $# == 0 )); then 13 echo "Usage: $0 HEAD" 14 echo "Benchmarks the current tree against the HEAD commit. Any git ref will work." 15 exit 1 16fi 17compareTo=$1 18 19SCRIPT_FILE=$(readlink -f "${BASH_SOURCE[0]}") 20SCRIPT_DIR=$(dirname "$SCRIPT_FILE") 21 22nixpkgs=$(cd "$SCRIPT_DIR/../.."; pwd) 23 24tmp="$(mktemp -d)" 25clean_up() { 26 rm -rf "$tmp" 27} 28trap clean_up EXIT SIGINT SIGTERM 29work="$tmp/work" 30mkdir "$work" 31cd "$work" 32 33declare -a stats=( 34 ".envs.elements" 35 ".envs.number" 36 ".gc.totalBytes" 37 ".list.concats" 38 ".list.elements" 39 ".nrFunctionCalls" 40 ".nrLookups" 41 ".nrOpUpdates" 42 ".nrPrimOpCalls" 43 ".nrThunks" 44 ".sets.elements" 45 ".sets.number" 46 ".symbols.number" 47 ".values.number" 48) 49 50runs=10 51 52run() { 53 # Empty the file 54 : > cpuTimes 55 56 for i in $(seq 0 "$runs"); do 57 NIX_PATH=nixpkgs=$1 NIX_SHOW_STATS=1 NIX_SHOW_STATS_PATH=$tmp/stats.json \ 58 nix-instantiate --eval --strict --show-trace >/dev/null \ 59 --expr 'with import <nixpkgs/lib>; with fileset; '"$2" 60 61 # Only measure the time after the first run, one is warmup 62 if (( i > 0 )); then 63 jq '.cpuTime' "$tmp/stats.json" >> cpuTimes 64 fi 65 done 66 67 # Compute mean and standard deviation 68 read -r mean sd < <(sta --mean --sd --brief <cpuTimes) 69 70 jq --argjson mean "$mean" --argjson sd "$sd" \ 71 '.cpuTimeMean = $mean | .cpuTimeSd = $sd' \ 72 "$tmp/stats.json" 73} 74 75bench() { 76 echo "Benchmarking expression $1" >&2 77 #echo "Running benchmark on index" >&2 78 run "$nixpkgs" "$1" > "$tmp/new.json" 79 ( 80 #echo "Checking out $compareTo" >&2 81 git -C "$nixpkgs" worktree add --quiet "$tmp/worktree" "$compareTo" 82 trap 'git -C "$nixpkgs" worktree remove "$tmp/worktree"' EXIT 83 #echo "Running benchmark on $compareTo" >&2 84 run "$tmp/worktree" "$1" > "$tmp/old.json" 85 ) 86 87 read -r oldMean oldSd newMean newSd percentageMean percentageSd < \ 88 <(jq -rn --slurpfile old "$tmp/old.json" --slurpfile new "$tmp/new.json" \ 89 ' $old[0].cpuTimeMean as $om 90 | $old[0].cpuTimeSd as $os 91 | $new[0].cpuTimeMean as $nm 92 | $new[0].cpuTimeSd as $ns 93 | (100 / $om * $nm) as $pm 94 # Copied from https://github.com/sharkdp/hyperfine/blob/b38d550b89b1dab85139eada01c91a60798db9cc/src/benchmark/relative_speed.rs#L46-L53 95 | ($pm * pow(pow($ns / $nm; 2) + pow($os / $om; 2); 0.5)) as $ps 96 | [ $om, $os, $nm, $ns, $pm, $ps ] 97 | @sh') 98 99 echo -e "Mean CPU time $newMean (σ = $newSd) for $runs runs is \e[0;33m$percentageMean% (σ = $percentageSd%)\e[0m of the old value $oldMean (σ = $oldSd)" >&2 100 101 different=0 102 for stat in "${stats[@]}"; do 103 oldValue=$(jq "$stat" "$tmp/old.json") 104 newValue=$(jq "$stat" "$tmp/new.json") 105 if (( oldValue != newValue )); then 106 percent=$(bc <<< "scale=100; result = 100/$oldValue*$newValue; scale=4; result / 1") 107 if (( oldValue < newValue )); then 108 echo -e "Statistic $stat ($newValue) is \e[0;31m$percent% (+$(( newValue - oldValue )))\e[0m of the old value $oldValue" >&2 109 else 110 echo -e "Statistic $stat ($newValue) is \e[0;32m$percent% (-$(( oldValue - newValue )))\e[0m of the old value $oldValue" >&2 111 fi 112 (( different++ )) || true 113 fi 114 done 115 echo "$different stats differ between the current tree and $compareTo" 116 echo "" 117} 118 119# Create a fairly populated tree 120touch f{0..5} 121mkdir d{0..5} 122mkdir e{0..5} 123touch d{0..5}/f{0..5} 124mkdir -p d{0..5}/d{0..5} 125mkdir -p e{0..5}/e{0..5} 126touch d{0..5}/d{0..5}/f{0..5} 127mkdir -p d{0..5}/d{0..5}/d{0..5} 128mkdir -p e{0..5}/e{0..5}/e{0..5} 129touch d{0..5}/d{0..5}/d{0..5}/f{0..5} 130mkdir -p d{0..5}/d{0..5}/d{0..5}/d{0..5} 131mkdir -p e{0..5}/e{0..5}/e{0..5}/e{0..5} 132touch d{0..5}/d{0..5}/d{0..5}/d{0..5}/f{0..5} 133 134bench 'toSource { root = ./.; fileset = ./.; }' 135 136rm -rf -- * 137 138touch {0..1000} 139bench 'toSource { root = ./.; fileset = unions (mapAttrsToList (name: value: ./. + "/${name}") (builtins.readDir ./.)); }' 140rm -rf -- *