diff --git a/scripts/run-setc-bench.sh b/scripts/run-setc-bench.sh new file mode 100755 index 0000000..9ed224a --- /dev/null +++ b/scripts/run-setc-bench.sh @@ -0,0 +1,312 @@ +#!/bin/bash +set -euo pipefail +export LC_ALL=C + +# Настройки. +SETC_DIR="$HOME/setc" +WORK_ROOT="$HOME/setc-bench" +RESULT_DIR="$HOME/res" +RUNS=3 +CPU=0 +RESET_WORK=1 # 0 — продолжить готовые сборки, 1 — начать всё заново. +PACKAGER='krosh ' +APT_SOURCE=/etc/apt/sources.list.d/alt.list +APT_GET=/usr/lib/apt/apt-get +RPM_BUILD_GIT=https://git.altlinux.org/gears/r/rpm-build.git +RPM_GIT=https://git.altlinux.org/gears/r/rpm.git + +fail() +{ + printf 'error: %s\n' "$*" >&2 + exit 1 +} + +prepare_spec() +{ + local spec=$1 suffix=$2 version release new_release date + + version=$(sed -n 's/^Version:[[:space:]]*//p' "$spec" | sed -n '1p') + release=$(sed -n 's/^Release:[[:space:]]*//p' "$spec" | sed -n '1p') + [[ -n $version && -n $release ]] || fail "cannot read Version/Release from $spec" + + new_release="$release.$suffix" + sed -i "0,/^Release:[[:space:]]*$release$/s//Release: $new_release/" "$spec" + + date=$(date '+%a %b %d %Y') + sed -i "/^%changelog/a\\ +* $date $PACKAGER $version-$new_release\\ +- Local set.c benchmark build.\\ +" "$spec" +} + +apt_options() +{ + local root=$1 + + APT_OPTIONS=( + -o 'Dir::Etc::main=-' + -o 'Dir::Etc::parts=-' + -o "Dir::Etc::sourcelist=$COMMON/sources.list" + -o "Dir::Etc::sourceparts=$COMMON/sources.list.d" + -o 'Dir::Etc::preferences=-' + -o 'Dir::Etc::preferencesparts=-' + -o "Dir::State::lists=$COMMON/lists/" + -o "Dir::State::status=$COMMON/status" + -o "Dir::Cache=$COMMON/cache/" + -o "Dir::Cache::archives=$COMMON/cache/archives" + -o "Dir::Cache::pkgcache=$COMMON/cache/pkgcache.bin" + -o "Dir::Cache::srcpkgcache=$COMMON/cache/srcpkgcache.bin" + -o "RPM::RootDir=$root" + ) +} + +operation_args() +{ + case $1 in + check) ARGS=(-s check) ;; + autoremove) ARGS=(-s autoremove) ;; + install-rpm-build) ARGS=(-s install rpm-build) ;; + install-openuds-server) ARGS=(-s install openuds-server) ;; + install-password-store) ARGS=(-s install password-store) ;; + upgrade) ARGS=(-o APT::Get::EnableUpgrade=true -s upgrade) ;; + dist-upgrade) ARGS=(-s dist-upgrade) ;; + *) fail "unknown operation: $1" ;; + esac +} + +operation_label() +{ + case $1 in + check) printf '%s' '-s check' ;; + autoremove) printf '%s' '-s autoremove' ;; + install-rpm-build) printf '%s' '-s install rpm-build' ;; + install-openuds-server) printf '%s' '-s install openuds-server' ;; + install-password-store) printf '%s' '-s install password-store' ;; + upgrade) printf '%s' '--enable-upgrade -s upgrade' ;; + dist-upgrade) printf '%s' '-s dist-upgrade' ;; + esac +} + +run_once() +{ + local operation=$1 variant=$2 run=$3 start end status + local libdir="$variant/lib/usr/lib64" + local root="$COMMON/root" + local raw="$variant/raw" + + operation_args "$operation" + apt_options "$root" + + start=$(date +%s%N) + if env LC_ALL=C APT_CONFIG="$COMMON/apt.conf" \ + LD_LIBRARY_PATH="$libdir" \ + taskset -c "$CPU" "$APT_GET" -q \ + "${APT_OPTIONS[@]}" "${ARGS[@]}" \ + >"$raw/$operation.$run.stdout" \ + 2>"$raw/$operation.$run.stderr"; then + status=0 + else + status=$? + fi + end=$(date +%s%N) + + RUN_TIME=$(awk -v start="$start" -v end="$end" \ + 'BEGIN { printf "%.6f", (end - start) / 1000000000 }') + RUN_STATUS=$status +} + +benchmark_variant() +{ + local variant=$1 result=$2 operation run average label status_text + local -a times statuses + local -a operations=( + check + autoremove + install-rpm-build + install-openuds-server + install-password-store + upgrade + dist-upgrade + ) + + mkdir -p "$variant/raw" + printf 'command\taverage_seconds\trun1_seconds\trun2_seconds\trun3_seconds\texit_status\n' \ + >"$result" + + for operation in "${operations[@]}"; do + times=() + statuses=() + + for ((run = 1; run <= RUNS; ++run)); do + run_once "$operation" "$variant" "$run" + times+=("$RUN_TIME") + statuses+=("$RUN_STATUS") + printf '%s: run %d/%d: %ss, status=%s\n' \ + "$operation" "$run" "$RUNS" "$RUN_TIME" "$RUN_STATUS" + done + + average=$(printf '%s\n' "${times[@]}" | \ + awk '{ total += $1 } END { printf "%.6f", total / NR }') + + status_text=${statuses[0]} + for status in "${statuses[@]}"; do + if [[ $status != "$status_text" ]]; then + status_text=$(IFS=,; printf 'mixed:%s' "${statuses[*]}") + break + fi + done + + label=$(operation_label "$operation") + printf '%s\t%s\t%s\t%s\t%s\t%s\n' \ + "$label" "$average" \ + "${times[0]}" "${times[1]}" "${times[2]}" "$status_text" \ + >>"$result" + done +} + +for command in git gear-hsh hsh rpm rpmquery rpm2cpio cpio apt-get apt-cache \ + taskset awk sed date sha256sum ldd; do + command -v "$command" >/dev/null || fail "required command not found: $command" +done +[[ -x $APT_GET ]] || fail "not found: $APT_GET" +[[ $RUNS -eq 3 ]] || fail 'RUNS must stay equal to 3 for the TSV format below' + +shopt -s nullglob +setc_files=("$SETC_DIR"/*.c) +((${#setc_files[@]} > 0)) || fail "no .c files in $SETC_DIR" + +if ((RESET_WORK)); then + # После прерванной сборки часть chroot принадлежит hasher-псевдопользователю; + # обычный rm её не удалит. + old_hashers=("$WORK_ROOT"/variants/*/hasher) + for old_hasher in "${old_hashers[@]}"; do + [[ -d $old_hasher ]] || continue + hsh --cleanup-only --workdir="$old_hasher" >/dev/null 2>&1 || true + done + rm -rf "$WORK_ROOT" +fi +mkdir -p "$WORK_ROOT/src" "$WORK_ROOT/variants" "$RESULT_DIR" +COMMON="$WORK_ROOT/common" +if [[ ! -s $COMMON/cache/pkgcache.bin ]]; then + rm -rf "$COMMON" + mkdir -p \ + "$COMMON/lists/partial" \ + "$COMMON/cache/archives/partial" \ + "$COMMON/sources.list.d" \ + "$COMMON/root/var/lib/rpm" + : >"$COMMON/apt.conf" + : >"$COMMON/status" + + # Один официальный p11 snapshot без hasher/stuff. Он создаётся до сборок + # и затем одинаково используется всеми вариантами. + awk '$1 == "rpm" && /p11\/branch/ && $0 !~ /(hasher|stuff)/ { print }' \ + "$APT_SOURCE" >"$COMMON/sources.list" + [[ -s $COMMON/sources.list ]] || fail "no p11 sources in $APT_SOURCE" + + cp -a /var/lib/rpm/. "$COMMON/root/var/lib/rpm/" + apt_options "$COMMON/root" + APT_CONFIG="$COMMON/apt.conf" apt-get -qq "${APT_OPTIONS[@]}" update + APT_CONFIG="$COMMON/apt.conf" apt-cache -q "${APT_OPTIONS[@]}" gencaches >/dev/null + sha256sum "$COMMON/cache/pkgcache.bin" "$COMMON/cache/srcpkgcache.bin" \ + >"$COMMON/cache.sha256" +fi + +# Чистые p11 gear-деревья клонируются один раз. +if [[ ! -d $WORK_ROOT/src/rpm-build-base/.git ]]; then + git clone --branch p11 --single-branch "$RPM_BUILD_GIT" \ + "$WORK_ROOT/src/rpm-build-base" +fi +if [[ ! -d $WORK_ROOT/src/rpm-base/.git ]]; then + git clone --branch p11 --single-branch "$RPM_GIT" \ + "$WORK_ROOT/src/rpm-base" +fi + +index=0 +for setc in "${setc_files[@]}"; do + index=$((index + 1)) + id=$(printf 'v%03d' "$index") + suffix="setc$id" + filename=$(basename "$setc") + result_name=${filename%.c}.tsv + variant="$WORK_ROOT/variants/$id" + source="$variant/src" + hasher="$variant/hasher" + repo="$hasher/repo/x86_64/RPMS.hasher" + + printf '\n===== %s: %s =====\n' "$id" "$filename" + mkdir -p "$source" "$hasher" "$variant/logs" "$variant/lib" + + if [[ ! -d $source/rpm-build/.git ]]; then + git clone --local "$WORK_ROOT/src/rpm-build-base" "$source/rpm-build" + git clone --local "$WORK_ROOT/src/rpm-base" "$source/rpm" + + # По условию один и тот же входной файл кладётся как lib/set.c в оба пакета. + cp "$setc" "$source/rpm-build/lib/set.c" + cp "$setc" "$source/rpm/lib/set.c" + + prepare_spec "$source/rpm-build/rpm-4_0.spec" "$suffix" + prepare_spec "$source/rpm/alt/rpm.spec" "$suffix" + else + cmp -s "$setc" "$source/rpm-build/lib/set.c" && \ + cmp -s "$setc" "$source/rpm/lib/set.c" || \ + fail "$filename changed; set RESET_WORK=1 and restart" + fi + + rpm_build_rpms=("$repo"/rpm-build-[0-9]*".$suffix".x86_64.rpm) + if ((${#rpm_build_rpms[@]} == 0)); then + ( + cd "$source/rpm-build" + gear-hsh \ + --commit \ + --with-stuff \ + --packager="$PACKAGER" \ + --no-sisyphus-check=changelog,packager,gpg \ + --mountpoints=/proc \ + --workdir="$hasher" \ + --target=x86_64 + ) 2>&1 | tee "$variant/logs/rpm-build.log" + else + printf 'rpm-build already built: %s\n' "${rpm_build_rpms[0]}" + fi + + librpm_rpms=("$repo"/librpm7-[0-9]*".$suffix".x86_64.rpm) + if ((${#librpm_rpms[@]} == 0)); then + ( + cd "$source/rpm" + gear-hsh \ + --commit \ + --with-stuff \ + --packager="$PACKAGER" \ + --no-sisyphus-check=changelog,packager,gpg \ + --mountpoints=/proc \ + --workdir="$hasher" \ + --target=x86_64 + ) 2>&1 | tee "$variant/logs/rpm.log" + else + printf 'rpm/librpm7 already built: %s\n' "${librpm_rpms[0]}" + fi + + # [0-9]* исключает отдельный пакет librpm7-debuginfo. + librpm_rpms=("$repo"/librpm7-[0-9]*".$suffix".x86_64.rpm) + ((${#librpm_rpms[@]} == 1)) || \ + fail "expected one librpm7 package for $filename, got ${#librpm_rpms[@]}" + + rm -rf "$variant/lib" + mkdir -p "$variant/lib" + ( + cd "$variant/lib" + rpm2cpio "${librpm_rpms[0]}" | cpio -idm --quiet + ) + + libdir="$variant/lib/usr/lib64" + [[ -e $libdir/librpm.so.7 && -e $libdir/librpmio.so.7 ]] || \ + fail "librpm libraries were not extracted for $filename" + LD_LIBRARY_PATH="$libdir" ldd "$APT_GET" | \ + grep -F "$libdir/librpm.so.7" >/dev/null || \ + fail "apt-get does not load the built librpm for $filename" + + benchmark_variant "$variant" "$RESULT_DIR/$result_name" +done + +printf '\nResults:\n' +printf ' %s\n' "$RESULT_DIR"/*.tsv