#!/bin/sh ############################################################################ # # Script for testing block device I/O performance. Running this script on a # block device that is connected to a remote SCST target device allows to # test the performance of the transport protocols implemented in SCST. The # operation of this script is similar to iozone, while this script is easier # to use. # # Copyright (C) 2009 Bart Van Assche . # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation, version 2 # of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # ############################################################################ ######################### # Function definitions # ######################### usage() { echo "Usage: $0 [-a] [-d] [-i ] [-n] [-r] [-s ] " echo " -a - use asynchronous (buffered) I/O." echo " -d - use direct (non-buffered) I/O." echo " -i - number times each test is iterated." echo " -n - do not verify the data on before overwriting it." echo " -r - only perform the read test." echo " -s - logarithm base two of the I/O size." echo " - block device to run the I/O performance test on." } # Echo ((2**$1)) pow2() { if [ $1 = 0 ]; then echo 1 else echo $((2 * $(pow2 $(($1 - 1)) ) )) fi } drop_caches() { sync if [ -w /proc/sys/vm/drop_caches ]; then echo 3 > /proc/sys/vm/drop_caches fi } # Read times in seconds from stdin, one number per line, echo each number # using format $1, and also echo the average transfer size in MB/s, its # standard deviation and the number of IOPS using the total I/O size $2 and # the block transfer size $3. echo_and_calc_avg() { awk -v fmt="$1" -v iosize="$2" -v blocksize="$3" 'BEGIN{pow_2_20=1024*1024}{if ($1 != 0){n++;sum+=iosize/$1;sumsq+=iosize*iosize/($1*$1)};printf fmt, $1} END{d=(n>0?sumsq/n-sum*sum/n/n:0);avg=(n>0?sum/n:0);stddev=(d>0?sqrt(d):0);iops=avg/blocksize;printf fmt fmt fmt,avg/pow_2_20,stddev/pow_2_20,iops}' } ######################### # Default settings # ######################### iterations=3 log2_io_size=30 # 1 GB log2_min_blocksize=9 # 512 bytes log2_max_blocksize=26 # 64 MB iotype=direct read_test_only=false verify_device_data=true ######################### # Argument processing # ######################### set -- $(/usr/bin/getopt "adhi:nrs:" "$@") while [ "$1" != "${1#-}" ] do case "$1" in '-a') iotype="buffered"; shift;; '-d') iotype="direct"; shift;; '-i') iterations="$2"; shift; shift;; '-n') verify_device_data="false"; shift;; '-r') read_test_only="true"; shift;; '-s') log2_io_size="$2"; shift; shift;; '--') shift;; *) usage; exit 1;; esac done if [ "$#" != 1 ]; then usage exit 1 fi device="$1" #################### # Performance test # #################### if [ ! -e "${device}" ]; then echo "Error: device ${device} does not exist." exit 1 fi if [ "${read_test_only}" = "false" -a ! -w "${device}" ]; then echo "Error: device ${device} is not writeable." exit 1 fi if [ "${read_test_only}" = "false" -a "${verify_device_data}" = "true" ] \ && ! cmp -s -n $(pow2 $log2_io_size) "${device}" /dev/zero then echo "Error: device ${device} still contains data." exit 1 fi if [ "${iotype}" = "direct" ]; then dd_oflags="oflag=direct" dd_iflags="iflag=direct" else dd_oflags="oflag=sync" dd_iflags="" fi # Header, line 1 printf "%9s " blocksize i=0 while [ $i -lt ${iterations} ] do printf "%8s " "W" i=$((i+1)) done printf "%8s %8s %8s " "W(avg," "W(std," "W" i=0 while [ $i -lt ${iterations} ] do printf "%8s " "R" i=$((i+1)) done printf "%8s %8s %8s" "R(avg," "R(std" "R" printf "\n" # Header, line 2 printf "%9s " "(bytes)" i=0 while [ $i -lt ${iterations} ] do printf "%8s " "(s)" i=$((i+1)) done printf "%8s %8s %8s " "MB/s)" ",MB/s)" "(IOPS)" i=0 while [ $i -lt ${iterations} ] do printf "%8s " "(s)" i=$((i+1)) done printf "%8s %8s %8s" "MB/s)" ",MB/s)" "(IOPS)" printf "\n" # Measurements log2_blocksize=${log2_max_blocksize} while [ ! $log2_blocksize -lt $log2_min_blocksize ] do if [ $log2_blocksize -gt $log2_io_size ]; then continue fi iosize=$(pow2 $log2_io_size) bs=$(pow2 $log2_blocksize) count=$(pow2 $(($log2_io_size - $log2_blocksize))) printf "%9d " ${bs} i=0 while [ $i -lt ${iterations} ] do if [ "${read_test_only}" = "false" ]; then drop_caches dd if=/dev/zero of="${device}" bs=${bs} count=${count} \ ${dd_oflags} 2>&1 \ | sed -n 's/.* \([0-9.]*\) s,.*/\1/p' else echo 0 fi i=$((i+1)) done | echo_and_calc_avg "%8.3f " ${iosize} ${bs} i=0 while [ $i -lt ${iterations} ] do drop_caches dd if="${device}" of=/dev/null bs=${bs} count=${count} \ ${dd_iflags} 2>&1 \ | sed -n 's/.* \([0-9.]*\) s,.*/\1/p' i=$((i+1)) done | echo_and_calc_avg "%8.3f " ${iosize} ${bs} printf "\n" log2_blocksize=$((log2_blocksize - 1)) done