|
| 1 | +#!/bin/bash |
| 2 | +# |
| 3 | +# sample usage: |
| 4 | +# |
| 5 | +# mkdir tmp |
| 6 | +# |
| 7 | +# # CPU-only build |
| 8 | +# bash ./ci/run.sh ./tmp/results ./tmp/mnt |
| 9 | +# |
| 10 | +# # with CUDA support |
| 11 | +# GGML_CUDA=1 bash ./ci/run.sh ./tmp/results ./tmp/mnt |
| 12 | +# |
| 13 | + |
| 14 | +if [ -z "$2" ]; then |
| 15 | + echo "usage: $0 <output-dir> <mnt-dir>" |
| 16 | + exit 1 |
| 17 | +fi |
| 18 | + |
| 19 | +mkdir -p "$1" |
| 20 | +mkdir -p "$2" |
| 21 | + |
| 22 | +OUT=$(realpath "$1") |
| 23 | +MNT=$(realpath "$2") |
| 24 | + |
| 25 | +rm -f "$OUT/*.log" |
| 26 | +rm -f "$OUT/*.exit" |
| 27 | +rm -f "$OUT/*.md" |
| 28 | + |
| 29 | +sd=`dirname $0` |
| 30 | +cd $sd/../ |
| 31 | +SRC=`pwd` |
| 32 | + |
| 33 | +ALL_MODELS=( "tiny.en" "tiny" "base.en" "base" "small.en" "small" "medium.en" "medium" "large-v1" "large-v2" "large-v3" "large-v3-turbo" ) |
| 34 | +BENCH_N_THREADS=4 |
| 35 | +BENCH_ENCODER_ONLY=0 |
| 36 | +BENCH_FLASH_ATTN=0 |
| 37 | + |
| 38 | +# check for user-specified models first. if not specified, use fast models |
| 39 | +if [ ! -z ${GGML_TEST_MODELS} ]; then |
| 40 | + IFS=',' read -r -a MODELS <<< "${GGML_TEST_MODELS}" |
| 41 | +else |
| 42 | + if [ ! -z ${GG_BUILD_LOW_PERF} ]; then |
| 43 | + MODELS=( "tiny" "base" "small" ) |
| 44 | + else |
| 45 | + MODELS=("${ALL_MODELS[@]}") |
| 46 | + fi |
| 47 | +fi |
| 48 | + |
| 49 | +CMAKE_EXTRA="-DWHISPER_FATAL_WARNINGS=ON" |
| 50 | + |
| 51 | +if [ ! -z ${GGML_CUDA} ]; then |
| 52 | + CMAKE_EXTRA="${CMAKE_EXTRA} -DGGML_CUDA=ON -DCMAKE_CUDA_ARCHITECTURES=native" |
| 53 | +fi |
| 54 | + |
| 55 | +if [ ! -z ${GGML_SYCL} ]; then |
| 56 | + if [ -z ${ONEAPI_ROOT} ]; then |
| 57 | + echo "Not detected ONEAPI_ROOT, please install oneAPI base toolkit and enable it by:" |
| 58 | + echo "source /opt/intel/oneapi/setvars.sh" |
| 59 | + exit 1 |
| 60 | + fi |
| 61 | + |
| 62 | + CMAKE_EXTRA="${CMAKE_EXTRA} -DGGML_SYCL=ON -DCMAKE_C_COMPILER=icx -DCMAKE_CXX_COMPILER=icpx -DGGML_SYCL_F16=ON" |
| 63 | +fi |
| 64 | + |
| 65 | +if [ ! -z ${WHISPER_OPENVINO} ]; then |
| 66 | + CMAKE_EXTRA="${CMAKE_EXTRA} -DWHISPER_OPENVINO=ON" |
| 67 | +fi |
| 68 | + |
| 69 | +if [ ! -z ${GGML_METAL} ]; then |
| 70 | + CMAKE_EXTRA="${CMAKE_EXTRA} -DGGML_METAL=ON" |
| 71 | +fi |
| 72 | + |
| 73 | +if [ ! -z ${GGML_VULKAN} ]; then |
| 74 | + CMAKE_EXTRA="${CMAKE_EXTRA} -DGGML_VULKAN=ON" |
| 75 | +fi |
| 76 | + |
| 77 | +if [ ! -z ${GGML_BLAS} ]; then |
| 78 | + CMAKE_EXTRA="${CMAKE_EXTRA} -DGGML_BLAS=ON" |
| 79 | +fi |
| 80 | + |
| 81 | +if [ ! -z ${WHISPER_COREML} ]; then |
| 82 | + CMAKE_EXTRA="${CMAKE_EXTRA} -DWHISPER_COREML=ON" |
| 83 | +fi |
| 84 | + |
| 85 | +## helpers |
| 86 | + |
| 87 | +# download a file if it does not exist or if it is outdated |
| 88 | +function gg_wget { |
| 89 | + local out=$1 |
| 90 | + local url=$2 |
| 91 | + |
| 92 | + local cwd=`pwd` |
| 93 | + |
| 94 | + mkdir -p $out |
| 95 | + cd $out |
| 96 | + |
| 97 | + # should not re-download if file is the same |
| 98 | + wget -nv -N $url |
| 99 | + |
| 100 | + cd $cwd |
| 101 | +} |
| 102 | + |
| 103 | +function gg_download_model { |
| 104 | + local model_name=$1 |
| 105 | + local model_file="$MNT/models/ggml-${model_name}.bin" |
| 106 | + |
| 107 | + if [ ! -f ${model_file} ]; then |
| 108 | + local cwd=`pwd` |
| 109 | + mkdir -p "$MNT/models" |
| 110 | + cd "$MNT/models" |
| 111 | + bash "$cwd/models/download-ggml-model.sh" ${model_name} . |
| 112 | + cd "$cwd" |
| 113 | + fi |
| 114 | +} |
| 115 | + |
| 116 | +function gg_printf { |
| 117 | + printf -- "$@" >> $OUT/README.md |
| 118 | +} |
| 119 | + |
| 120 | +# Helper function to check command exit status |
| 121 | +function gg_check_last_command_status { |
| 122 | + local exit_file=$1 |
| 123 | + local command_name=$2 |
| 124 | + |
| 125 | + local exit_status=$? |
| 126 | + echo "$exit_status" > "$exit_file" |
| 127 | + |
| 128 | + if [ $exit_status -ne 0 ]; then |
| 129 | + echo "Error: Command $command_name failed with exit status $exit_status" |
| 130 | + return 1 |
| 131 | + fi |
| 132 | + |
| 133 | + return 0 |
| 134 | +} |
| 135 | + |
| 136 | +# Usage: gg_run <test_name> [additional_args...] |
| 137 | +# |
| 138 | +# Parameters: |
| 139 | +# test_name - Name of the test to run (calls gg_run_<test_name>) |
| 140 | +# additional_args - Any additional arguments to pass to the test function (first argument is appended to the log filename) |
| 141 | +function gg_run { |
| 142 | + ci=$1 |
| 143 | + |
| 144 | + if [ $# -gt 1 ]; then |
| 145 | + ci="${ci}_${2}" |
| 146 | + fi |
| 147 | + |
| 148 | + set -o pipefail |
| 149 | + set -x |
| 150 | + |
| 151 | + gg_run_$1 "$@" | tee $OUT/$ci.log |
| 152 | + cur=$? |
| 153 | + echo "$cur" > $OUT/$ci.exit |
| 154 | + |
| 155 | + set +x |
| 156 | + set +o pipefail |
| 157 | + |
| 158 | + gg_sum_$1 "$@" |
| 159 | + |
| 160 | + ret=$((ret | cur)) |
| 161 | +} |
| 162 | + |
| 163 | +function gg_check_build_requirements { |
| 164 | + if ! command -v cmake &> /dev/null; then |
| 165 | + gg_printf 'cmake not found, please install' |
| 166 | + fi |
| 167 | + |
| 168 | + if ! command -v make &> /dev/null; then |
| 169 | + gg_printf 'make not found, please install' |
| 170 | + fi |
| 171 | +} |
| 172 | + |
| 173 | +## ci |
| 174 | + |
| 175 | +function gg_run_ctest { |
| 176 | + mode=$2 |
| 177 | + |
| 178 | + cd ${SRC} |
| 179 | + |
| 180 | + rm -rf build-ci-${mode} && mkdir build-ci-${mode} && cd build-ci-${mode} |
| 181 | + |
| 182 | + set -e |
| 183 | + |
| 184 | + gg_check_build_requirements |
| 185 | + |
| 186 | + (time cmake -DCMAKE_BUILD_TYPE=${mode} ${CMAKE_EXTRA} .. ) 2>&1 | tee -a $OUT/${ci}-cmake.log |
| 187 | + (time make -j$(nproc) ) 2>&1 | tee -a $OUT/${ci}-make.log |
| 188 | + |
| 189 | + (time ctest --output-on-failure -L main -E test-opt ) 2>&1 | tee -a $OUT/${ci}-ctest.log |
| 190 | + |
| 191 | + set +e |
| 192 | +} |
| 193 | + |
| 194 | +function gg_sum_ctest { |
| 195 | + mode=$2 |
| 196 | + |
| 197 | + gg_printf '### %s\n\n' "${ci}" |
| 198 | + |
| 199 | + gg_printf 'Runs ctest in '${mode}' mode\n' |
| 200 | + gg_printf '- status: %s\n' "$(cat $OUT/${ci}.exit)" |
| 201 | + gg_printf '```\n' |
| 202 | + gg_printf '%s\n' "$(cat $OUT/${ci}-ctest.log)" |
| 203 | + gg_printf '```\n' |
| 204 | +} |
| 205 | + |
| 206 | +function gg_run_bench { |
| 207 | + cd ${SRC} |
| 208 | + |
| 209 | + # set flash attention flag if enabled |
| 210 | + fattn="" |
| 211 | + if [ "$BENCH_FLASH_ATTN" -eq 1 ]; then |
| 212 | + fattn="-fa" |
| 213 | + fi |
| 214 | + |
| 215 | + # run memcpy benchmark if not encoder-only mode |
| 216 | + if [ "$BENCH_ENCODER_ONLY" -eq 0 ]; then |
| 217 | + echo "Running memcpy benchmark" |
| 218 | + (time ./build-ci-release/bin/whisper-bench -w 1 -t $BENCH_N_THREADS 2>&1) | tee -a $OUT/${ci}-memcpy.log |
| 219 | + gg_check_last_command_status "$OUT/${ci}-memcpy.exit" "memcpy benchmark" |
| 220 | + |
| 221 | + echo "Running ggml_mul_mat benchmark with $BENCH_N_THREADS threads" |
| 222 | + (time ./build-ci-release/bin/whisper-bench -w 2 -t $BENCH_N_THREADS 2>&1) | tee -a $OUT/${ci}-mul_mat.log |
| 223 | + gg_check_last_command_status "$OUT/${ci}-mul_mat.exit" "ggml_mul_mat benchmark" |
| 224 | + fi |
| 225 | + |
| 226 | + echo "Running benchmark for all models" |
| 227 | + |
| 228 | + # generate header for the benchmark table |
| 229 | + { |
| 230 | + printf "| %16s | %13s | %3s | %3s | %7s | %7s | %7s | %7s | %7s |\n" "Config" "Model" "Th" "FA" "Enc." "Dec." "Bch5" "PP" "Commit" |
| 231 | + printf "| %16s | %13s | %3s | %3s | %7s | %7s | %7s | %7s | %7s |\n" "---" "---" "---" "---" "---" "---" "---" "---" "---" |
| 232 | + } | tee -a $OUT/${ci}-models-table.log |
| 233 | + |
| 234 | + # run benchmark for each model |
| 235 | + for model in "${MODELS[@]}"; do |
| 236 | + echo "Benchmarking model: $model" |
| 237 | + |
| 238 | + # run the benchmark and capture output |
| 239 | + output=$(./build-ci-release/bin/whisper-bench -m $MNT/models/ggml-$model.bin -t $BENCH_N_THREADS $fattn 2>&1) |
| 240 | + ret=$? |
| 241 | + |
| 242 | + # save the raw output |
| 243 | + echo "$output" > $OUT/${ci}-bench-$model.log |
| 244 | + |
| 245 | + if [ $ret -eq 0 ]; then |
| 246 | + # parse the benchmark results |
| 247 | + encode_time=$(echo "$output" | grep "encode time" | awk '{print $11}') |
| 248 | + decode_time=$(echo "$output" | grep "decode time" | awk '{print $11}') |
| 249 | + batchd_time=$(echo "$output" | grep "batchd time" | awk '{print $11}') |
| 250 | + prompt_time=$(echo "$output" | grep "prompt time" | awk '{print $11}') |
| 251 | + system_info=$(echo "$output" | grep "system_info") |
| 252 | + actual_threads=$(echo "$output" | grep "system_info" | awk '{print $4}') |
| 253 | + |
| 254 | + # determine configuration |
| 255 | + config="" |
| 256 | + if [[ $system_info == *"AVX2 = 1"* ]]; then |
| 257 | + config="$config AVX2" |
| 258 | + fi |
| 259 | + if [[ $system_info == *"NEON = 1"* ]]; then |
| 260 | + config="$config NEON" |
| 261 | + fi |
| 262 | + if [[ $system_info == *"BLAS = 1"* ]]; then |
| 263 | + config="$config BLAS" |
| 264 | + fi |
| 265 | + if [[ $system_info == *"COREML = 1"* ]]; then |
| 266 | + config="$config COREML" |
| 267 | + fi |
| 268 | + if [[ $system_info == *"CUDA = 1"* ]]; then |
| 269 | + config="$config CUDA" |
| 270 | + fi |
| 271 | + if [[ $system_info == *"METAL = 1"* ]]; then |
| 272 | + config="$config METAL" |
| 273 | + fi |
| 274 | + |
| 275 | + # get commit hash |
| 276 | + commit=$(git rev-parse --short HEAD) |
| 277 | + |
| 278 | + # add row to benchmark table |
| 279 | + printf "| %16s | %13s | %3s | %3s | %7s | %7s | %7s | %7s | %7s |\n" \ |
| 280 | + "$config" "$model" "$actual_threads" "$BENCH_FLASH_ATTN" "$encode_time" "$decode_time" "$batchd_time" "$prompt_time" "$commit" \ |
| 281 | + | tee -a $OUT/${ci}-models-table.log |
| 282 | + else |
| 283 | + echo "Benchmark failed for model: $model" | tee -a $OUT/${ci}-bench-errors.log |
| 284 | + fi |
| 285 | + done |
| 286 | +} |
| 287 | + |
| 288 | +function gg_sum_bench { |
| 289 | + gg_printf '### %s\n\n' "${ci}" |
| 290 | + |
| 291 | + gg_printf 'Whisper Benchmark Results\n' |
| 292 | + gg_printf '- status: %s\n' "$(cat $OUT/${ci}.exit)" |
| 293 | + |
| 294 | + # show memcpy and ggml_mul_mat benchmark results if available |
| 295 | + if [ "$BENCH_ENCODER_ONLY" -eq 0 ]; then |
| 296 | + if [ -f "$OUT/${ci}-memcpy.log" ]; then |
| 297 | + gg_printf '#### memcpy Benchmark\n\n' |
| 298 | + gg_printf '```\n%s\n```\n\n' "$(cat $OUT/${ci}-memcpy.log)" |
| 299 | + fi |
| 300 | + |
| 301 | + if [ -f "$OUT/${ci}-mul_mat.log" ]; then |
| 302 | + gg_printf '#### ggml_mul_mat Benchmark\n\n' |
| 303 | + gg_printf '```\n%s\n```\n\n' "$(cat $OUT/${ci}-mul_mat.log)" |
| 304 | + fi |
| 305 | + fi |
| 306 | + |
| 307 | + # show model benchmark results |
| 308 | + gg_printf '#### Model Benchmarks\n\n' |
| 309 | + if [ -f "$OUT/${ci}-models-table.log" ]; then |
| 310 | + gg_printf '%s\n\n' "$(cat $OUT/${ci}-models-table.log)" |
| 311 | + else |
| 312 | + gg_printf 'No model benchmark results available.\n\n' |
| 313 | + fi |
| 314 | + |
| 315 | + # show any errors that occurred |
| 316 | + if [ -f "$OUT/${ci}-bench-errors.log" ]; then |
| 317 | + gg_printf '#### Benchmark Errors\n\n' |
| 318 | + gg_printf '```\n%s\n```\n\n' "$(cat $OUT/${ci}-bench-errors.log)" |
| 319 | + fi |
| 320 | +} |
| 321 | + |
| 322 | +ret=0 |
| 323 | + |
| 324 | +for model in "${MODELS[@]}"; do |
| 325 | + test $ret -eq 0 && gg_download_model ${model} |
| 326 | +done |
| 327 | + |
| 328 | +test $ret -eq 0 && gg_run ctest debug |
| 329 | +test $ret -eq 0 && gg_run ctest release |
| 330 | + |
| 331 | +test $ret -eq 0 && gg_run bench |
| 332 | + |
| 333 | +exit $ret |
0 commit comments