#! /usr/bin/env bash # ASSUMPTIONS: # - the OPAM packages, specified by the user, are topologically sorted wrt. to the dependency relationship. # - all the variables below are set. set -e BENCH_DEBUG= r='\033[0m' # reset (all attributes off) b='\033[1m' # bold u='\033[4m' # underline nl=$'\n' bt='`' # backtick start_code_block='```' end_code_block='```' # Check that the required arguments are provided check_variable () { if [ ! -v "$1" ] then echo "Variable $1 should be set" exit 1 fi } #check_variable "BUILD_ID" #check_variable "BUILD_URL" #check_variable "JOB_NAME" #check_variable "JENKINS_URL" #check_variable "CI_JOB_URL" #check_variable "CI_REPOSITORY_URL" : "${coq_pr_number:=}" : "${coq_pr_comment_id:=}" : "${new_ocaml_version:=5.4.1}" : "${old_ocaml_version:=5.4.1}" : "${new_ocaml_flambda:=1}" : "${old_ocaml_flambda:=1}" : "${new_coq_repository:=${CI_REPOSITORY_URL:-.}}" : "${old_coq_repository:=${CI_REPOSITORY_URL:-.}}" : "${new_coq_opam_archive_git_uri:=https://github.com/coq/opam-coq-archive.git}" : "${old_coq_opam_archive_git_uri:=https://github.com/coq/opam-coq-archive.git}" : "${new_coq_opam_archive_git_branch:=master}" : "${old_coq_opam_archive_git_branch:=master}" : "${num_of_iterations:=1}" : "${timeout:=3h}" : "${coq_opam_packages:=rocq-stdlib rocq-bignums coq-hott coq-performance-tests-lite coq-engine-bench-lite rocq-elpi rocq-mathcomp-boot rocq-mathcomp-order rocq-mathcomp-ssreflect rocq-mathcomp-finite-group rocq-mathcomp-algebra rocq-mathcomp-solvable rocq-mathcomp-field rocq-mathcomp-group-representation coq-mathcomp-odd-order rocq-mathcomp-analysis coq-math-classes coq-corn coq-compcert rocq-equations rocq-metarocq-utils rocq-metarocq-common rocq-metarocq-template rocq-metarocq-pcuic rocq-metarocq-safechecker rocq-metarocq-erasure rocq-metarocq-translations coq-color coq-coqprime coq-coqutil coq-bedrock2 coq-rewriter coq-fiat-core coq-fiat-parsers coq-fiat-crypto-with-bedrock coq-unimath coq-coquelicot coq-iris-examples coq-fourcolor coq-rewriter-perf-SuperFast coq-vst coq-category-theory coq-neural-net-interp-computed-lite}" : "${coq_native:=}" : "${auto_overlays:=1}" # example: coq-hott.dev git+https://github.com/some-user/coq-hott#some-branch # (make sure to include the version for the opam package, note that just https won't work) : "${new_opam_override_urls:=}" : "${old_opam_override_urls:=}" if [ "$CI" ]; then : "${new_coq_commit:=$(git rev-parse HEAD^2)}" : "${old_coq_commit:=$(git merge-base HEAD^1 $new_coq_commit)}" else echo New coq commit: read -r new_coq_commit new_coq_commit=$(git rev-parse "$new_coq_commit") echo "new_coq_commit=$new_coq_commit" echo Old coq commit: read -r old_coq_commit old_coq_commit=$(git rev-parse "$old_coq_commit") echo "old_coq_commit=$old_coq_commit" fi new_ocaml_switch=ocaml-base-compiler.$new_ocaml_version old_ocaml_switch=ocaml-base-compiler.$old_ocaml_version if echo "$num_of_iterations" | grep '^[1-9][0-9]*$' 2> /dev/null > /dev/null; then : else echo echo "ERROR: num_of_iterations \"$num_of_iterations\" is not a positive integer." > /dev/stderr print_man_page_hint exit 1 fi bench_dirname="_bench" mkdir -p "${bench_dirname}" working_dir="$PWD/${bench_dirname}" log_dir=$working_dir/logs mkdir "$log_dir" export COQ_LOG_DIR=$log_dir # maps CI project (first arg of "overlay") # to space separated list of opam packages which use the project's url declare -A auto_overlay_map auto_overlay_map[stdlib]="rocq-stdlib" auto_overlay_map[elpi]="rocq-elpi" auto_overlay_map[equations]="rocq-equations" auto_overlay_map[metarocq]="rocq-metarocq-utils rocq-metarocq-common rocq-metarocq-template rocq-metarocq-pcuic rocq-metarocq-safechecker rocq-metarocq-erasure rocq-metarocq-translations" if [ "$auto_overlays" ]; then CI_PULL_REQUEST="${CI_COMMIT_REF_NAME#pr-}" overlay() { local project=$1 local ov_url=$2 local ov_ref=$3 local ov_prnumber=$4 local ov_prbranch=$5 : "${ov_prbranch:=$ov_ref}" if [ "$CI_PULL_REQUEST" = "$ov_prnumber" ]; then for package in ${auto_overlay_map[$project]}; do new_opam_override_urls="$package.dev git+$ov_url#$ov_ref $new_opam_override_urls" done fi } for overlay in dev/ci/user-overlays/*.sh; do # the directory can be empty if [ -e "$overlay" ]; then . "$overlay" fi done fi echo "DEBUG: ocaml -version = $(ocaml -version)" echo "DEBUG: working_dir = $working_dir" echo "DEBUG: new_ocaml_switch = $new_ocaml_switch" echo "DEBUG: new_coq_repository = $new_coq_repository" echo "DEBUG: new_coq_commit = $new_coq_commit" echo "DEBUG: new_coq_opam_archive_git_uri = $new_coq_opam_archive_git_uri" echo "DEBUG: new_coq_opam_archive_git_branch = $new_coq_opam_archive_git_branch" echo "DEBUG: old_ocaml_switch = $old_ocaml_switch" echo "DEBUG: old_coq_repository = $old_coq_repository" echo "DEBUG: old_coq_commit = $old_coq_commit" echo "DEBUG: old_coq_opam_archive_git_uri = $old_coq_opam_archive_git_uri" echo "DEBUG: old_coq_opam_archive_git_branch = $old_coq_opam_archive_git_branch" echo "DEBUG: num_of_iterations = $num_of_iterations" echo "DEBUG: coq_opam_packages = $coq_opam_packages" echo "DEBUG: new_opam_override_urls = $new_opam_override_urls" echo "DEBUG: old_opam_override_urls = $old_opam_override_urls" echo "DEBUG: coq_pr_number = $coq_pr_number" echo "DEBUG: coq_pr_comment_id = $coq_pr_comment_id" echo "DEBUG: coq_native = $coq_native" echo "DEBUG: auto_overlays = $auto_overlays" # We put local binaries such as opam in .bin and extend PATH BIN=$(pwd)/.bin mkdir "$BIN" wget https://github.com/ocaml/opam/releases/download/2.3.0/opam-2.3.0-x86_64-linux -O "$BIN"/opam chmod +x "$BIN"/opam export OPAMSKIPUPDATE=1 # stop opam from messing with our pin edits # generate per file info in test suite and coq_makefile devs export TIMED=1 export PATH="$BIN":$PATH echo "Global env info:" echo "----------------" echo "pwd: $PWD" echo "path: $PATH" echo "opam version: `opam --version`" number_of_processors=$(cat /proc/cpuinfo | grep '^processor *' | wc -l) program_name="$0" program_path=$(readlink -f "${program_name%/*}") coqbot_url_prefix="https://coqbot.herokuapp.com/pendulum/" # -------------------------------------------------------------------------------- # Some sanity checks of command-line arguments provided by the user that can be done right now. if which perf > /dev/null; then echo -n else echo > /dev/stderr echo "ERROR: \"perf\" program is not available." > /dev/stderr echo > /dev/stderr exit 1 fi if which curl > /dev/null; then : else echo > /dev/stderr echo "ERROR: \"curl\" program is not available." > /dev/stderr echo > /dev/stderr exit 1 fi if which du > /dev/null; then : else echo > /dev/stderr echo "ERROR: \"du\" program is not available." > /dev/stderr echo > /dev/stderr exit 1 fi if [ ! -e "$working_dir" ]; then echo > /dev/stderr echo "ERROR: \"$working_dir\" does not exist." > /dev/stderr echo > /dev/stderr exit 1 fi if [ ! -d "$working_dir" ]; then echo > /dev/stderr echo "ERROR: \"$working_dir\" is not a directory." > /dev/stderr echo > /dev/stderr exit 1 fi if [ ! -w "$working_dir" ]; then echo > /dev/stderr echo "ERROR: \"$working_dir\" is not writable." > /dev/stderr echo > /dev/stderr exit 1 fi # -------------------------------------------------------------------------------- # Tell coqbot to update the initial comment, if we know which one to update function coqbot_update_comment() { is_done="$1" comment_body="$2" uninstallable_packages="$3" if [ ! -z "${coq_pr_number}" ]; then comment_text="" artifact_text="" if [ -z "${is_done}" ]; then comment_text="in progress, " artifact_text="eventually " else comment_text="" artifact_text="" fi comment_text="Benchmarking ${comment_text}log available [here](${CI_JOB_URL}) ([raw log here](${CI_JOB_URL}/raw)), artifacts ${artifact_text}available for [download](${CI_JOB_URL}/artifacts/download) and [browsing](${CI_JOB_URL}/artifacts/browse)" if [ ! -z "${comment_body}" ]; then comment_text="${comment_text}${nl}${start_code_block}${nl}${comment_body}${nl}${end_code_block}" fi if [ ! -z "${uninstallable_packages}" ]; then comment_text="${comment_text}${nl}The following packages failed to install: ${uninstallable_packages}" fi comment_text="${comment_text}${nl}${nl}
Old Rocq version ${old_coq_commit}" comment_text="${comment_text}${nl}${nl}${start_code_block}${nl}$(git log -n 1 "${old_coq_commit}")${nl}${end_code_block}${nl}
" comment_text="${comment_text}${nl}${nl}
New Rocq version ${new_coq_commit}" comment_text="${comment_text}${nl}${nl}${start_code_block}${nl}$(git log -n 1 "${new_coq_commit}")${nl}${end_code_block}${nl}
" comment_text="${comment_text}${nl}${nl}[Diff: ${bt}${old_coq_commit}..${new_coq_commit}${bt}](https://github.com/rocq-prover/rocq/compare/${old_coq_commit}..${new_coq_commit})" # if there's a comment id, we update the comment while we're # in progress; otherwise, we wait until the end to post a new # comment if [ ! -z "${coq_pr_comment_id}" ]; then # Tell coqbot to update the in-progress comment curl -X POST --data-binary "${coq_pr_number}${nl}${coq_pr_comment_id}${nl}${comment_text}" "${coqbot_url_prefix}/update-comment" elif [ ! -z "${is_done}" ]; then # Tell coqbot to post a new comment that we're done benchmarking curl -X POST --data-binary "${coq_pr_number}${nl}${comment_text}" "${coqbot_url_prefix}/new-comment" fi if [ ! -z "${is_done}" ]; then # Tell coqbot to remove the `needs: benchmarking` label curl -X POST --data-binary "${coq_pr_number}" "${coqbot_url_prefix}/benchmarking-done" fi fi } # initial update to the comment, to say that we're in progress coqbot_update_comment "" "" "" # -------------------------------------------------------------------------------- zulip_post="" if [[ $ZULIP_BENCH_BOT ]]; then pr_full=$(git log -n 1 --pretty=%s) pr_full=${pr_full#"[CI merge] PR #"} pr_num=${pr_full%%:*} pr_msg=${pr_full#*:} zulip_header="Bench at $CI_JOB_URL Testing [$pr_msg](https://github.com/rocq-prover/rocq/pull/$pr_num) On packages $coq_opam_packages " # 24008 is the "github notifications" stream resp=$(curl -sSX POST https://rocq-prover.zulipchat.com/api/v1/messages \ -u "$ZULIP_BENCH_BOT" \ --data-urlencode type=stream \ --data-urlencode to='240008' \ --data-urlencode subject='Bench Notifications' \ --data-urlencode content="$zulip_header") zulip_post=$(echo "$resp" | jq .id) case "$zulip_post" in ''|*[!0-9]*) # not an int echo "Failed to post to zulip: $resp" zulip_post="" ;; esac fi zulip_edit() { if ! [[ $zulip_post ]]; then return; fi ending=$1 if [[ $rendered_results ]]; then msg="$zulip_header ~~~ $rendered_results ~~~ $ending " else msg="$zulip_header $ending " fi curl -sSX PATCH https://rocq-prover.zulipchat.com/api/v1/messages/"$zulip_post" \ -u "$ZULIP_BENCH_BOT" \ --data-urlencode content="$msg" >/dev/null || echo "Failed to edit zulip post" >&2 } zulip_autofail() { code=$? com=$BASH_COMMAND zulip_edit "Failed '$com' with exit code $code." } if [[ $zulip_post ]]; then trap zulip_autofail ERR; fi # see https://github.com/rocq-prover/rocq/pull/15807 if [ "$(ulimit -s)" != "unlimited" ]; then ulimit -S -s $((2 * $(ulimit -s))) fi # Clone the indicated git-repository. coq_dir="$working_dir/coq" git clone -q "$new_coq_repository" "$coq_dir" cd "$coq_dir" git remote rename origin new_coq_repository git remote add old_coq_repository "$old_coq_repository" git fetch -q "$old_coq_repository" git checkout -q $new_coq_commit coq_opam_version=dev # -------------------------------------------------------------------------------- new_opam_root="$working_dir/opam.NEW" old_opam_root="$working_dir/opam.OLD" # -------------------------------------------------------------------------------- old_coq_opam_archive_dir="$working_dir/old_coq_opam_archive" git clone -q --depth 1 -b "$old_coq_opam_archive_git_branch" "$old_coq_opam_archive_git_uri" "$old_coq_opam_archive_dir" new_coq_opam_archive_dir="$working_dir/new_coq_opam_archive" git clone -q --depth 1 -b "$new_coq_opam_archive_git_branch" "$new_coq_opam_archive_git_uri" "$new_coq_opam_archive_dir" initial_opam_packages="num zarith ocamlfind dune yojson camlzip" # Create an opam root and install Rocq # $1 = root_name {ex: NEW / OLD} # $2 = compiler version # $3 = directory of coq opam archive # $4 = override urls # $5 = use flambda if = "1" create_opam() { local RUNNER="$1" local OPAM_DIR="$working_dir/opam.$RUNNER" local OCAML_VER="$2" local OPAM_COQ_DIR="$3" local OPAM_OVERRIDE_URLS="$4" local USE_FLAMBDA="$5" export OPAMROOT="$OPAM_DIR" export COQ_RUNNER="$RUNNER" opam init --disable-sandboxing -qn -j "$number_of_processors" --bare # Allow beta compiler switches opam repo add -q --set-default beta https://github.com/ocaml/ocaml-beta-repository.git # Allow experimental compiler switches opam repo add -q --set-default ocaml-pr https://github.com/ejgallego/ocaml-pr-repository.git # Rest of default switches opam repo add -q --set-default iris-dev "https://github.com/rocq-iris/opam.git" local comp_packages if [[ $USE_FLAMBDA = 1 ]]; then comp_packages=--packages=ocaml-variants.${OCAML_VER}+options,ocaml-option-flambda else comp_packages=ocaml-base-compiler.$OCAML_VER fi opam switch create -qy -j "$number_of_processors" "ocaml-$RUNNER" $comp_packages eval $(opam env) if [ ! -z "$BENCH_DEBUG" ]; then opam config list; fi opam repo add -q --this-switch coq-core-dev "$OPAM_COQ_DIR/core-dev" # For rocq-stdlib opam repo add -q --this-switch coq-extra-dev "$OPAM_COQ_DIR/extra-dev" opam repo add -q --this-switch coq-released "$OPAM_COQ_DIR/released" local override_package local url_or_package for url_or_package in $OPAM_OVERRIDE_URLS; do if [ "$override_package" ]; then echo "Setting source url for $override_package to $url_or_package in $RUNNER" # NB opam editor start seems to ignore -y but works with > " opam pin add -nye "$override_package" - &2 echo "malformed opam_override_urls for $RUNNER (odd length)" zulip_edit "Bench failed: malformed opam_override_urls for $RUNNER (odd length)" exit 1 fi opam pin add -y dune 3.23.1 # 3.24 not compatible with many packages opam install -qy -j "$number_of_processors" $initial_opam_packages if [ ! -z "$BENCH_DEBUG" ]; then opam repo list; fi echo "wrap-build-commands: [\"$program_path/wrapper.sh\"]" >> "$OPAM_DIR/config" if [ ! -z "$coq_native" ]; then opam install coq-native rocq-native; fi } # Create an OPAM-root to which we will install the NEW version of Rocq. create_opam "NEW" "$new_ocaml_version" \ "$new_coq_opam_archive_dir" "$new_opam_override_urls" "$new_ocaml_flambda" cd "$coq_dir" git checkout -q "$new_coq_commit" new_coq_commit_long=$(git log --pretty=%H | head -n 1) echo "new_coq_commit = $new_coq_commit" echo "new_coq_commit_long = $new_coq_commit_long" # pre build needed helpers (needs an opam switch) dune build --root "$program_path/../.." -- dev/bench/render_results.exe dev/bench/render_line_results.exe rocq-devtools.install render_results=$(readlink -f "$program_path/../../_build/default/dev/bench/render_results.exe") render_line_results=$(readlink -f "$program_path/../../_build/default/dev/bench//render_line_results.exe") timelog2html=$(readlink -f "$program_path/../../_build/default/dev/bench/rocqtimelog2html.exe") # Create an OPAM-root to which we will install the OLD version of Rocq. create_opam "OLD" "$old_ocaml_version" \ "$old_coq_opam_archive_dir" "$old_opam_override_urls" "$old_ocaml_flambda" git checkout -q "$old_coq_commit" old_coq_commit_long=$(git log --pretty=%H | head -n 1) echo "old_coq_commit = $old_coq_commit" echo "old_coq_commit_long = $old_coq_commit_long" installable_coq_opam_packages="" zulip_edit "Opam setup, benching continues..." format_vosize() { old=$(stat -c%s $2) new=$(stat -c%s $3) diff=$((new - old)) diffpercent=$(((diff * 100) / $old)) echo "$1 $old $new $diff $diffpercent%" } # -------------------------------------------------------------------------------- # Measure the compilation times of the specified OPAM packages in both switches failed_packages= skipped_packages= # Generate per line timing info in devs that use coq_makefile export TIMING=1 export PROFILING=1 export ROCQ_PROFILE_COMPONENTS=command,parse_command,partac.perform # packages tied to the coq commit need to be pinned accordingly core_packages='rocq-runtime coq-core rocq-core rocqide-server' for coq_opam_package in $core_packages $coq_opam_packages; do export COQ_OPAM_PACKAGE=$coq_opam_package is_core= for core in $core_packages; do if [ "$coq_opam_package" = "$core" ]; then is_core=1; fi done if [[ $is_core ]]; then : not getting info from opam repo else if [ ! -z "$BENCH_DEBUG" ]; then opam list opam show $coq_opam_package || { failed_packages="$failed_packages $coq_opam_package (unknown package)" continue } else # cause to skip with error if unknown package opam show $coq_opam_package >/dev/null || { failed_packages="$failed_packages $coq_opam_package (unknown package)" continue } fi fi echo "coq_opam_package = $coq_opam_package" for RUNNER in NEW OLD; do export COQ_RUNNER=$RUNNER # perform measurements for the NEW/OLD commit (provided by the user) if [ $RUNNER = "NEW" ]; then export OPAMROOT="$new_opam_root" COQ_HASH=$new_coq_commit_long else export OPAMROOT="$old_opam_root" COQ_HASH=$old_coq_commit_long fi echo "Testing $RUNNER commit: $(date)" git checkout -q $COQ_HASH if { [ "$coq_opam_package" = rocq-core ] || [ "$coq_opam_package" = rocq-runtime ]; } \ && ! [ -e $coq_opam_package.opam ]; then echo "Skipping $coq_opam_package for $RUNNER" continue 2 fi if [ -e "$coq_opam_package.opam" ]; then local_opam_dir=. elif [ -e "stdlib/$coq_opam_package.opam" ]; then local_opam_dir=stdlib else local_opam_dir= fi if [[ $local_opam_dir ]]; then opam pin add -ny "$coq_opam_package.dev" -k path $local_opam_dir fi eval $(opam env) # If a given OPAM-package was already installed (as a # dependency of some OPAM-package that we have benchmarked # before), remove it. opam uninstall -q $coq_opam_package >/dev/null 2>&1 # '∗' is a unicode star used by opam for packags to be installed for dep in $(opam install --show-actions "$coq_opam_package" | grep -o '∗\s*[^ ]*' | sed 's/∗\s*//g'); do # show-actions will print transitive deps # so we don't need to look at the skipped_packages if echo "$failed_packages" | grep -q "^$dep "; then skipped_packages="$skipped_packages $coq_opam_package (dependency $dep failed)" continue 3 fi done opam install $coq_opam_package -v -b -j "$number_of_processors" --deps-only -y \ 3>$log_dir/$coq_opam_package.$RUNNER.opam_install.deps_only.stdout.log 1>&3 \ 4>$log_dir/$coq_opam_package.$RUNNER.opam_install.deps_only.stderr.log 2>&4 || { failed_packages="$failed_packages $coq_opam_package (dependency install failed in $RUNNER)" continue 2 } if [ ! -z "$BENCH_DEBUG" ]; then ls -l $working_dir; fi for iteration in $(seq $num_of_iterations); do export COQ_ITERATION=$iteration _RES=0 timeout "$timeout" opam install -y -v -b -j 1 $coq_opam_package \ 3>$log_dir/$coq_opam_package.$RUNNER.opam_install.$iteration.stdout.log 1>&3 \ 4>$log_dir/$coq_opam_package.$RUNNER.opam_install.$iteration.stderr.log 2>&4 || \ _RES=$? if [ $_RES = 0 ] && [ -e $log_dir/$coq_opam_package.$RUNNER.$iteration.ncoms ]; then echo $_RES > $log_dir/$coq_opam_package.$RUNNER.opam_install.$iteration.exit_status # "opam install" was successful. # Remove the benchmarked OPAM-package, unless this is the # very last iteration (we want to keep this OPAM-package # because other OPAM-packages we will benchmark later # might depend on it --- it would be a waste of time to # remove it now just to install it later) if [ $iteration != $num_of_iterations ]; then opam uninstall -q $coq_opam_package fi elif [ $_RES = 0 ]; then # package became a virtual package (no commands were run), act as though it failed echo 127 > $log_dir/$coq_opam_package.$RUNNER.opam_install.$iteration.exit_status failed_packages="$failed_packages $coq_opam_package (virtual package?) (in $RUNNER)" continue 3 else # "opam install" failed. echo $_RES > $log_dir/$coq_opam_package.$RUNNER.opam_install.$iteration.exit_status failed_packages="$failed_packages $coq_opam_package (in $RUNNER)" continue 3 fi done # avoid opam downgrading rocq if some package has weird constraints # there is no "opam switch add-invariant" so we do this sed instead, # cf https://github.com/ocaml/opam/issues/6269 if [ "$coq_opam_package" = "rocq-runtime" ]; then opam switch set-invariant --formula \ "$(opam switch invariant | sed 's/]/ \"'"$coq_opam_package"'\"]/')" fi done case $coq_opam_package in rocqide-server|coq) # don't render results for these ;; *) installable_coq_opam_packages="$installable_coq_opam_packages $coq_opam_package" esac # -------------------------------------------------------------- cat $log_dir/$coq_opam_package.$RUNNER.1.*.time || true cat $log_dir/$coq_opam_package.$RUNNER.1.*.perf || true # Print the intermediate results after we finish benchmarking each OPAM package if [ "$coq_opam_package" = "$(echo $coq_opam_packages | sed 's/ /\n/g' | tail -n 1)" ]; then # It does not make sense to print the intermediate results when # we finished bechmarking the very last OPAM package because the # next thing will do is that we will print the final results. # It would look lame to print the same table twice. : else echo "DEBUG: $render_results "$log_dir" $num_of_iterations 1 user_time_pdiff $installable_coq_opam_packages" rendered_results="$($render_results "$log_dir" $num_of_iterations 1 user_time_pdiff $installable_coq_opam_packages)" echo "${rendered_results}" # update the comment coqbot_update_comment "" "${rendered_results}" "" msg="Benching continues ($coq_opam_package done)..." if [ -n "$failed_packages" ]; then msg="$msg Failed: $failed_packages $skipped_packages" fi zulip_edit "$msg" fi # N.B. Not all packages end in .dev, e.g., coq-lambda-rust uses .dev.timestamp. # So we use a wildcard to catch such packages. coq_opam_package_nover=${coq_opam_package%%.*} new_base_path=$(echo "$new_opam_root/ocaml-NEW/.opam-switch/build/$coq_opam_package_nover".*/) old_base_path=$(echo "$old_opam_root/ocaml-OLD/.opam-switch/build/$coq_opam_package_nover".*/) # Generate per-file comparison for iteration in $(seq $num_of_iterations); do # opam logs prefix the printing with "- " so remove that # remove the base path for nicer printing and so the script can identify common files "$program_path"/../../tools/make-both-time-files.py --real \ <(sed -e 's/^- //' -e "s:$new_base_path::" "$log_dir/$coq_opam_package.NEW.opam_install.$iteration.stdout.log") \ <(sed -e 's/^- //' -e "s:$old_base_path::" "$log_dir/$coq_opam_package.OLD.opam_install.$iteration.stdout.log") \ > "$log_dir/$coq_opam_package.BOTH.perfile_timings.$iteration.log" done # Generate HTML report for LAST run for vo in $(cd $new_base_path/; find . -name '*.vo'); do if [ -e $old_base_path/$vo ]; then format_vosize "$coq_opam_package/$vo" "$old_base_path/$vo" "$new_base_path/$vo" >> "$log_dir/vosize.log" fi # use profile if it exists, otherwise timing # NB: timelog2html sometimes randomly fails old_data= new_data= if [ -e "$old_base_path/$vo.prof.json.gz" ]; then old_data="$old_base_path/$vo.prof.json.gz" elif [ -e "$old_base_path/${vo%%o}.timing" ]; then old_data="$old_base_path/${vo%%o}.timing" fi if [ -e "$new_base_path/$vo.prof.json.gz" ]; then new_data="$new_base_path/$vo.prof.json.gz" elif [ -e "$new_base_path/${vo%%o}.timing" ]; then new_data="$new_base_path/${vo%%o}.timing" fi if [ "$old_data" ] && [ "$new_data" ]; then mkdir -p "$working_dir/html/$coq_opam_package/$(dirname "$vo")/" $timelog2html \ "-o" "$working_dir/html/$coq_opam_package/${vo%%o}.html" \ "-raw-o" "$working_dir/html/$coq_opam_package/${vo%%o}.rocq-timediff" \ "$new_base_path/${vo%%o}" \ "$old_data" \ "$new_data" \ || echo "Failed (code $?): timelog2html for $vo on $old_data $new_data" fi done done # postprocess full vosize log (sort by % change then absolute change) sort -r -n -k 5 -k 4 "$log_dir/vosize.log" > "$log_dir/vosize.sorted.log" mv "$log_dir/vosize.sorted.log" "$log_dir/vosize.log" # Since we do not upload all files, store a list of the files # available so that if we at some point want to tweak which files we # upload, we'll know which ones are available for upload du -ha "$working_dir" > "$working_dir/files.listing" # The following directories in $working_dir are no longer used: # # - coq, opam.OLD, opam.NEW # Measured data for each `$coq_opam_package`, `$iteration`, `status \in {NEW,OLD}`: # # - $working_dir/$coq_opam_package.$status.$iteration.time # => output of /usr/bin/time --format="%U" ... # # - $working_dir/$coq_opam_package.NEW.$iteration.perf # => output of perf stat -e instructions:u,cycles:u ... # # The next script processes all these files and prints results in a table. # Generate per-file comparison for everything at once new_base_path=$new_opam_root/ocaml-NEW/.opam-switch/build/ old_base_path=$old_opam_root/ocaml-OLD/.opam-switch/build/ for iteration in $(seq $num_of_iterations); do "$program_path"/../../tools/make-both-time-files.py --real \ <(sed -e 's/^- //' -e "s:$new_base_path::" "$log_dir/"*".NEW.opam_install.$iteration.stdout.log") \ <(sed -e 's/^- //' -e "s:$old_base_path::" "$log_dir/"*".OLD.opam_install.$iteration.stdout.log") \ > "$log_dir/ALL.BOTH.perfile_timings.$iteration.log" done # timings data timings=$working_dir/timings mkdir -p $timings # Print line by line slow downs and speed ups if [ -d "$working_dir/html" ]; then # might not exist if all jobs failed cd "$working_dir/html" $render_line_results # Move line timing files to timings folder (they will become artifacts) mv fast_table slow_table timings_table $timings # html tables don't get generated if the bench is run locally ie without CI variables for f in fast_table.html slow_table.html timings_table.html; do if [ -f "$f" ]; then mv "$f" $timings; fi done fi echo "INFO: workspace = ${CI_JOB_URL:-.}/artifacts/browse/${bench_dirname}" # Print the final results. if [ -z "$installable_coq_opam_packages" ]; then # Tell the user that none of the OPAM-package(s) the user provided # /are installable. printf "\n\nINFO: failed to install: $coq_opam_packages" coqbot_update_comment "done" "" "$coq_opam_packages" exit 1 fi echo "DEBUG: $render_results $log_dir $num_of_iterations 1 user_time_pdiff $installable_coq_opam_packages" rendered_results="$($render_results "$log_dir" $num_of_iterations 1 user_time_pdiff $installable_coq_opam_packages)" echo "${rendered_results}" echo "${rendered_results}" > $timings/bench_summary echo "INFO: per line timing: ${CI_JOB_URL:-.}/artifacts/browse/${bench_dirname}/html/" cd "$coq_dir" echo INFO: Old Rocq version git log -n 1 "$old_coq_commit" echo INFO: New Rocq version git log -n 1 "$new_coq_commit" if [ -n "$failed_packages" ]; then not_installable_coq_opam_packages=$failed_packages if [ -n "$skipped_packages" ]; then not_installable_coq_opam_packages="$not_installable_coq_opam_packages $skipped_packages" fi else # in case the failed package detection is bugged not_installable_coq_opam_packages=$(comm -23 <(echo $coq_opam_packages | sed 's/ /\n/g' | sort | uniq) <(echo $installable_coq_opam_packages | sed 's/ /\n/g' | sort | uniq) | sed 's/\t//g') fi coqbot_update_comment "done" "${rendered_results}" "${not_installable_coq_opam_packages}" touch $timings/bench_failures if [ -n "$not_installable_coq_opam_packages" ]; then # Tell the user that some of the provided OPAM-package(s) # is/are not installable. printf '\n\nINFO: failed to install %s\n' "$not_installable_coq_opam_packages" | tee $timings/bench_failures zulip_edit "Bench complete, failed to install packages: $not_installable_coq_opam_packages" exit 1 fi zulip_edit "Bench complete: all packages successfully installed."