#!/bin/ksh

# $Id: bench 3310 2006-08-16 22:41:34Z tov $
#
# "bench", a shell script to benchmark Scheme implementations
#
# Please report any errors or extensions to the author:
#
#   Marc Feeley (feeley@iro.umontreal.ca)
#
# The following have contributed to this benchmark suite:
#
#   Harvey Stein (abel@netvision.net.il)

# -----------------------------------------------------------------------------

error ()
{
  echo $1
  echo '
Usage: bench [-r runs] [-c clean] [-i iterfile] <scheme-systems> <benchmarks>

<scheme-systems> is the abbreviated name of one or more
Scheme implementations to use, i.e.:

  bigloo           for Bigloo.
  bigloo-int       for Bigloo (interpreted).
  chez             for Chez-Scheme.
  elk              for Elk.
  gambit           for Gambit-C.
  gambit-int       for Gambit-C (interpreted).
  guile            for Guile.
  hobbit           for '"SCM's"' hobbit compiler.
  larceny          for Larceny.
  mit              for MIT-Scheme.
  psi              for PSI.
  scheme2c         for scheme2c.
  scm              for SCM.
  scsh             for scsh.
  stalin           for Stalin.
  stk              for stk (actually, snow = STk No Windows).
  rs               for rscheme.
  rs-int           for rscheme (interpreted).
  umb-scheme       for umb-scheme.
  all              for all of the above.
  all-interpreters for the above interpreters.
  all-compilers    for the above compilers.

<benchmarks> is the name of one or more benchmarks
to use, i.e.:

  all         for all the benchmarks
  fib         for the fib benchmark
  "fib boyer" for fib & boyer.

runs is the number of times to run each benchmark (default is 1).

clean is whether or not to clean out the build directory.
true = clean.  Useful for testing or inspection.

iterfile is the file which specifies the number of iterations in
each benchmark.  If not supplied, we use num-iters.scm for compilers
and num-iters-int.scm for interpreters.  For testing, you might
want to use one-iter.scm, which runs each test once.'
  exit
}

# -----------------------------------------------------------------------------

cleanup ()
{
  if [ "$clean" = "true" ] ; then
     # It's true that technically speaking, we should be in the build
     # directory when this fcn is called.  Thus, we should be able to
     # just do rm *.  However, that's kind of dangerous, so instead,
     # we delete files newer than the mark file that evaluate () makes.

     for x in * ; do
        if [ $x -nt clean_newer_than_me ] ; then
          rm $x
        fi
     done
  fi
  rm clean_newer_than_me
}

evaluate ()
{
  echo > clean_newer_than_me
  sleep 1
  {
  echo
  echo Testing $1 under $NAME
  echo Compiling...
  make_src_code $1
  $COMP $1
  i=0
  while [ "$i" -lt "$NB_RUNS" ]
  do
    echo Running...
    $EXEC $1
    i=`expr $i + 1`
  done
  cleanup
  } 2>&1 | tee -a ../../results.${NAME}

}

make_src_code ()
{
  if [ -n "$iterfile" ]; then
    cat ../../src/prefix-${system}.scm ../../src/${iterfile} ../../src/$1.scm ../../src/suffix-${system}.scm > $1.scm
  else
    cat ../../src/$1.c > $1.c
  fi

}

# -----------------------------------------------------------------------------
# Definitions specific to Gambit-C compiler

gambit_comp ()
{
  DIR=`pwd`
  cp $1.scm /proj/will/Apps/Gambit/gambc30/gsc/temp.scm
  cd /proj/will/Apps/Gambit/gambc30/gsc
  {
# echo LD_LIBRARY_PATH=../../../lib GAMBCDIR=../../../lib ../../../gsc/gsc $1.scm
# echo gcc -I../../../lib -L../../../lib -O1 -D___SINGLE_HOST -o $1 $1.c $1_.c -lgambc -lm -ldl
    echo LD_LIBRARY_PATH=../lib GAMBCDIR=../lib ./gsc temp.scm
    echo gcc -I../lib -L../lib -O1 -D___SINGLE_HOST -o temp temp.c temp_.c ../lib/libgambc.so.1.1 -lm -ldl
  } | time sh
  rm temp.scm temp.c temp_.c
  cd $DIR
}

