#!/usr/bin/ksh # # (c) 2009, lists@nerdbynature.de # # Yet another filesystem benchmark script # # - bonnie++ # - dbench # - iozone3 # - tiobench # - some generic operations, like tar/touch/rm # # TODO: # - different mount options for each filesystems # - different benchmark options # # v0.1 - initial version # v0.2 - disabled 2 filesystems # ufs - http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=526586 # nilfs2 - filesystem fills up until ENOSPC # v0.3 - run tiobench with only 1 thread, otherwise we get: # Illegal division by zero at /usr/bin/tiobench line 163 # v0.4 - rewrite for ksh: this way we have fancy features to replace seq(1) # but still don't have to use a full blown bash. # # CONF="/usr/local/etc/fs-bench.conf" log() { echo "`date +'%F %H:%M:%S'`: $1" [ -n "$2" ] && exit "$2" } # sanity checks if [ ! -b "$1" -o ! -d "$2" -o ! -f $CONF ]; then log "Usage: `basename $0` [dev] [mpt]" log "Make sure $CONF exists!" 1 else DEV="$1" MPT="`echo $2 | sed 's/\/$//'`" . "$CONF" fi # overwrite results dir? if [ -d "$LOG" ]; then printf "Directory $LOG already exists, overwrite? (y/n) " && read c if [ $c = "y" ]; then $DEBUG rm -rf "$LOG" $DEBUG mkdir -p "$LOG"/raw else log "Aborted." 1 fi else $DEBUG mkdir "$LOG" fi ######################################################## mkfs_btrfs() { $DEBUG mkfs.btrfs $DEV 1>/dev/null $DEBUG mount -t btrfs -o noatime $DEV $MPT > /dev/null 2>&1 ERR=$? } mkfs_ext2() { $DEBUG mkfs.ext2 -q $DEV $DEBUG mount -t ext2 -o noatime,user_xattr $DEV $MPT > /dev/null 2>&1 ERR=$? } mkfs_ext3() { $DEBUG mkfs.ext3 -q $DEV $DEBUG mount -t ext3 -o noatime,user_xattr $DEV $MPT > /dev/null 2>&1 ERR=$? } mkfs_ext4() { $DEBUG mkfs.ext4 -q $DEV $DEBUG mount -t ext4 -o noatime,user_xattr $DEV $MPT > /dev/null 2>&1 ERR=$? } mkfs_jfs() { $DEBUG mkfs.jfs -q $DEV 1>/dev/null $DEBUG mount -t jfs -o noatime $DEV $MPT > /dev/null 2>&1 ERR=$? } mkfs_nilfs2() { $DEBUG mkfs.nilfs2 $DEV 2>/dev/null $DEBUG mount -t nilfs2 -o noatime $DEV $MPT > /dev/null 2>&1 ERR=$? } mkfs_reiserfs() { $DEBUG mkfs.reiserfs -q $DEV > /dev/null 2>&1 $DEBUG mount -t reiserfs -o noatime,user_xattr $DEV $MPT > /dev/null 2>&1 ERR=$? } mkfs_ufs() { $DEBUG mkfs.ufs -O2 -U $DEV > /dev/null 2>&1 $DEBUG mount -t ufs -o noatime $DEV $MPT > /dev/null 2>&1 ERR=$? } mkfs_xfs() { $DEBUG mkfs.xfs -q -f $DEV $DEBUG mount -t xfs -o noatime $DEV $MPT > /dev/null 2>&1 ERR=$? } ######################################################## run_bonnie() { eval conf_bonnie log "Running $b on $fs..." $DEBUG bonnie++ -u root -r $RAM -s $SIZE -m "$fs" -x 1 -d "$MPT" 1>$LOG/raw/bonnie-$fs.csv 2>$LOG/raw/bonnie-$fs.err $DEBUG cat $LOG/raw/bonnie-*.csv | bon_csv2html > $LOG/bonnie.html } ######################################################## run_stress() { # # Based on http://oss.oracle.com/~mason/stress.sh # Copyright (C) 1999 Bibliotech Ltd., 631-633 Fulham Rd., London SW6 5UQ. # $Id: stress.sh,v 1.2 1999/02/10 10:58:04 rich Exp $ # eval conf_stress log "Running $b on $fs (size: `du -sk "$CONTENT" | awk '{print $1 / 1024 " MB"}'`)..." $DEBUG mkdir $MPT/stress || log "cannot create $MPT/stress" 1 $DEBUG cd $MPT/stress || log "cannot cd into $MPT/stress" 1 # computing MD5 sums over content directory find $CONTENT -type f -exec md5sum '{}' + | sort > $MPT/content.sums # starting stress test processes p=1 while [ $p -le $CONCURRENT ]; do ( # wait for all processes to start up. if [ "$STAGGER" = "yes" ]; then sleep `expr 2 \* $p` else sleep 1 fi r=1 while [ $r -le $RUNS ]; do log "Running stresstest in $MPT/stress/$p (r: $r)..." # Remove old directories. $DEBUG rm -rf $MPT/stress/$p # copy content $DEBUG mkdir $MPT/stress/$p || log "cannot create $MPT/stress/$p" $DEBUG cp -dRx $CONTENT/* $MPT/stress/$p || log "cannot copy $CONTENT to $MPT/stress/$p" # compare the content and the copy. $DEBUG cd $MPT/stress/$p $DEBUG find . -type f -exec md5sum '{}' + | sort > $MPT/stress.$p $DEBUG diff -q $MPT/content.sums $MPT/stress.$p if [ $? != 0 ]; then log "corruption found in $MPT/stress/$p (r: $r)" continue fi $DEBUG cd $MPT/stress $DEBUG rm -f $MPT/stress.$p r=`expr $r + 1` done ) & p=`expr $p + 1` done } ######################################################## run_dbench() { eval conf_dbench log "Running $b on $fs..." $DEBUG dbench -x -t $TIME -D $MPT $NPROC > $LOG/raw/dbench-$fs.log echo "$fs: `egrep '^Throughput' $LOG/raw/dbench-$fs.log`" >> $LOG/dbench.txt } run_iozone() { eval conf_iozone log "Running $b on $fs..." $DEBUG cd $MPT || log "cannot cd into $MPT" 1 $DEBUG iozone -a -c -S $CACHESIZE -s $FILESIZE > $LOG/raw/iozone-$fs.log } run_tiobench() { eval conf_tiobench log "Running $b on $fs..." $DEBUG tiobench --identifier fs_"$fs" --size $SIZE --numruns $NUMRUNS --dir $MPT \ --block 4096 --block 8192 --threads 1 \ 1>$LOG/raw/tiobench-$fs.log 2>$LOG/raw/tiobench-$fs.err # results are hard to summarize echo " File Blk Num Avg Maximum Lat% Lat% CPU" > $LOG/tiobench.txt echo " Size Size Thr Rate (CPU%) Latency Latency >2s >10s Eff" >> $LOG/tiobench.txt for t in "Sequential Reads" "Sequential Writes" "Random Reads" "Random Writes"; do echo $t # adjust -An for more/less than 2 different blocksizes! grep -h -A5 "$t" $LOG/raw/tiobench-*.log | egrep '^fs_' echo done >> $LOG/tiobench.txt } run_generic() { eval conf_generic CONTENT_SIZE=`du -sk "$CONTENT" | awk '{print $1}'` ERR="" log "Running $b on $fs..." # - copy content to fs # - tar up local content # - copy content within same fs # - create NUMFILES in NUMDIRS GEN_BEGIN=`date +%s` $DEBUG tar -cf - $CONTENT 2>/dev/null | tar -C $MPT -xf - 2>/dev/null $DEBUG diff -q $CONTENT $MPT/$CONTENT > /dev/null 2>&1 [ $? = 0 ] || ERR="- FAILED" $DEBUG sync GEN_END=`date +%s` GEN_DUR=`echo "scale=2; $GEN_END - $GEN_BEGIN" | bc -l` SPEED=`echo "scale=2; ( $CONTENT_SIZE / $GEN_DUR ) / 1024" | bc -l` log "$fs: 1-tar content to $MPT and running diff took $GEN_DUR seconds. ($SPEED MB/s) $ERR" > $LOG/raw/generic-$fs.log GEN_BEGIN=`date +%s` cd $MPT $DEBUG tar -cf - . 2>/dev/null | dd of=/dev/null 2>/dev/null [ $? = 0 ] || ERR="- FAILED" $DEBUG sync GEN_END=`date +%s` GEN_DUR=`echo "scale=2; $GEN_END - $GEN_BEGIN" | bc -l` SPEED=`echo "scale=2; ( $CONTENT_SIZE / $GEN_DUR ) / 1024" | bc -l` log "$fs: 2-tar content on $MPT took $GEN_DUR seconds. ($SPEED MB/s) $ERR" >> $LOG/raw/generic-$fs.log GEN_BEGIN=`date +%s` cd $MPT $DEBUG mkdir TEST.$$ $DEBUG cp -fpR * TEST.$$ 2>/dev/null [ $? = 0 ] || ERR="- FAILED" $DEBUG sync GEN_END=`date +%s` GEN_DUR=`echo "scale=2; $GEN_END - $GEN_BEGIN" | bc -l` SPEED=`echo "scale=2; ( $CONTENT_SIZE / $GEN_DUR ) / 1024" | bc -l` log "$fs: 3-cp content took $GEN_DUR seconds. ($SPEED MB/s) $ERR" >> $LOG/raw/generic-$fs.log # create many files GEN_BEGIN=`date +%s` $DEBUG mkdir -p $MPT/manyfiles for d in `seq 1 $NUMDIRS`; do $DEBUG mkdir -p $MPT/manyfiles/dir."$d" 2>>$LOG/raw/generic-$fs.err $DEBUG cd $MPT/manyfiles/dir."$d" 2>>$LOG/raw/generic-$fs.err seq 1 $NUMFILES | xargs touch 2>>$LOG/raw/generic-$fs.err done sync GEN_END=`date +%s` GEN_DUR=`echo "scale=2; $GEN_END - $GEN_BEGIN" | bc -l` NUMDIRS_CREATED=`cd $MPT/manyfiles; find . -type d | egrep -v '^\.$' | wc -l` NUMFILES_CREATED=`cd $MPT/manyfiles; find . -type f | wc -l` [ $NUMDIRS_CREATED = $NUMDIRS -a $NUMFILES_CREATED = $NUMFILES ] || ERR="- FAILED" log "$fs: 4-creating $NUMFILES files in each of the $NUMDIRS directories took $GEN_DUR seconds. $ERR" >> $LOG/raw/generic-$fs.log # create many dirs (we just replace NUMDIRS with NUMFILES and vice versa) GEN_BEGIN=`date +%s` $DEBUG mkdir -p $MPT/manydirs for d in `seq 1 $NUMFILES`; do $DEBUG mkdir -p $MPT/manydirs/dir."$d" 2>>$LOG/raw/generic-$fs.err $DEBUG cd $MPT/manydirs/dir."$d" 2>>$LOG/raw/generic-$fs.err seq 1 $NUMDIRS | xargs touch 2>>$LOG/raw/generic-$fs.err done sync GEN_END=`date +%s` GEN_DUR=`echo "scale=2; $GEN_END - $GEN_BEGIN" | bc -l` NUMDIRS_CREATED=`cd $MPT/manydirs; find . -type d | egrep -v '^\.$' | wc -l` NUMFILES_CREATED=`cd $MPT/manydirs; find . -type f | wc -l` [ $NUMDIRS_CREATED = $NUMFILES -a $NUMFILES_CREATED = $NUMDIRS ] || ERR="- FAILED" log "$fs: 5-creating $NUMDIRS files in each of the $NUMFILES directories took $GEN_DUR seconds. $ERR" >> $LOG/raw/generic-$fs.log # results across all tests echo "Content size is `echo "scale=2; $CONTENT_SIZE / 1024" | bc -l` MB" > $LOG/generic.txt for t in `seq 1 5`; do grep -h -- "$t"- $LOG/raw/generic-*.log | sed 's/^.*[0-9]: //;s/[0-9]-//' echo done >> $LOG/generic.txt } ######################################################## mount | grep -q "$MPT" && log "$MPT is already mounted!" 1 BEGIN=`date +%s` for fs in $FILESYSTEMS; do echo "========================================================" FS_BEG=`date +%s` # mkfs, mount, umount for every benchmark for b in $BENCHMARKS; do mkfs_"$fs" if [ ! $ERR = 0 ]; then log "mkfs failed ($fs, $b)" continue fi BM_BEG=`date +%s` run_"$b" # 2>&1 | tee "$LOG"/"$b"-"$fs".log $DEBUG cd / $DEBUG sync $DEBUG sleep 5 $DEBUG sync $DEBUG umount $MPT || log "Unmounting $fs failed!" 1 BM_END=`date +%s` log "Running $b on $fs took `echo "scale=2; ( $BM_END - $BM_BEG ) / 60" | bc -l` minutes." done FS_END=`date +%s` echo log "Running all benchmarks on $fs took `echo "scale=2; ( $FS_END - $FS_BEG ) / 60" | bc -l` minutes." echo done 2>&1 | tee $LOG/fs-bench.log END=`date +%s` log "Finished after `echo "scale=2; ( $END - $BEGIN ) / 60" | bc -l` minutes."