| 1 | #!/usr/bin/env bash
 | 
| 2 | #
 | 
| 3 | # Test how long it takes to read many files
 | 
| 4 | 
 | 
| 5 | big-stream() {
 | 
| 6 |   cat */*.py 
 | 
| 7 |   # Python messes up here!
 | 
| 8 |   #*/*/*.py
 | 
| 9 | }
 | 
| 10 | 
 | 
| 11 | slow-stream() {
 | 
| 12 |   ### for testing signal handling in loop
 | 
| 13 |   local secs=${1:-1}
 | 
| 14 | 
 | 
| 15 |   while read -r line; do
 | 
| 16 |     sleep $secs
 | 
| 17 |     echo $line
 | 
| 18 |   done
 | 
| 19 | }
 | 
| 20 | 
 | 
| 21 | # TODO: Add to benchmarks2, which uses the oils-for-unix
 | 
| 22 | OSH_OPT=_bin/cxx-opt/osh
 | 
| 23 | YSH_OPT=_bin/cxx-opt/ysh
 | 
| 24 | 
 | 
| 25 | OSH_ASAN=_bin/cxx-asan/osh
 | 
| 26 | YSH_ASAN=_bin/cxx-asan/ysh
 | 
| 27 | 
 | 
| 28 | py3-count() {
 | 
| 29 |   echo '=== python3'
 | 
| 30 | 
 | 
| 31 |   # Buffered I/O is much faster
 | 
| 32 |   python3 -c '
 | 
| 33 | import sys
 | 
| 34 | i = 0
 | 
| 35 | for line in sys.stdin:
 | 
| 36 |   i += 1
 | 
| 37 | print(i)
 | 
| 38 | '
 | 
| 39 | }
 | 
| 40 | 
 | 
| 41 | awk-count() {
 | 
| 42 |   echo '=== awk'
 | 
| 43 |   awk '{ i += 1 } END { print i } '
 | 
| 44 | }
 | 
| 45 | 
 | 
| 46 | exec-ysh-count() {
 | 
| 47 |   local ysh=$1
 | 
| 48 |   local do_trap=${2:-}
 | 
| 49 | 
 | 
| 50 |   echo '=== ysh'
 | 
| 51 | 
 | 
| 52 |   local code='
 | 
| 53 | var i = 0
 | 
| 54 | for _ in (stdin) {
 | 
| 55 |   setvar i += 1
 | 
| 56 | }
 | 
| 57 | echo $i
 | 
| 58 | '
 | 
| 59 | 
 | 
| 60 |   if test -n "$do_trap"; then
 | 
| 61 |     # Register BEFORE creating pipeline
 | 
| 62 |     #trap usr1-handler USR1
 | 
| 63 |     code="
 | 
| 64 | trap 'echo \[pid \$\$\] usr1' USR1
 | 
| 65 | trap 'echo \[pid \$\$\] exit with status \$?' EXIT
 | 
| 66 | echo \"hi from YSH pid \$\$\"
 | 
| 67 | 
 | 
| 68 | $code
 | 
| 69 | "
 | 
| 70 |   fi
 | 
| 71 | 
 | 
| 72 |   # New buffered read!
 | 
| 73 |   exec $ysh -c "$code"
 | 
| 74 | }
 | 
| 75 | 
 | 
| 76 | usr1-handler() {
 | 
| 77 |   echo "pid $$ got usr1"
 | 
| 78 | }
 | 
| 79 | 
 | 
| 80 | exec-sh-count() {
 | 
| 81 |   local sh=$1
 | 
| 82 |   local do_trap=${2:-}
 | 
| 83 | 
 | 
| 84 |   echo "shell pid = $$"
 | 
| 85 | 
 | 
| 86 |   echo === $sh
 | 
| 87 | 
 | 
| 88 |   local code='
 | 
| 89 | i=0
 | 
| 90 | while read -r line; do
 | 
| 91 |   i=$(( i + 1 ))
 | 
| 92 | done
 | 
| 93 | echo $i
 | 
| 94 | '
 | 
| 95 | 
 | 
| 96 |   if test -n "$do_trap"; then
 | 
| 97 |     # Register BEFORE creating pipeline
 | 
| 98 |     #trap usr1-handler USR1
 | 
| 99 |     code="
 | 
| 100 | trap 'echo \[pid \$\$\] usr1' USR1
 | 
| 101 | trap 'echo \[pid \$\$\] exit with status \$?' EXIT
 | 
| 102 | echo \"hi from $sh pid \$\$\"
 | 
| 103 | 
 | 
| 104 | $code
 | 
| 105 | "
 | 
| 106 |   fi
 | 
| 107 |   #echo "$code"
 | 
| 108 | 
 | 
| 109 |   # need exec here for trap-demo
 | 
| 110 |   exec $sh -c "$code"
 | 
| 111 | }
 | 
| 112 | 
 | 
| 113 | compare-line-count() {
 | 
| 114 |   echo '=== wc'
 | 
| 115 |   time wc -l < $BIG_FILE  # warmup
 | 
| 116 |   echo
 | 
| 117 | 
 | 
| 118 |   time py3-count < $BIG_FILE
 | 
| 119 |   echo
 | 
| 120 | 
 | 
| 121 |   time awk-count < $BIG_FILE
 | 
| 122 |   echo
 | 
| 123 | 
 | 
| 124 |   time $0 exec-ysh-count $YSH_OPT < $BIG_FILE
 | 
| 125 |   echo
 | 
| 126 | 
 | 
| 127 |   for sh in dash bash $OSH_OPT; do
 | 
| 128 |     # need $0 because it exec
 | 
| 129 |     time $0 exec-sh-count $sh < $BIG_FILE
 | 
| 130 |     echo
 | 
| 131 |   done
 | 
| 132 | }
 | 
| 133 | 
 | 
| 134 | sh-count-slow-trap() {
 | 
| 135 |   local write_delay=${1:-0.20}
 | 
| 136 |   local kill_delay=${2:-0.07}
 | 
| 137 |   local -a argv=( ${@:3} )
 | 
| 138 | 
 | 
| 139 |   local len=${#argv[@]}
 | 
| 140 |   #echo "len=$len"
 | 
| 141 | 
 | 
| 142 |   if test $len -eq 0; then
 | 
| 143 |     echo 'argv required'
 | 
| 144 |   fi
 | 
| 145 |   echo "argv: ${argv[@]}"
 | 
| 146 | 
 | 
| 147 |   local sh=$1
 | 
| 148 | 
 | 
| 149 |   #exec-sh-count bash T & < <(seq 100 | slow-stream)
 | 
| 150 | 
 | 
| 151 |   echo "[pid $$] Spawn stream with write delay $write_delay"
 | 
| 152 | 
 | 
| 153 |   seq 10 | slow-stream $write_delay | "${argv[@]}" &
 | 
| 154 |   local pid=$!
 | 
| 155 | 
 | 
| 156 |   echo "pid of background job = $pid"
 | 
| 157 |   echo 'pstree:'
 | 
| 158 |   pstree -p $pid
 | 
| 159 |   echo
 | 
| 160 | 
 | 
| 161 |   echo "[pid $$] Entering kill loop ($kill_delay secs)"
 | 
| 162 | 
 | 
| 163 |   while true; do
 | 
| 164 |     # wait for USR1 to be registered
 | 
| 165 |     sleep $kill_delay
 | 
| 166 | 
 | 
| 167 |     kill -s USR1 $pid
 | 
| 168 |     local status=$?
 | 
| 169 | 
 | 
| 170 |     echo "[pid $$] kill $pid status: $status"
 | 
| 171 |     if test $status -ne 0; then
 | 
| 172 |       break
 | 
| 173 |     fi
 | 
| 174 | 
 | 
| 175 |   done
 | 
| 176 | 
 | 
| 177 |   time wait
 | 
| 178 |   echo "wait status: $?"
 | 
| 179 | }
 | 
| 180 | 
 | 
| 181 | test-ysh-for() {
 | 
| 182 |   sh-count-slow-trap '' '' exec-ysh-count $YSH_ASAN T
 | 
| 183 |   #sh-count-slow-trap '' '' exec-ysh-count bin/ysh T
 | 
| 184 | 
 | 
| 185 |   #sh-count-slow-trap 2.0 0.7 exec-ysh-count bin/ysh T
 | 
| 186 | 
 | 
| 187 |   #sh-count-slow-trap 2.0 0.7 exec-ysh-count $YSH_ASAN T
 | 
| 188 | }
 | 
| 189 | 
 | 
| 190 | test-ysh-read-error() {
 | 
| 191 |   ### testing errno!
 | 
| 192 | 
 | 
| 193 |   set +o errexit
 | 
| 194 |   $YSH_ASAN -c 'for x in (stdin) { echo $x }' < /tmp
 | 
| 195 |   echo status=$?
 | 
| 196 | }
 | 
| 197 | 
 | 
| 198 | test-read-errors() {
 | 
| 199 |   set +o errexit
 | 
| 200 | 
 | 
| 201 |   # Awk prints a warning, but exits 0!
 | 
| 202 |   awk '{ print }' < /tmp
 | 
| 203 |   echo status=$?
 | 
| 204 |   echo
 | 
| 205 | 
 | 
| 206 |   seq 3 | perl -e 'while (<>) { print "-" . $_ }'
 | 
| 207 | 
 | 
| 208 |   # Hm perl doesn't report this error!
 | 
| 209 |   perl -e 'while (<>) { print }' < /tmp
 | 
| 210 |   echo status=$?
 | 
| 211 | 
 | 
| 212 |   echo
 | 
| 213 | 
 | 
| 214 |   python3 -c '
 | 
| 215 | import sys
 | 
| 216 | for line in sys.stdin:
 | 
| 217 |   print(line)
 | 
| 218 | print("end")
 | 
| 219 | ' < /tmp
 | 
| 220 |   echo status=$?
 | 
| 221 | 
 | 
| 222 | 
 | 
| 223 | }
 | 
| 224 | 
 | 
| 225 | readonly BIG_FILE=_tmp/lines.txt
 | 
| 226 | 
 | 
| 227 | setup-benchmark() {
 | 
| 228 |   local n=${1:-1}  # how many copies
 | 
| 229 |   mkdir -p $(dirname $BIG_FILE)
 | 
| 230 | 
 | 
| 231 |   for i in $(seq $n); do
 | 
| 232 |     big-stream 
 | 
| 233 |   done > $BIG_FILE
 | 
| 234 | 
 | 
| 235 |   wc -l $BIG_FILE
 | 
| 236 | 
 | 
| 237 |   ninja $OSH_OPT $YSH_OPT
 | 
| 238 | }
 | 
| 239 | 
 | 
| 240 | setup-test() {
 | 
| 241 |   ninja $OSH_ASAN $YSH_ASAN
 | 
| 242 | }
 | 
| 243 | 
 | 
| 244 | soil-benchmark() {
 | 
| 245 |   setup-benchmark
 | 
| 246 | 
 | 
| 247 |   compare-line-count
 | 
| 248 | }
 | 
| 249 | 
 | 
| 250 | soil-test() {
 | 
| 251 |   setup-test
 | 
| 252 | 
 | 
| 253 |   # dash exits at the first try
 | 
| 254 |   #sh-count-slow-trap '' '' exec-sh-count dash T
 | 
| 255 | 
 | 
| 256 |   #sh-count-slow-trap '' '' exec-sh-count bash T
 | 
| 257 | 
 | 
| 258 |   # Oh interesting, mksh waits until the main loop!  Different behavior
 | 
| 259 |   #sh-count-slow-trap '' '' exec-sh-count mksh T
 | 
| 260 | 
 | 
| 261 |   sh-count-slow-trap '' '' exec-sh-count $OSH_ASAN T
 | 
| 262 | 
 | 
| 263 |   # OSH behaves like bash/zsh, yay
 | 
| 264 | 
 | 
| 265 |   test-ysh-for
 | 
| 266 | 
 | 
| 267 | 
 | 
| 268 |   return
 | 
| 269 | 
 | 
| 270 |   for sh in $YSH_OPT dash bash $OSH_OPT; do
 | 
| 271 |     sh-count-with-trap $sh
 | 
| 272 |     echo
 | 
| 273 |     echo
 | 
| 274 |   done
 | 
| 275 | }
 | 
| 276 | 
 | 
| 277 | "$@"
 | 
| 278 | 
 |