|  | 
|  | 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