gambit_exec ()
{
  DIR=`pwd`
  cd /proj/will/Apps/Gambit/gambc30/gsc
#   LD_LIBRARY_PATH=../../../../lib time ./$1
   LD_LIBRARY_PATH=../lib time temp
  cd $DIR
}

# -----------------------------------------------------------------------------
# Definitions specific to Gambit-C interpreter

gambit_int_comp ()
{
  :
}

gambit_int_exec ()
{
  DIR=`pwd`
  cp $1.scm /proj/will/Apps/Gambit/gambc30/gsi/temp.scm
  cd /proj/will/Apps/Gambit/gambc30/gsi
#   LD_LIBRARY_PATH=../../../lib time ../../../gsi/gsi ./temp.scm
  ./gsi ./temp.scm
  rm temp.scm
  cd $DIR
}

# -----------------------------------------------------------------------------
# Definitions specific to Chez-Scheme

chez_comp ()
{
  :
}

chez_exec ()
{
  echo "(load \"$1.scm\")" | time scheme
}

# -----------------------------------------------------------------------------
# Definitions specific to Larceny

larceny_comp ()
{
  echo "(begin (load \"../../switches.scm\") (compile-file \"$1.scm\") (exit))" | ../../larceny ../../twobit.heap
}

larceny_exec ()
{
  echo "(load \"$1.fasl\")" | time ../../larceny ../../r5rs.heap
}

# -----------------------------------------------------------------------------
# Definitions specific to bigloo compiler

bigloo_comp ()
{
  case $1 in
     ctak|maze|puzzle) usecc="-call/cc" ;;
     *)                usecc=""         ;;
  esac
  time bigloo -O6 -farithmetic -unsafeatrsv $usecc -copt -O2 $1.scm -o $1
  ls -l $1
}

bigloo_exec ()
{
  time ./$1
}

# -----------------------------------------------------------------------------
# Definitions specific to bigloo interpreter

bigloo_int_comp ()
{
  :
}

bigloo_int_exec ()
{
  echo "(load \"$1.scm\")" | time bigloo
}

# -----------------------------------------------------------------------------
# Definitions specific to stalin

stalin_comp ()
{
  mv $1.scm $1.sc
  time stalin -copt -O2 -Ob -Om -On -Or -Ot -s $1
}

stalin_exec ()
{
  time ./$1
}

# -----------------------------------------------------------------------------
# Definitions specific to scm

scm_comp ()
{
  :
}

scm_exec ()
{
  time scm -f $1.scm
}

# -----------------------------------------------------------------------------
# Definitions specific to STk

stk_comp ()
{
  :
}

stk_exec ()
{
  ### Need to send in /dev/null so that it doesn't hang when there's
  ### an error...
  time snow -f $1.scm < /dev/null
}

# -----------------------------------------------------------------------------
# Definitions specific to CC

cc_comp ()
{
  {
     echo cc -O2 -o $1 $1.c -lm
  } | time sh
  ls -l $1
}

cc_exec ()
{
   time ./$1
}

# -----------------------------------------------------------------------------
# Definitions specific to GCC

gcc_comp ()
{
  {
     echo gcc -O2 -o $1 $1.c -lm
  } | time sh
  ls -l $1
}

gcc_exec ()
{
   time ./$1
}

# -----------------------------------------------------------------------------

ALL_BENCHMARKS="boyer browse conform cpstak ctak dderiv deriv destruc diviter divrec earley fft fib fibfp maze mazefun mbrot nucleic peval pnpoly puzzle ray scheme simplex slatex sum sumfp tak takl triangl trav1 trav2 smlboyer nboyer dynamic graphs lattice quicksort perm9"


