#!/usr/bin/perl ######################################################### # multitest, by Marcus Sorensen, BetterServers Inc # # Licensed under the Open Software License version 3.0 # # http://opensource.org/licenses/OSL-3.0 # ######################################################### use strict; $| = 1; my $colors = { red => "\e[1;31m", def => "\e[0m", green => "\e[1;32m", cyan => "\e[1;36m" }; my $restbetweentests = 15; unless ( `which fio 2>/dev/null`) { print "No executable 'fio' found in path, exiting\n"; exit; } print <{red} Multiple IO Tester$colors->{def} This application emulates a busy server in several states by launching multiple threads that do various types of IO. This allows us to see what the consequences are of running in a multitasking environment. This test uses direct IO and invalidates caches between tests, testing the disk, not the memory. $colors->{red}NOTE:$colors->{def} You need at least 3GB of free space in your current working directory. The following tests currently consist of: 8 sequential readers 8 sequential writers 8 mixed seqential readers/writers (random choice per IO) 8 random readers 8 random writers 8 mixed random readers/writers (random choice per IO) Feel free to modify the script to meet your needs. Enjoy! The test should take less than 2 minutes. Press to begin... EOF ; my $tests = { 'read' => { 'order' => 1, 'block' => '1024k', 'output' => { 'multiiotester'=>'4', '2'=>'5', '3'=>'6' }, 'name' => 'sequential read' }, 'write' => { 'order' => 2, 'block' => '1024k', 'output' => { 'multiiotester'=>'20', '2'=>'25', '3'=>'47' }, 'name' => 'sequential write' }, 'rw' => { 'order' => 3, 'block' => '1024k', 'output' => { 'multiiotester'=>'4,20', '2'=>'5,25', '3'=>'6,47' }, 'name' => 'seq read/seq write' }, 'randread' => { 'order' => 4, 'block' => '4k', 'output' => { 'multiiotester'=>'4', '2'=>'5', '3'=>'6' }, 'name' => 'random read' }, 'randwrite' => { 'order' => 5, 'block' => '4k', 'output' => { 'multiiotester'=>'20', '2'=>'25', '3'=>'47' }, 'name' => 'random write' } , 'randrw' => { 'order' => 6, 'block' => '4k', 'output' => { 'multiiotester'=>'4,20', '2'=>'5,25', '3'=>'6,47' }, 'name' => 'rand read/rand write' } }; #mkdir('./multiiotester') or die "unable to create test directory: $^E"; mkdir('./multiiotester') if ! -d './multiiotester'; chdir('./multiiotester') or die "unable to chdir to test directory: $^E"; foreach my $t ( sort{$tests->{$a}->{order} cmp $tests->{$b}->{order}} keys %{$tests} ) { print "$colors->{cyan} running IO \"$tests->{$t}->{name}\" test... $colors->{def}\n"; my $cmd = "fio --direct=1 --invalidate=1 --ioengine=libaio --iodepth=8 --thread --time_based --runtime=10 --rw=$t --bs=$tests->{$t}->{block} --size=300M --numjobs=8 --name=multiiotester --minimal | grep ';'"; my @output = `$cmd`; $output[0] =~ /^(.*?);/; my $version = $1; my $data; foreach my $d (@output){ next unless $d =~ /;/; my $field = $tests->{$t}->{output}->{$version}; my @items = split(";",$d); if ($field =~ /(\d+),(\d+)/) { $data .= "$items[$1];$items[$2]\n"; } else { $data .= "$items[$field]\n"; } } my @results = split(/;/,combinejobs($data)); my @iops = map { toiops($_,$tests->{$t}->{block}) } @results; @results = map { convert($_) } @results; if( $t =~ /^rand/ ) { print "\tresult is $colors->{green}" . join("/", @results) . "$colors->{def} per second\n"; print "\tequals $colors->{green}" . join("$colors->{def}/$colors->{green}", @iops) . "$colors->{def} IOs per second\n\n"; } else { print "\tresult is $colors->{green}" . join("$colors->{def}/$colors->{green}", @results) . "$colors->{def} per second\n\n"; } sleep $restbetweentests; } #print "cleaning up files..\n"; #unlink glob "multiiotester*"; #chdir(".."); #rmdir("multiiotester") or print "unable to delete directory 'multiiotester'\n"; ########################### ####### subroutines ####### ########################### sub convert { my $val = shift; my @units = ('KB','MB','GB'); my $i = 0; $val =~ /^\d+/; while (length($&) > 3 ) { $val = sprintf("%.2f",$val / 1024); $i++; $val =~ /^\d+/; } return $val . $units[$i]; } sub toiops { my $val = shift; my $blocksize = shift; $blocksize =~ s/k//; my $io = sprintf("%.1f",$val/$blocksize); return $io; } sub combinejobs { my $input = shift; my @lines = split(/\n/,$input); my @output = (); foreach my $l (0..$#lines) { my @temp = split(/;/,$lines[$l]); foreach my $t (0..$#temp){ $output[$t] += $temp[$t]; } } return join(";",@output); }