xref: /libCEED/benchmarks/benchmark.sh (revision d4cc18453651bd0f94c1a2e078b2646a92dafdcc)
1#!/bin/bash
2
3# Copyright (c) 2017-2026, Lawrence Livermore National Security, LLC and other CEED contributors.
4# All Rights Reserved. See the top-level LICENSE and NOTICE files for details.
5#
6# SPDX-License-Identifier: BSD-2-Clause
7#
8# This file is part of CEED:  http://github.com/ceed
9
10this_file="${BASH_SOURCE[0]}"
11if [[ "${#BASH_ARGV[@]}" -ne "$#" ]]; then
12   script_is_sourced="yes"
13   exit_cmd=return
14else
15   script_is_sourced=""
16   exit_cmd=exit
17fi
18test_file=""
19backend_list="/cpu/self"
20bp_list="bp1 bp3"
21run=""
22num_proc_run=${num_proc_run:-""}
23num_proc_node=${num_proc_node:-""}
24dry_run="" # empty string = NO
25start_shell=""
26verbose=""
27cur_dir="$PWD"
28
29mpiexec="mpirun"
30mpiexec_np="-np"
31mpiexec_opts=""
32mpiexec_post_opts=""
33profiler=""
34
35function abspath()
36{
37   local outvar="$1" path="$2" cur_dir="$PWD"
38   cd "$path" && path="$PWD" && cd "$cur_dir" && eval "$outvar=\"$path\""
39}
40
41abspath root_dir ".." || $exit_cmd 1
42build_root="$root_dir/build"
43
44if [[ -t 1 ]]; then
45   # ANSI color codes
46   none=$'\E[0m'
47   red=$'\E[0;31m'
48   green=$'\E[0;32m'
49   yellow=$'\E[0;33m'
50   blue=$'\E[0;34m'
51   bblue=$'\E[1;34m'
52   magenta=$'\E[0;35m'
53   cyan=$'\E[0;36m'
54   clear="$(tput sgr0)"
55fi
56
57help_msg="
58$this_file [options]
59
60Options:
61   -h|--help                print this usage information and exit
62   -b|--bp \"list\"           choose the benchmark problems to run
63   -c|--ceed \"list\"         choose the libCEED backends to benchmark
64   -r|--run <name>          run the tests in the script <name>
65   -n|--num-proc \"list\"     total number of MPI tasks to use in the tests
66   -p|--proc-node \"list\"    number of MPI tasks per node to use in the tests
67   -d|--dry-run             show (but do not run) the commands for the tests
68   -s|--shell               execute bash shell commands before running the test
69   -v|--verbose             print additional messages
70   -x                       enable script tracing with 'set -x'
71   var=value                define shell variables; evaluated with 'eval'
72
73This script builds and runs a set of benchmarks for a list of specified
74backends.
75
76Example usage:
77  $this_file  --run petsc-bpsraw.sh
78  $this_file  --run petsc-bps.sh
79"
80
81function build_examples()
82{
83   for example; do
84      # We require the examples to be already built because we do not know what
85      # options to use when building the library + examples.
86      if [ ! -e $build_root/$example ]; then
87         echo "Error: example is not built: $example"
88         return 1
89      fi
90   done
91}
92
93function compose_mpi_run_command()
94{
95   mpi_run="${mpiexec:-mpirun} ${mpiexec_opts}"
96   mpi_run+=" ${mpiexec_np:--np} ${num_proc_run} ${mpiexec_post_opts}"
97   if [[ -n "$profiler" ]]; then
98      mpi_run+=" $profiler"
99   fi
100}
101
102function quoted_echo()
103{
104   local arg= string=
105   for arg; do
106      if [[ -z "${arg##* *}" ]]; then
107         string+=" \"${arg//\"/\\\"}\""
108      else
109         string+=" $arg"
110      fi
111   done
112   printf "%s\n" "${string# }"
113}
114
115function set_num_nodes()
116{
117   if [[ -n "$num_proc_node" ]]; then
118      ((num_proc_run % num_proc_node != 0)) && {
119         echo "The total number of tasks ($num_proc_run) must be a multiple of"
120         echo "the number of tasks per node ($num_proc_node). Stop."
121         return 1
122      }
123      ((num_nodes = num_proc_run / num_proc_node))
124   else
125      num_proc_node="unknown number of"
126      num_nodes=""
127   fi
128   echo "Running the tests using a total of $num_proc_run MPI tasks ..." | tee -a $output_file
129   echo "... with $num_proc_node tasks per node ..." | tee -a $output_file
130   echo | tee -a $output_file
131}
132
133### Process command line parameters
134
135while [ $# -gt 0 ]; do
136
137case "$1" in
138   -h|--help)
139      # Echo usage information
140      echo "$help_msg"
141      $exit_cmd
142      ;;
143   -b|--bp)
144      shift
145      [ $# -gt 0 ] || {
146      echo "Missing \"list\" in --bp \"list\""; $exit_cmd 1; }
147      bp_list="$1"
148      ;;
149   -c|--ceed)
150      shift
151      [ $# -gt 0 ] || {
152      echo "Missing \"list\" in --ceed \"list\""; $exit_cmd 1; }
153      backend_list="$1"
154      ;;
155   -r|--run)
156      run=on
157      shift
158      [ $# -gt 0 ] || { echo "Missing <name> in --run <name>"; $exit_cmd 1; }
159      test_file="$1"
160      [[ -r "$test_file" ]] || {
161         echo "Test script not found: '$1'"; $exit_cmd 1
162      }
163      ;;
164   -n|--num-proc)
165      shift
166      [ $# -gt 0 ] || {
167      echo "Missing \"list\" in --num-proc \"list\""; $exit_cmd 1; }
168      num_proc_run="$1"
169      ;;
170   -p|--proc-node)
171      shift
172      [ $# -gt 0 ] || {
173      echo "Missing \"list\" in --proc-node \"list\""; $exit_cmd 1; }
174      num_proc_node="$1"
175      ;;
176   -d|--dry-run)
177      dry_run="quoted_echo"
178      ;;
179   -s|--shell)
180      start_shell="yes"
181      ;;
182   -v|--verbose)
183      verbose="yes"
184      ;;
185   -x)
186      set -x
187      ;;
188   *=*)
189      eval "$1" || { echo "Error evaluating argument: $1"; $exit_cmd 1; }
190      ;;
191   *)
192      echo "Unknown option: '$1'"
193      $exit_cmd 1
194      ;;
195esac
196
197shift
198done # while ...
199# Done processing command line parameters
200
201num_proc_list=(${num_proc_run:-4})
202num_proc_list_size=${#num_proc_list[@]}
203num_proc_node_list=(${num_proc_node:-4})
204num_proc_node_list_size=${#num_proc_node_list[@]}
205(( num_proc_list_size != num_proc_node_list_size )) && {
206   echo "
207The size of the number-of-processors list (option --num-proc) must be the same
208as the size of the number-of-processors-per-node list (option --proc-node)."
209   echo
210   $exit_cmd 1
211}
212
213### Loop over BPs
214
215for bp in $bp_list; do
216
217### Loop over backends
218
219for backend in $backend_list; do
220(  ## Run each backend in its own environment
221
222### Setup output
223### Test name
224cd "$cur_dir"
225abspath test_dir "$(dirname "$test_file")" || $exit_cmd 1
226test_basename="$(basename "$test_file")"
227test_file="${test_dir}/${test_basename}"
228### Backend name
229short_backend=${backend//[\/]}
230### Output file
231output_file="${test_file%%.*}-$bp-$short_backend-output.txt"
232rm -rf output_file
233
234### Setup the environment based on $backend
235
236echo
237echo "Using backend $backend ..." | tee $output_file
238
239### Run the tests (building and running $test_file)
240
241[ -n "$run" ] && {
242
243[[ "$verbose" = "yes" ]] && {
244   echo "Test problem file, $test_basename:" | tee -a $output_file
245   echo "------------------------------------------------" | tee -a $output_file
246   cat $test_file | tee -a $output_file
247   echo "------------------------------------------------" | tee -a $output_file
248   echo | tee -a $output_file
249}
250
251test_exe_dir="$build_root"
252
253trap 'printf "\nScript interrupted.\n"; '$exit_cmd' 33' INT
254
255## Source the test script file.
256echo "Reading test file: $test_file" | tee -a $output_file
257echo | tee -a $output_file
258test_required_examples=""
259. "$test_file" || $exit_cmd 1
260
261## Build files required by the test
262echo "Example(s) required by the test: $test_required_examples" | tee -a $output_file
263build_examples $test_required_examples || $exit_cmd 1
264echo | tee -a $output_file
265
266## Loop over the number-of-processors list.
267for (( num_proc_idx = 0; num_proc_idx < num_proc_list_size; num_proc_idx++ ))
268do
269
270num_proc_run="${num_proc_list[$num_proc_idx]}"
271num_proc_node="${num_proc_node_list[$num_proc_idx]}"
272
273set_num_nodes || $exit_cmd 1
274compose_mpi_run_command
275
276if [[ "$start_shell" = "yes" ]]; then
277   if [[ ! -t 1 ]]; then
278      echo "Standard output is not a terminal. Stop." | tee -a $output_file
279      $exit_cmd 1
280   fi
281   echo "Reading shell commands, type 'c' to continue, 'exit' to stop ..." | tee -a $output_file
282   echo | tee -a $output_file
283   cd "$cur_dir"
284   set -o emacs
285   PS1='$ '
286   [[ -r $HOME/.bashrc ]] && source $HOME/.bashrc
287   HISTFILE="$root_dir/.bash_history"
288   history -c
289   history -r
290   # bind '"\\C-i": menu-complete'
291   alias c='break'
292   while cwd="$PWD/" cwd="${cwd#${root_dir}/}" cwd="${cwd%/}" \
293         prompt="[${cyan}benchmarks$none:$blue$cwd$clear]\$ " && \
294         read -p "$prompt" -e line; do
295      history -s "$line"
296      history -w
297      shopt -q -s expand_aliases
298      eval "$line"
299      shopt -q -u expand_aliases
300   done
301   [[ "${#line}" -eq 0 ]] && { echo; $exit_cmd 0; }
302   shopt -q -u expand_aliases
303   echo "Continuing ..." | tee -a $output_file
304fi
305
306# Call the function run_tests defined inside the $test_file
307ceed=$backend
308if [ -z "$dry_run" ]; then
309   run_tests >> $output_file
310else
311   run_tests
312fi
313echo
314
315done ## End of loop over processor numbers
316
317trap - INT
318
319} ## run is on
320
321$exit_cmd 0
322
323) || {
324   echo "Sub-shell for backend '$backend' returned error code $?. Stop."
325   $exit_cmd 1
326}
327done ## Loop over $backend_list
328
329done ## Loop over $bp_list
330
331$exit_cmd 0
332
333