ALL_INTERPRETERS='bigloo-int gambit-int scm stk'
ALL_COMPILERS='bigloo chez gambit larceny stalin cc gcc'
ALL_SYSTEMS="$ALL_COMPILERS $ALL_INTERPRETERS"

## Arg processing...
if [ "$#" -lt 2 ]; then
  error '>>> At least two command line arguments are needed'
fi


cmdline="$0"
flagsdone=0

NB_RUNS=1
clean=true

while [ $# -gt 2 ] ; do
   arg="$1"
   shift
   case $arg in
      -r) NB_RUNS=$1    ; shift ;;
      -c) clean=$1      ; shift ;;
      -i) forceiters=$1 ; shift ;;
       *) error ">>> Unknown argument of $arg given." ;;
   esac
done

if [ "$#" -ne 2 ]; then
  error '>>> Last two arguments must be <systems> and <benchmarks>'
fi

case "$1" in
   all)              systems="$ALL_SYSTEMS" ;;
   all-interpreters) systems="$ALL_INTERPRETERS" ;;
   all-compilers)    systems="$ALL_COMPILERS" ;;
   *)                systems="$1" ;;
esac

case "$2" in
   all) benchmarks="$ALL_BENCHMARKS" ;;
   *)   benchmarks="$2" ;;
esac

## Run each benchmark under each system...
for system in $systems ; do

   case "$system" in

    bigloo) NAME='Bigloo'
            COMP=bigloo_comp
            EXEC=bigloo_exec
            iterfile=num-iters.scm
            ;;

bigloo-int) NAME='Bigloo-int'
            COMP=bigloo_int_comp
            EXEC=bigloo_int_exec
            iterfile=num-iters-int.scm
            ;;

      chez) NAME='Chez-Scheme'
            COMP=chez_comp
            EXEC=chez_exec
            iterfile=num-iters.scm
            ;;

    gambit) NAME='Gambit-C'
            COMP=gambit_comp
            EXEC=gambit_exec
            iterfile=num-iters.scm
            ;;

gambit-int) NAME='Gambit-C-int'
            COMP=gambit_int_comp
            EXEC=gambit_int_exec
            iterfile=num-iters-int.scm
            ;;

   larceny) NAME='Larceny'
            COMP=larceny_comp
            EXEC=larceny_exec
            iterfile=num-iters.scm
            ;;

       mit) NAME='MIT-Scheme'
            COMP=mit_comp
            EXEC=mit_exec
            iterfile=num-iters.scm
            ;;

   mit-int) NAME='MIT-Scheme-int'
            COMP=mit_int_comp
            EXEC=mit_int_exec
            iterfile=num-iters-int.scm
            ;;

       scm) NAME='SCM'
            COMP=scm_comp
            EXEC=scm_exec
            iterfile=num-iters-int.scm
            ;;

    stalin) NAME='Stalin'
            COMP=stalin_comp
            EXEC=stalin_exec
            iterfile=num-iters.scm
            ;;

       stk) NAME='STk'
            COMP=stk_comp
            EXEC=stk_exec
            iterfile=num-iters-int.scm
            ;;

        cc) NAME='CC'
            COMP=cc_comp
            EXEC=cc_exec
            iterfile=
            ;;

       gcc) NAME='GCC'
            COMP=gcc_comp
            EXEC=gcc_exec
            iterfile=
            ;;

         *) error '>>> Unknown system'
            ;;
   esac

   if [ -n "$forceiters" ] ; then iterfile="$forceiters" ; fi

   cd sys/$system
   if [ $? != 0 ] ; then
      echo "ERROR: Can't change to directory sys/$system."
      exit 1
   fi

   {
      echo
      echo '****************************'
      echo Benchmarking $NAME on `date` under `uname -a`
   } >> ../../results.${NAME}

   for program in $benchmarks ; do
      evaluate $program
   done
   cd ../..
   if [ $? != 0 ] ; then
      echo "ERROR: Can't change back to benchmark directory."
      exit 1
   fi
done
