Commit 657452b7 authored by Célestin Marot's avatar Célestin Marot
Browse files

initial commit

parents
#!/bin/bash
set -o pipefail
domath() {
python3 -Ic "from math import *
print($@)"
}
omp_get_max_threads() {
tmpf=`mktemp` && echo -e "#include <omp.h>\nint main() {return omp_get_max_threads();}" | cc -x c -o tmpf - -fopenmp && ./tmpf; echo $? && rm tmpf
}
# default options
NITER=5
THREAD_LIST=()
AFFINITY_LIST=()
FILE_LIST=()
OPTIND=1
while getopts "hn:a:o:t:f:v" opt; do
case $opt in
h)
cat <<-EOF
usage:
./benchmark.sh [OPTIONS...] PROGRAM
example: benchmark.sh -t "1 2 4 8" hxt_clang/bin/hxtMesh3d
this script can benchmark any program and return the result as a .csv file.
a .info file with the same name contains the information about the program, CPU etc.
available options:
-n NITER run the program NITER times (default 5)
-a AWK_COMMAND awk command to find a values for which the mean must be computed
-o OUTPUT specify the output csv file (default=PROGRAM_NITER_THREADS.csv)
-t THREADS use OMP_NUM_THREADS=THREADS. THREADS can be of the form "T1 T2 T3" (can also be used multiple time)
-f FILES run the program on each file in FILES (can also be used multiple time)
-v verbose: set -o verbose
EOF
exit 0
;;
n)
NITER=${OPTARG}
;;
a)
if [ -n "${AWK_COMMAND}" ]; then
echo "awk command ${OPTARG} overrides previous awk command ${AWK_COMMAND}" >&2
fi
AWK_COMMAND="${OPTARG}"
;;
o)
OUTPUT=${OPTARG}
;;
t)
THREAD_LIST+=(${OPTARG})
;;
f)
FILE_LIST+=(${OPTARG})
;;
v)
set -o verbose
;;
\?)
echo "Invalid option: -${OPTARG}" >&2
;;
:)
echo "Option -${OPTARG} requires an argument." >&2
exit 1
;;
esac
done
shift $((OPTIND-1))
PROGRAM=$@
if [ -z "${PROGRAM}" ]; then
echo -e "No PROGRAM given\n\nUsage: ./benchmark.sh [OPTIONS...] PROGRAM" >&2
exit 1
fi
if ((!${#THREAD_LIST[@]} )); then
THREAD_LIST=(`omp_get_max_threads`)
fi
if ((!${#FILE_LIST[@]} )); then
FILE_LIST=("")
fi
if [ -z ${OUTPUT} ]; then
OUTPUT=`IFS=_; echo "${PROGRAM//[\/ ]/_}_${NITER}t${THREAD_LIST[*]}"`
fi
echo -e "testing \`${PROGRAM}\` ${NITER} times with (${THREAD_LIST[@]}) threads\ninput from (${FILE_LIST[@]}) output to \"${OUTPUT}\""
mv -i ${OUTPUT}.csv ${OUTPUT}.csv_bak
mv -i ${OUTPUT}.info ${OUTPUT}.info_bak
mv -i ${OUTPUT}.log ${OUTPUT}.log_bak
# writing the .info file
echo -e "\n \
writing info to ${OUTPUT}.info
==========================================================================="
echo "program: ${PROGRAM}" | tee -a ${OUTPUT}.info
for (( f = 0; f < ${#FILE_LIST[@]}; f++ )); do
echo "input file #$f: ${FILE_LIST[$f]}" | tee -a ${OUTPUT}.info
done
echo "number of iteration: ${NITER}" | tee -a ${OUTPUT}.info
echo "number of threads: ${THREAD_LIST[@]}" | tee -a ${OUTPUT}.info
if [ -n "${AWK_COMMAND}" ]; then
echo "awk command: ${AWK_COMMAND}" | tee -a ${OUTPUT}.info
fi
echo "program comments:" | tee -a ${OUTPUT}.info
readelf -p .comment ${PROGRAM%% *} | tee -a ${OUTPUT}.info
ret=$?
if [ $ret -ne 0 ]; then
echo "${PROGRAM%% *} does not appear to be an ELF executable" >&2
exit $ret
fi
lscpu | tee -a ${OUTPUT}.info
for (( f = 0; f < ${#FILE_LIST[@]}; f++ )); do
file=${FILE_LIST[$f]}
echo -e "\n \
benchmarking ${PROGRAM} ${file}
==========================================================================="
for (( t = 0; t < ${#THREAD_LIST[@]}; t++ )); do
export OMP_NUM_THREADS=${THREAD_LIST[$t]}
export GOMP_CPU_AFFINITY=`lscpu -p | python3 utils/get_best_affinity.py ${THREAD_LIST[$t]}`
echo -e "\n \
${OMP_NUM_THREADS} threads with affinity \"${GOMP_CPU_AFFINITY}\"
---------------------------------------------------------------------------"
wtimeAvg=0
CPUPercAvg=0
maxMemAvg=0
failed=0
for (( i = 0; i < ${NITER}; i++ )); do
echo "running ${PROGRAM} ${file} ... (writing output to .log file)"
out=`/usr/bin/time -f "%e %P %M" ${PROGRAM} ${file} 2>&1 | tee -a ${OUTPUT}.log`
ret=$?
if [ $ret -ne 0 ]; then
echo "/!\\ ======== benchmarked program failed ======== /!\\" >&2
failed=1
break
fi
time_out=(${out##*$'\n'}) # keep only the last line
echo "wtime:${time_out[0]} s, CPU%:${time_out[1]}, max Mem:${time_out[2]} kB"
wtimeAvg=`domath ${wtimeAvg}+${time_out[0]}`
CPUPercAvg=`domath ${CPUPercAvg}+${time_out[1]%\%}`
maxMemAvg=`domath ${maxMemAvg}+${time_out[2]}`
if [ -n "${AWK_COMMAND}" ]; then
awk_out=(`printf "%s" "${out}" | awk "${AWK_COMMAND}"`)
echo "awk command output: ${awk_out[@]}"
for (( c = 0; c < ${#awk_out[@]}; c++ )); do
awkAvg[$c]=`domath ${awkAvg[$c]}+${awk_out[$c]}`
done
fi
done
if (( ${failed} == 0 )); then
wtimeAvg=`domath ${wtimeAvg}/${NITER}`
CPUPercAvg=`domath ${CPUPercAvg}/${NITER}`
maxMemAvg=`domath ${maxMemAvg}/${NITER}`
for (( c = 0; c < ${#awkAvg[@]}; c++ )); do
awkAvg[$c]=`domath ${awkAvg[$c]}/${NITER}`
done
echo "writing results to .csv file"
(IFS=",";echo "${PROGRAM},${file},${OMP_NUM_THREADS},${wtimeAvg},${CPUPercAvg},${maxMemAvg},${awkAvg[*]}" | tee -a ${OUTPUT}.csv)
for (( c = 0; c < ${#awkAvg[@]}; c++ )); do
awkAvg[$c]=0
done
fi
done
done
#!/bin/bash
# example (with 1 2 4 7 8 threads): `bash hxt_clang.sh "1 2 4 7 8"`
bash benchmark.sh \
-t "$@" \
-n5 \
-o results/hxt_clang \
-f "meshes/*.msh" \
-a '/Final tet\. mesh contains/{print $7} /tEmptyMesh|tVerifyBnd|tBndRecovery|tConvertMesh|tRefine|tOptimize/{print $5}' \
hxt_clang/bin/hxtMesh3d -sv1
\ No newline at end of file
#!/bin/bash
# example (with 1 2 4 7 8 threads): `bash hxt_gcc.sh "1 2 4 7 8"`
bash benchmark.sh \
-t "$@" \
-n5 \
-o results/hxt_gcc \
-f "meshes/*.msh" \
-a '/Final tet\. mesh contains/{print $7} /tEmptyMesh|tVerifyBnd|tBndRecovery|tConvertMesh|tRefine|tOptimize/{print $5}' \
hxt_gcc/bin/hxtMesh3d -sv2
\ No newline at end of file
#!/usr/bin/env python3
# usage: GOMP_CPU_AFFINITY=`lscpu -p | python3 get_best_affinity.py ${OMP_NUM_THREADS}`
import sys
import numpy as np
# read the number of threads in the arguments
maxThreads=int(sys.argv[1])
# read the cpu info from stdin
cpus = []
for line in sys.stdin:
if line.startswith("#"):
continue
cpu = [int(elem) for elem in line.split(',') if elem]
cpus.append(cpu)
# cpus contain, for each virtual CPU:
# cpus[:,0]= the index of the virtual CPU
# cpus[:,1]= the corresponding physical core
# cpus[:,2]= the corresponding socket
# cpus[:,3]= the corresponding node
cpus = np.array(cpus, dtype=int)
[sockets, nCPUPerSocket]=np.unique(cpus[:,2], return_counts=True)
use = np.zeros(cpus.shape[0], dtype=bool) # we will put a 1 if we want to use that cpu
if maxThreads >= cpus.shape[0]:
use[:] = True
else:
threads=0
for socketN in range(sockets.size):
if(threads>=maxThreads):
break
onThisSocket=np.flatnonzero(cpus[:,2]==sockets[socketN])
[physicalOnThisSocket, nCPUPerPhysicalOnThisSocket] = np.unique(cpus[onThisSocket,1], return_counts=True)
threadsOnSocket=0
threadsPerPhysicalOnThisSocket = np.zeros(physicalOnThisSocket.size, dtype=int)
while threads<maxThreads and threadsOnSocket<nCPUPerSocket[socketN]:
# 1. find the physical core on this socket with the most space left
spaceLeft=nCPUPerPhysicalOnThisSocket - threadsPerPhysicalOnThisSocket
physicalN=np.argmax(spaceLeft)
# 2. find the first corresponding empty slot
corresponding=np.argmax(np.logical_and(cpus[:,1]==physicalOnThisSocket[physicalN],np.logical_not(use)))
use[corresponding] = True
threads+=1
threadsOnSocket+=1
threadsPerPhysicalOnThisSocket[physicalN]+=1
affinity=cpus[np.flatnonzero(use),0]
if affinity.size>1 and np.all(affinity==np.arange(affinity.size)):
print(str(0)+'-'+str(affinity.size-1))
else:
print(' '.join(str(a) for a in affinity))
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment