sistema_progs

Programas para customizar o meu entorno de traballo nos meus equipos persoais
Log | Files | Refs

benchmark.sh (14731B)


      1 #!/bin/bash
      2 
      3 #%define 1
      4 #%if target == "ksh"
      5 #%% $ echo _ble_measure_target=ksh
      6 #%end
      7 if ! type ble/util/print &>/dev/null; then
      8   function ble/util/unlocal { builtin unset -v "$@"; }
      9   function ble/util/print { builtin printf '%s\n' "$1"; }
     10   function ble/util/print-lines { builtin printf '%s\n' "$@"; }
     11 fi
     12 
     13 function ble-measure/.loop {
     14   # Note: ksh requires to quote ;
     15   builtin eval "function _target { ${2:+"$2; "}return 0; }"
     16   local __ble_i __ble_n=$1
     17   for ((__ble_i=0;__ble_i<__ble_n;__ble_i++)); do
     18     _target
     19   done
     20 }
     21 
     22 ## @fn ble-measure/.time n command
     23 ##   @param[in] n command
     24 ##   @var[out] ret
     25 ##     計測にかかった総時間を μs 単位で返します。
     26 if ((BASH_VERSINFO[0]>=5)) ||
     27      { [[ ${ZSH_VERSION-} ]] && zmodload zsh/datetime &>/dev/null && [[ ${EPOCHREALTIME-} ]]; } ||
     28      [[ ${SECONDS-} == *.??? ]]
     29 then
     30   ## @fn ble-measure/.get-realtime
     31   ##   @var[out] ret
     32   if [[ ${EPOCHREALTIME-} ]]; then
     33     _ble_measure_resolution=1 # [usec]
     34     function ble-measure/.get-realtime {
     35       local LC_ALL= LC_NUMERIC=C
     36       ret=$EPOCHREALTIME
     37     }
     38   else
     39     # Note: ksh does not have "local"-equivalent for the POSIX-style functions,
     40     #   so we do not set the locale here.  Anyway, we do not care the
     41     #   interference with outer-scope variables since this script is used
     42     #   limitedly in ksh.
     43     _ble_measure_resolution=1000 # [usec]
     44     function ble-measure/.get-realtime {
     45       ret=$SECONDS
     46     }
     47   fi
     48   function ble-measure/.time {
     49     ble-measure/.get-realtime 2>/dev/null; local __ble_time1=$ret
     50     ble-measure/.loop "$1" "$2" &>/dev/null
     51     ble-measure/.get-realtime 2>/dev/null; local __ble_time2=$ret
     52 
     53     # convert __ble_time1 and __ble_time2 to usec
     54     # Note: ksh does not support empty index as ${__ble_frac::6}.
     55     local __ble_frac
     56     [[ $__ble_time1 == *.* ]] || __ble_time1=${__ble_time1}.
     57     __ble_frac=${__ble_time1##*.}000000 __ble_time1=${__ble_time1%%.*}${__ble_frac:0:6}
     58     [[ $__ble_time2 == *.* ]] || __ble_time2=${__ble_time2}.
     59     __ble_frac=${__ble_time2##*.}000000 __ble_time2=${__ble_time2%%.*}${__ble_frac:0:6}
     60 
     61     ((ret=__ble_time2-__ble_time1))
     62     ((ret==0&&(ret=_ble_measure_resolution)))
     63     ((ret>0))
     64   }
     65 elif [[ ${ZSH_VERSION-} ]]; then
     66   _ble_measure_resolution=1000 # [usec]
     67 #%if target == "ksh"
     68   # [ksh incompatible code stripped]
     69 #%else
     70   function ble-measure/.time {
     71     local result=
     72     result=$({ time ( ble-measure/.loop "$1" "$2" ; ) } 2>&1 )
     73     #local result=$({ time ( ble-measure/.loop "$1" "$2" &>/dev/null); } 2>&1)
     74     result=${result##*cpu }
     75     local rex='(([0-9]+):)?([0-9]+)\.([0-9]+) total$'
     76     if [[ $result =~ $rex ]]; then
     77       if [[ -o KSH_ARRAYS ]]; then
     78         local m=${match[1]} s=${match[2]} ms=${match[3]}
     79       else
     80         local m=${match[2]} s=${match[3]} ms=${match[4]}
     81       fi
     82       m=${m:-0} ms=${ms}000; ms=${ms:0:3}
     83 
     84       ((ret=((10#0$m*60+10#0$s)*1000+10#0$ms)*1000))
     85       return 0
     86     else
     87       builtin echo "ble-measure: failed to read the result of \`time': $result." >&2
     88       ret=0
     89       return 1
     90     fi
     91   }
     92 #%end
     93 else
     94   _ble_measure_resolution=1000 # [usec]
     95 #%if target == "ksh"
     96   # [ksh incompatible code stripped]
     97 #%else
     98   function ble-measure/.time {
     99     ret=0
    100 
    101     local result TIMEFORMAT='[%R]' __ble_n=$1 __ble_command=$2
    102     if declare -f ble/util/assign &>/dev/null; then
    103       ble/util/assign result '{ time ble-measure/.loop "$__ble_n" "$__ble_command" &>/dev/null;} 2>&1'
    104     else
    105       result=$({ time ble-measure/.loop "$1" "$2" &>/dev/null;} 2>&1)
    106     fi
    107 
    108     local rex='\[([0-9]+)(\.([0-9]+))?\]'
    109     [[ $result =~  $rex ]] || return 1
    110     local s=${BASH_REMATCH[1]}
    111     local ms=${BASH_REMATCH[3]}000; ms=${ms::3}
    112     ((ret=(10#0$s*1000+10#0$ms)*1000))
    113     return 0
    114   }
    115 #%end
    116 fi
    117 
    118 _ble_measure_base= # [nsec]
    119 _ble_measure_base_nestcost=0 # [nsec/10]
    120 #%if target == "ksh"
    121 #%% $ echo typeset -a _ble_measure_base_real
    122 #%% $ echo typeset -a _ble_measure_base_guess
    123 #%else
    124 _ble_measure_base_real=()
    125 _ble_measure_base_guess=()
    126 #%end
    127 _ble_measure_count=1 # 同じ倍率で _ble_measure_count 回計測して最小を取る。
    128 _ble_measure_threshold=100000 # 一回の計測が threshold [usec] 以上になるようにする
    129 
    130 #%if target != "ksh"
    131 ## @fn ble-measure/calibrate
    132 function ble-measure/calibrate.0 { ble-measure -qc"$calibrate_count" ''; }
    133 function ble-measure/calibrate.1 { ble-measure/calibrate.0; }
    134 function ble-measure/calibrate.2 { ble-measure/calibrate.1; }
    135 function ble-measure/calibrate.3 { ble-measure/calibrate.2; }
    136 function ble-measure/calibrate.4 { ble-measure/calibrate.3; }
    137 function ble-measure/calibrate.5 { ble-measure/calibrate.4; }
    138 function ble-measure/calibrate.6 { ble-measure/calibrate.5; }
    139 function ble-measure/calibrate.7 { ble-measure/calibrate.6; }
    140 function ble-measure/calibrate.8 { ble-measure/calibrate.7; }
    141 function ble-measure/calibrate.9 { ble-measure/calibrate.8; }
    142 function ble-measure/calibrate.A { ble-measure/calibrate.9; }
    143 function ble-measure/calibrate {
    144   local ret= nsec=
    145 
    146   local calibrate_count=1
    147   _ble_measure_base=0
    148   _ble_measure_base_nestcost=0
    149 
    150   # nest0: calibrate.0 の ble-measure 内部での ${#FUNCNAME[*]}
    151   local nest0=$((${#FUNCNAME[@]}+2))
    152   [[ ${ZSH_VERSION-} ]] && nest0=$((${#funcstack[@]}+2))
    153   ble-measure/calibrate.0; local x0=$nsec
    154   ble-measure/calibrate.A; local xA=$nsec
    155   local nest_cost=$((xA-x0))
    156   _ble_measure_base=$((x0-nest_cost*nest0/10))
    157   _ble_measure_base_nestcost=$nest_cost
    158 }
    159 function ble-measure/fit {
    160   local ret nsec
    161   _ble_measure_base=0
    162   _ble_measure_base_nestcost=0
    163 
    164   local calibrate_count=10
    165 
    166   local c= nest_level=${#FUNCNAME[@]}
    167   for c in {0..9} A; do
    168     "ble-measure/calibrate.$c"
    169     ble/util/print "$((nest_level++)) $nsec"
    170   done > ble-measure-fit.txt
    171 
    172   gnuplot - <<EOF
    173 f(x) = a * x + b
    174 b=4500;a=100
    175 fit f(x) 'ble-measure-fit.txt' via a,b
    176 EOF
    177 }
    178 #%end
    179 
    180 ## @fn ble-measure/.read-arguments.get-optarg
    181 ##   @var[in] args arg i c
    182 ##   @var[in,out] iarg
    183 ##   @var[out] optarg
    184 function ble-measure/.read-arguments.get-optarg {
    185   if ((i+1<${#arg})); then
    186     optarg=${arg:$((i+1))}
    187     i=${#arg}
    188     return 0
    189   elif ((iarg<${#args[@]})); then
    190     optarg=${args[iarg++]}
    191     return 0
    192   else
    193     ble/util/print "ble-measure: missing option argument for '-$c'."
    194     flags=E$flags
    195     return 1
    196   fi
    197 }
    198 
    199 ## @fn ble-measure/.read-arguments args
    200 ##   @var[out] flags
    201 ##   @var[out] command count
    202 function ble-measure/.read-arguments {
    203   local -a args; args=("$@")
    204   local iarg=0 optarg=
    205   [[ ${ZSH_VERSION-} && ! -o KSH_ARRAYS ]] && iarg=1
    206   while [[ ${args[iarg]} == -* ]]; do
    207     local arg=${args[iarg++]}
    208     case $arg in
    209     (--) break ;;
    210     (--help) flags=h$flags ;;
    211     (--no-print-progress) flags=V$flags ;;
    212     (--*)
    213       ble/util/print "ble-measure: unrecognized option '$arg'."
    214       flags=E$flags ;;
    215     (-?*)
    216       local i= c= # Note: zsh prints the values with just "local i c"
    217       for ((i=1;i<${#arg};i++)); do
    218         c=${arg:$i:1}
    219         case $c in
    220         (q) flags=qV$flags ;;
    221         ([ca])
    222           [[ $c == a ]] && flags=a$flags
    223           ble-measure/.read-arguments.get-optarg && count=$optarg ;;
    224         (T)
    225           ble-measure/.read-arguments.get-optarg &&
    226             measure_threshold=$optarg ;;
    227         (B)
    228           ble-measure/.read-arguments.get-optarg &&
    229             __ble_base=$optarg ;;
    230         (*)
    231           ble/util/print "ble-measure: unrecognized option '-$c'."
    232           flags=E$flags ;;
    233         esac
    234       done ;;
    235     (-)
    236       ble/util/print "ble-measure: unrecognized option '$arg'."
    237       flags=E$flags ;;
    238     esac
    239   done
    240   local IFS=$' \t\n'
    241   if [[ ${ZSH_VERSION-} ]]; then
    242     command="${args[$iarg,-1]}"
    243   else
    244     command="${args[*]:$iarg}"
    245   fi
    246   [[ $flags != *E* ]]
    247 }
    248 
    249 ## @fn ble-measure [-q|-ac COUNT] command
    250 ##   command を繰り返し実行する事によりその実行時間を計測します。
    251 ##   -q を指定した時、計測結果を出力しません。
    252 ##   -c COUNT を指定した時 COUNT 回計測して最小値を採用します。
    253 ##   -a COUNT を指定した時 COUNT 回計測して平均値を採用します。
    254 ##
    255 ##   @var[out] ret
    256 ##     実行時間を usec 単位で返します。
    257 ##   @var[out] nsec
    258 ##     実行時間を nsec 単位で返します。
    259 function ble-measure {
    260   local __ble_level=${#FUNCNAME[@]} __ble_base=
    261   [[ ${ZSH_VERSION-} ]] && __ble_level=${#funcstack[@]}
    262   local flags= command= count=$_ble_measure_count
    263   local measure_threshold=$_ble_measure_threshold
    264   ble-measure/.read-arguments "$@" || return "$?"
    265   if [[ $flags == *h* ]]; then
    266     ble/util/print-lines \
    267       'usage: ble-measure [-q|-ac COUNT|-TB TIME] [--] COMMAND' \
    268       '    Measure the time of command.' \
    269       '' \
    270       '  Options:' \
    271       '    -q        Do not print results to stdout.' \
    272       '    -a COUNT  Measure COUNT times and average.' \
    273       '    -c COUNT  Measure COUNT times and take minimum.' \
    274       '    -T TIME   Set minimal measuring time.' \
    275       '    -B BASE   Set base time (overhead of ble-measure).' \
    276       '    --        The rest arguments are treated as command.' \
    277       '    --help    Print this help.' \
    278       '' \
    279       '  Arguments:' \
    280       '    COMMAND   Command to be executed repeatedly.' \
    281       '' \
    282       '  Exit status:' \
    283       '    Returns 1 for the failure in measuring the time.  Returns 2 after printing' \
    284       '    help.  Otherwise, returns 0.'
    285     return 2
    286   fi
    287 
    288   if [[ ! $__ble_base ]]; then
    289     if [[ $_ble_measure_base ]]; then
    290       # ble-measure/calibrate 実行済みの時
    291       __ble_base=$((_ble_measure_base+_ble_measure_base_nestcost*__ble_level/10))
    292     else
    293       # それ以外の時は __ble_level 毎に計測
    294       if [[ ! $ble_measure_calibrate && ! ${_ble_measure_base_guess[__ble_level]} ]]; then
    295         if [[ ! ${_ble_measure_base_real[__ble_level+1]} ]]; then
    296           if [[ ${_ble_measure_target-} == ksh ]]; then
    297             # Note: In ksh, we cannot do recursive call with dynamic scoping,
    298             # so we directly call the measuring function
    299             ble-measure/.time 50000 ''
    300             ((nsec=ret*1000/50000))
    301           else
    302             local ble_measure_calibrate=1
    303             ble-measure -qc3 -B 0 ''
    304             ble/util/unlocal ble_measure_calibrate
    305           fi
    306           _ble_measure_base_real[__ble_level+1]=$nsec
    307           _ble_measure_base_guess[__ble_level+1]=$nsec
    308         fi
    309 
    310         # 上の実測値は一つ上のレベル (__ble_level+1) での結果になるので現在のレベル
    311         # (__ble_level) の値に補正する。レベル毎の時間が chatoyancy での線形フィッ
    312         # トの結果に比例する仮定して補正を行う。
    313         #
    314         # linear-fit result with $f(x) = A x + B$ in chatoyancy
    315         #   A = 65.9818 pm 2.945 (4.463%)
    316         #   B = 4356.75 pm 19.97 (0.4585%)
    317         local cA=6598 cB=435675
    318         nsec=${_ble_measure_base_real[__ble_level+1]}
    319         _ble_measure_base_guess[__ble_level]=$((nsec*(cB+cA*(__ble_level-1))/(cB+cA*__ble_level)))
    320         ble/util/unlocal cA cB
    321       fi
    322       __ble_base=${_ble_measure_base_guess[__ble_level]:-0}
    323     fi
    324   fi
    325 
    326   local __ble_max_n=500000
    327   local prev_n= prev_utot=
    328   local -i n
    329   for n in {1,10,100,1000,10000,100000}\*{1,2,5}; do
    330     [[ $prev_n ]] && ((n/prev_n<=10 && prev_utot*n/prev_n<measure_threshold*2/5 && n!=50000)) && continue
    331 
    332     local utot=0
    333     [[ $flags != *V* ]] && printf '%s (x%d)...' "$command" "$n" >&2
    334     ble-measure/.time "$n" "$command" || return 1
    335     [[ $flags != *V* ]] && printf '\r\e[2K' >&2
    336     ((utot=ret,utot>=measure_threshold||n==__ble_max_n)) || continue
    337 
    338     prev_n=$n prev_utot=$utot
    339     local min_utot=$utot
    340 
    341     # 繰り返し計測して最小値 (-a の時は平均値) を採用
    342     if [[ $count ]]; then
    343       local sum_utot=$utot sum_count=1 i
    344       for ((i=2;i<=count;i++)); do
    345         [[ $flags != *V* ]] && printf '%s' "$command (x$n $i/$count)..." >&2
    346         if ble-measure/.time "$n" "$command"; then
    347           ((utot=ret,utot<min_utot)) && min_utot=$utot
    348           ((sum_utot+=utot,sum_count++))
    349         fi
    350         [[ $flags != *V* ]] && printf '\r\e[2K' >&2
    351       done
    352       if [[ $flags == *a* ]]; then
    353         ((utot=sum_utot/sum_count))
    354       else
    355         utot=$min_utot
    356       fi
    357     fi
    358 
    359     # upate base if the result is shorter than base
    360     if ((min_utot<0x7FFFFFFFFFFFFFFF/1000)); then
    361       local __ble_real=$((min_utot*1000/n))
    362       [[ ${_ble_measure_base_real[__ble_level]} ]] &&
    363         ((__ble_real<_ble_measure_base_real[__ble_level])) &&
    364         _ble_measure_base_real[__ble_level]=$__ble_real
    365       [[ ${_ble_measure_base_guess[__ble_level]} ]] &&
    366         ((__ble_real<_ble_measure_base_guess[__ble_level])) &&
    367         _ble_measure_base_guess[__ble_level]=$__ble_real
    368       ((__ble_real<__ble_base)) &&
    369         __ble_base=$__ble_real
    370     fi
    371 
    372     local nsec0=$__ble_base
    373     if [[ $flags != *q* ]]; then
    374       local reso=$_ble_measure_resolution
    375       local awk=ble/bin/awk
    376       type "$awk" &>/dev/null || awk=awk
    377       local -x title="$command (x$n)"
    378       "$awk" -v utot="$utot" -v nsec0="$nsec0" -v n="$n" -v reso="$reso" '
    379         function genround(x, mod) { return int(x / mod + 0.5) * mod; }
    380         BEGIN { title = ENVIRON["title"]; printf("%12.3f usec/eval: %s\n", genround(utot / n - nsec0 / 1000, reso / 10.0 / n), title); exit }'
    381     fi
    382 
    383     local out
    384     ((out=utot/n))
    385     if ((n>=1000)); then
    386       ((nsec=utot/(n/1000)))
    387     else
    388       ((nsec=utot*1000/n))
    389     fi
    390     ((out-=nsec0/1000,nsec-=nsec0))
    391     ret=$out
    392     return 0
    393   done
    394 }
    395 #%end
    396 #%if target == "ksh"
    397 #%% # varname and command names
    398 #%% define 1 1.r|builtin ||
    399 #%% define 1 1.r| local | typeset |
    400 #%% define 1 1.r|ble_measure_calibrate|_ble_measure_calibrate|
    401 #%% # function names
    402 #%% define 1 1.r|ble/util/unlocal|_ble_util_unlocal|
    403 #%% define 1 1.r|ble/util/print-lines|_ble_util_print_lines|
    404 #%% define 1 1.r|ble/util/print|_ble_util_print|
    405 #%% define 1 1.r|ble-measure/.loop|_ble_measure__loop|
    406 #%% define 1 1.r|ble-measure/.get-realtime|_ble_measure__get_realtime|
    407 #%% define 1 1.r|ble-measure/.time|_ble_measure__time|
    408 #%% define 1 1.r|ble-measure/.read-arguments.get-optarg|_ble_measure__read_arguments_get_optarg|
    409 #%% define 1 1.r|ble-measure/.read-arguments|_ble_measure__read_arguments|
    410 #%% define 1 1.r|ble-measure|ble_measure|
    411 #%% # function defs
    412 #%% define 1 1.r|function _ble_measure__time|_ble_measure__time()|
    413 #%% define 1 1.r|function _ble_measure__get_realtime|_ble_measure__get_realtime()|
    414 #%% define 1 1.r|function _ble_util_unlocal|_ble_util_unlocal()|
    415 #%% define 1 1.r|function _ble_measure__read_arguments_get_optarg|_ble_measure__read_arguments_get_optarg()|
    416 #%% define 1 1.r|function _ble_measure__read_arguments|_ble_measure__read_arguments()|
    417 #%% define 1 1.r|function ble_measure|ble_measure()|
    418 #%end
    419 #%expand 1