diff --git a/README.md b/README.md index 7d344e0..b9aa9df 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,14 @@ You must download and install the following or StorScore will not work: The Visual Studio 2015 C++ runtime libraries for x86 & x64: https://www.microsoft.com/en-us/download/details.aspx?id=48145 +StorScore requires libXML. Install it from the command line: + + For ActiveState perl installs: + $> ppm install XML::LibXML + + For Strawberry perl installs: + $> cpan XML::LibXML + StorScore will work without these components, but some features will be disabled: @@ -107,4 +115,4 @@ Open Source Code of Conduct This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) -or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. \ No newline at end of file +or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. diff --git a/StorScore.cmd b/StorScore.cmd index 3d8e8a7..6c0e349 100644 --- a/StorScore.cmd +++ b/StorScore.cmd @@ -115,7 +115,7 @@ else "$prefix-" . strftime( "%Y-%m-%d_%H-%M-%S", localtime ); } -print "Targeting " . uc( $target->type ) . ": " . $target->model . "\n"; +print "\nTargeting " . uc( $target->type ) . ": " . $target->model . "\n\n"; my $recipe = Recipe->new( file_name => $recipe_file, diff --git a/StorScore.ver b/StorScore.ver index 546d892..ad12a2d 100644 --- a/StorScore.ver +++ b/StorScore.ver @@ -2,4 +2,4 @@ The results in this directory were generated from the version of storscore published on the date listed below. If there is no date, the results were generated from an unpublished & untracked version of code. -Tue 01/24/2017 9:00:53.83 +Tue 02/21/2017 16:48:27.05 diff --git a/bin/DiskSpd.exe b/bin/DiskSpd.exe index ff63ed5..c00c244 100644 Binary files a/bin/DiskSpd.exe and b/bin/DiskSpd.exe differ diff --git a/lib/CommandLine.pm b/lib/CommandLine.pm index f2df997..d0c9542 100644 --- a/lib/CommandLine.pm +++ b/lib/CommandLine.pm @@ -363,8 +363,8 @@ END if( $self->demo_mode ) { $self->_active_range( 1 ); - $self->_test_time_override( 5 ); - $self->_warmup_time_override( 0 ); + $self->_test_time_override( 10 ); # seconds + $self->_warmup_time_override( 5 ); # seconds } if( defined $self->test_time_override ) diff --git a/lib/DiskSpdParser.pm b/lib/DiskSpdParser.pm index 9149469..6ff8c2b 100644 --- a/lib/DiskSpdParser.pm +++ b/lib/DiskSpdParser.pm @@ -31,36 +31,166 @@ use warnings; use Moose; use Util; -sub parse_cmd_line($$) +sub parse_workload($$$) { my $self = shift; + my $dir = shift; my $cmd_line = shift; my $stats_ref = shift; + + $cmd_line =~ /.+-X(.+).xml/; + my $xml_profile_path = $1; + + if( $xml_profile_path ) + { + # NB: we can't use the filename as-is from the result file because + # the results directory probably moved from its original location + + $xml_profile_path =~ /.*(profile-.*)/; + my $xml_profile = $1; + + parse_cmd_xml($dir, "$xml_profile.xml", $stats_ref); + } + else + { + parse_cmd_flags($cmd_line, $stats_ref); + } +} + +sub parse_cmd_xml($$$) +{ + my $dir = shift; + my $profile_filename = shift; + my $stats_ref = shift; + + my %target_stats = (); + my $target_count = 0; + my $sum_of_write_mix = 0; + my $sum_of_QD = 0; + my $sum_of_entropy = 0; + + my $xml_parser = XML::LibXML->new(); + my $doc = $xml_parser->parse_file( "$dir\\$profile_filename" ); + + foreach my $target ( $doc->findnodes( '//Target' ) ) + { + $target_count++; + + my $path = $target->findvalue( './Path' ); + # + #cannot target multiple files when there is no filesystem + $target_stats{$path}{'Raw Disk'} = 0; + + my $write_ratio = $target->findvalue( './WriteRatio' ); + $target_stats{$path}{'W Mix'} = $write_ratio; + $target_stats{$path}{'R Mix'} = 100 - $write_ratio; + $sum_of_write_mix += $write_ratio; + + my $io_size_B = $target->findvalue( './BlockSize' ); + $target_stats{$path}{'Access Size'} = $io_size_B / BYTES_PER_KB_BASE2; + + my $alignment = $io_size_B; + if( $target->findvalue( './Random' ) ) + { + $target_stats{$path}{'Access Type'} = 'random'; + $alignment = $target->findvalue( './Random' ); + } + else + { + $target_stats{$path}{'Access Type'} = 'sequential'; + $alignment = $target->findvalue( './StrideSize' ); + } + $target_stats{$path}{'Alignment'} = $alignment / BYTES_PER_KB_BASE2; + + my $ios_per_thread = $target->findvalue( './RequestCount' ); + my $num_threads = $target->findvalue( './ThreadsPerFile' ); + $target_stats{$path}{'QD'} = $ios_per_thread * $num_threads; + $sum_of_QD += $target_stats{$path}{'QD'}; + + my $entropy_filename = $target->findvalue( './/FilePath' ); + + #search for the format of Storscore's pre-generated entropy files + if( $entropy_filename =~ /.?([^\\]*)_pct_comp\.bin/ ) + { + my $file_name = $1; + + $file_name =~ /(\d+)/; + + $target_stats{$path}{'Compressibility'} = $1; + $sum_of_entropy += $1; + } + else + { + #file name format is not recognized + $target_stats{$path}{'Compressibility'} = "unknown"; + $sum_of_entropy = "unknown"; + } + } + + # Issue review: the aggregate workload of multiple targets is random + # Is there anything more intersting we can call it? For example, if all the + # individual workloads are sequential, should we call it something like + # "streaming"? + + # This entry representing the aggregate workload + $target_stats{'Total'}{'Raw Disk'} = 0; + $target_stats{'Total'}{'Access Type'} = 'random'; + $target_stats{'Total'}{'W Mix'} = $sum_of_write_mix / $target_count; + $target_stats{'Total'}{'R Mix'} = 100 - $target_stats{'Total'}{'W Mix'}; + $target_stats{'Total'}{'QD'} = $sum_of_QD; + $target_stats{'Total'}{'Compressibility'} = $sum_of_entropy / $target_count; + + $stats_ref->{'Workloads'} = \%target_stats; +} +sub parse_cmd_flags($$) +{ + my $cmd_line = shift; + my $stats_ref = shift; + + my %target_stats = (); + + $cmd_line =~ /\s+(".*"|\S+)$/ or die; + my $target = $1; + + if( $target =~ /^\#\d+$/ ) + { + $target_stats{'Total'}{'Raw Disk'} = 1; + } + else + { + $target_stats{'Total'}{'Raw Disk'} = 0; + } if( $cmd_line =~ /-w(\d+)/ ) { - $stats_ref->{'W Mix'} = $1; - $stats_ref->{'R Mix'} = 100 - $stats_ref->{'W Mix'}; + $target_stats{'Total'}{'W Mix'} = $1; + $target_stats{'Total'}{'R Mix'} = 100 - $1; } else { - $stats_ref->{'W Mix'} = 0; - $stats_ref->{'R Mix'} = 100; + $target_stats{'Total'}{'W Mix'} = 0; + $target_stats{'Total'}{'R Mix'} = 100; } - if( $cmd_line =~ /-r/ ) + $cmd_line =~ /-b(\w+)/; + $target_stats{'Total'}{'Access Size'} = human_to_kilobytes( $1 ); + + $target_stats{'Total'}{'Alignment'} = human_to_kilobytes( $1 ); + if( $cmd_line =~ /-r(\d+.*)\s+/ or $cmd_line =~ /-si(\d+.*)\s+/ ) { - $stats_ref->{'Access Type'} = 'random'; + $target_stats{'Total'}{'Alignment'} = human_to_kilobytes( $1 ); + } + + if( $cmd_line =~ /-r\s+/ ) + { + $target_stats{'Total'}{'Access Type'} = 'random'; } else { - $stats_ref->{'Access Type'} = 'sequential'; + $target_stats{'Total'}{'Access Type'} = 'sequential'; } - $cmd_line =~ /-b(\w+)/; - $stats_ref->{'Access Size'} = human_to_kilobytes( $1 ); - my $num_threads = 1; if( $cmd_line =~ /-t(\d+)/ ) @@ -71,52 +201,31 @@ sub parse_cmd_line($$) $cmd_line =~ /-o(\d+)/; my $ios_per_thread = $1; - $stats_ref->{'QD'} = $ios_per_thread * $num_threads; + $target_stats{'Total'}{'QD'} = $ios_per_thread * $num_threads; - if( $cmd_line =~ /-Z.*?([^\\]*)\.bin/ ) + #search for the format of Storscore's pre-generated entropy files + if( $cmd_line =~ /-Z.*?([^\\]*)_pct_comp\.bin/ ) { my $file_name = $1; $file_name =~ /(\d+)/; - $stats_ref->{'Compressibility'} = $1; + $target_stats{'Total'}{'Compressibility'} = $1; } else { - $stats_ref->{'Compressibility'} = 0; + #file name format is not recognized + $target_stats{'Total'}{'Compressibility'} = "unknown"; } - $cmd_line =~ /\s+(".*"|\S+)$/ or die; - my $target = $1; + $stats_ref->{'Workloads'} = \%target_stats; - if( $target =~ /^\#\d+$/ ) - { - $stats_ref->{'Raw Disk'} = 1; - } - else - { - $stats_ref->{'Raw Disk'} = 0; - } } my $pct_table_pat = qr/\|\s+(.+)\s+\|\s+(.+)\s+\|\s+(.+)\s+/; my @extract_rules = ( - { - match => qr/.+alignment:\s+(\d+).+/, - store => - [ - 'Alignment' - ] - }, - { - match => qr/.+stride:\s+(\d+).+/, - store => - [ - 'Alignment' - ] - }, { match => qr/min $pct_table_pat/, store => @@ -236,7 +345,7 @@ my @extract_rules = }, ); -sub remove_na($@) +sub remove_na($) { my $stats_ref = shift; @@ -255,34 +364,103 @@ sub parse($$) my $LOG = shift; my $stats_ref = shift; - + + my %target_stats = (); + my $previous_line = ""; + my $target_count = 0; + while( my $line = <$LOG> ) { + + if( $line =~ /path:/ ) + { + $target_count++; + } + if( $line =~ /^(\w+) IO/ ) { my $io_type = $1; while( $line = <$LOG> ) { + # This line contains per-thread throughput information + if( $line =~ /\s(\d+.*B)/ and $target_count > 1 ) + { + $line =~ s/\s+//g; + my @values = split /\|/, $line; + + my $mb = $values[1] / BYTES_PER_MB_BASE2; + my $ios = $values[2]; + my $mb_s = $values[3]; + my $iops = $values[4]; + my $lat = $values[5]; + + my $target_with_size = $values[7]; + $target_with_size =~ /(.*)\(.*\)/; + my $target = $1; + + $target_stats{$target}{"IOs $io_type"} += $ios; + $target_stats{$target}{"MB/sec $io_type"} += $mb_s; + $target_stats{$target}{"IOPS $io_type"} += $iops; + $target_stats{$target}{"Avg Latency $io_type"} += $ios*$lat; + } + + # This line contains the throughput totals for this IO type if( $line =~ /total:/ ) { $line =~ s/\s+//g; my @values = split /\|/, $line; - - $stats_ref->{"IOs $io_type"} = $values[1]; - $stats_ref->{"MB/sec $io_type"} = $values[2]; - $stats_ref->{"IOPS $io_type"} = $values[3]; - $stats_ref->{"Avg Latency $io_type"} = $values[4]; + + $target_stats{"Total"}{"IOs $io_type"} = $values[1]; + $target_stats{"Total"}{"MB/sec $io_type"} = $values[2]; + $target_stats{"Total"}{"IOPS $io_type"} = $values[3]; + $target_stats{"Total"}{"Avg Latency $io_type"} = $values[4]; last; } } + + foreach my $path ( keys %target_stats ) + { + unless ( $path eq "Total" or + $target_stats{$path}{"IOs $io_type"} == 0 ) + { + $target_stats{$path}{"Avg Latency $io_type"} = + $target_stats{$path}{"Avg Latency $io_type"} / + $target_stats{$path}{"IOs $io_type"}; + } + } + } - do_simple_extract( $line, $stats_ref, \@extract_rules ); + + if( $line =~ /-ile/ ) + { + + my $target = "Total"; + $previous_line =~ s/\s+//g; + $target = $previous_line + unless ( $previous_line eq "total:" ) or + ( $previous_line eq "" ); + + while( $line = <$LOG> ) + { + do_simple_extract( $line, + $target_stats{$target}, + \@extract_rules ); + last if( $line =~ /max/ ); + } + } + + $previous_line = $line; + } + + foreach my $path ( keys %target_stats ) + { + remove_na( $target_stats{$path} ); } - remove_na( $stats_ref, \@extract_rules ); + $stats_ref->{"Measurements"} = \%target_stats; } no Moose; diff --git a/lib/DiskSpdRunner.pm b/lib/DiskSpdRunner.pm index 032bd99..0515e4c 100644 --- a/lib/DiskSpdRunner.pm +++ b/lib/DiskSpdRunner.pm @@ -66,10 +66,7 @@ if( hyperthreading_enabled() ) $affinity_string = join ',', ( @physical_cores, @logical_cores ); } -else -{ - # Do nothing, DiskSpd uses simple round-robin affinity by default -} +# Else do nothing, DiskSpd uses simple round-robin affinity by default sub run($$) { @@ -78,18 +75,130 @@ sub run($$) my $test_ref = shift; my $run_type = shift; + my $run_time = $test_ref->{'run_time'}; + + if( $run_type eq 'warmup' ) + { + $run_time = $test_ref->{'warmup_time'}; + } + elsif( $run_type eq 'precondition' ) + { + $run_time = '3600' + unless ( $pretend or $self->cmd_line->demo_mode ); + } + + my $cmd; + + if( $test_ref->{'target_count'} > 1 ) + { + $cmd = $self->get_xml_cmdline($test_ref, $run_time); + } + else + { + $cmd = $self->get_cmdline($test_ref, $run_time); + } + + my $out_file = $self->output_dir . + "\\$run_type-$test_ref->{'description'}.txt"; + + open( my $OUT, ">$out_file" ) + or die "could not open $out_file: $!"; + + # Save the command line as line 1 of the file + print $OUT "$cmd\n"; + + my ( $errorlevel, $stdout, $stderr ) = execute_task( $cmd ); + + print $OUT "$stdout\n"; + print $OUT "$stderr\n"; + + close( $OUT ); + + print STDERR "\n\tDiskSpd returned non-zero errorlevel" if $errorlevel; + +} + +sub get_xml_cmdline($$$) +{ + my $self = shift; + my $test_ref = shift; + my $run_time = shift; + + my $compressibility = $test_ref->{'compressibility'}; + my $profile_template = $recipes_dir . + "\\xmlProfiles\\$test_ref->{'xml_profile'}"; + + my $parser = XML::LibXML->new(); + my $doc = $parser->parse_file( $profile_template ); + + for my $timespan ( $doc->findnodes( '//TimeSpan' ) ) + { + my $duration_node = XML::LibXML::Element->new( 'Duration' ); + $duration_node->appendTextNode( "$run_time" ); + $timespan->addChild( $duration_node ); + } + + # Use default unless compressibility is specified by the test + my $entropy_file = + Compressibility::get_filename( + $compressibility // $self->cmd_line->compressibility + ); + + my $entropy_file_size = int Compressibility::FILE_SIZE; + + for my $data_source ( $doc->findnodes( '//RandomDataSource' ) ) + { + my $file_node = XML::LibXML::Element->new('FilePath'); + $file_node->appendTextNode( "$entropy_file" ); + $data_source->addChild( $file_node ); + + my $file_size_node = XML::LibXML::Element->new( 'SizeInBytes' ); + $file_size_node->appendTextNode( "$entropy_file_size" ); + $data_source->addChild( $file_size_node ); + } + + my $profile_target_nodes = $doc->findnodes( '//Target' ); + my $profile_target_count = $profile_target_nodes->size(); + my $recipe_target_count = $test_ref->{'target_count'}; + + my $msg = "Number of targets in recipe ($recipe_target_count)" . + "does not match profile ($profile_target_count)\n"; + die $msg unless( $profile_target_count == $recipe_target_count ); + + my $file_num = 0; + for my $target ( $profile_target_nodes->get_nodelist ) + { + $file_num++; + + my $path = $self->target->volume . "\\$TEST_FILE_STUB$file_num.dat"; + + my $path_node = XML::LibXML::Element->new( 'Path' ); + $path_node->appendTextNode( $path ); + $target->addChild( $path_node ); + } + + my $profile_file = $self->output_dir . + "\\profile-$test_ref->{'description'}.xml"; + + $doc->toFile($profile_file); + + my $cmd = "DiskSpd.exe -X$profile_file"; + + return $cmd; +} + +sub get_cmdline($$$) +{ + my $self = shift; + my $test_ref = shift; + my $run_time = shift; + my $write_percentage = $test_ref->{'write_percentage'}; my $access_pattern = $test_ref->{'access_pattern'}; my $block_size = $test_ref->{'block_size'}; my $queue_depth = $test_ref->{'queue_depth'}; - my $run_time = $test_ref->{'run_time'}; my $compressibility = $test_ref->{'compressibility'}; - if( $run_type eq 'warmup' ) - { - $run_time = $test_ref->{'warmup_time'}; - } - # Gradually increase the number of threads as queue_depth # increases in order to prevent CPU limitation. my $num_threads = 1; @@ -151,23 +260,7 @@ sub run($$) $cmd .= $self->target->file_name; } - my $out_file = $self->output_dir . - "\\$run_type-$test_ref->{'description'}.txt"; - - open( my $OUT, ">$out_file" ) - or die "could not open $out_file: $!"; - - # Save the command line as line 1 of the file - print $OUT "$cmd\n"; - - my ( $errorlevel, $stdout, $stderr ) = execute_task( $cmd ); - - print $OUT "$stdout\n"; - print $OUT "$stderr\n"; - - close( $OUT ); - - print STDERR "\n\tDiskSpd returned non-zero errorlevel" if $errorlevel; + return $cmd; } no Moose; diff --git a/lib/Endurance.pm b/lib/Endurance.pm index 893b5f5..87f2402 100644 --- a/lib/Endurance.pm +++ b/lib/Endurance.pm @@ -72,10 +72,10 @@ sub compute_host_writes($$) my $stats_ref = shift; my $ddb_ref = shift; - return unless exists $stats_ref->{'Host Writes Before'}; + return unless exists $stats_ref->{'Measurements'}{'Total'}{'Host Writes Before'}; - my $before = $stats_ref->{'Host Writes Before'}; - my $after = $stats_ref->{'Host Writes After'}; + my $before = $stats_ref->{'Measurements'}{'Total'}{'Host Writes Before'}; + my $after = $stats_ref->{'Measurements'}{'Total'}{'Host Writes After'}; my $diff = $after - $before; # if the device db doesn't define a unit, assume it's an NVMe drive with the default units @@ -87,7 +87,8 @@ sub compute_host_writes($$) $stats_ref->{'Notes'} .= "Host writes too small ($diff). WAF is wrong; " if $diff < 5; - $stats_ref->{'Host Writes'} = ( $after - $before ) * $units; + $stats_ref->{'Measurements'}{'Total'}{'Host Writes'} = + ( $after - $before ) * $units; } sub compute_controller_writes($$) @@ -95,10 +96,10 @@ sub compute_controller_writes($$) my $stats_ref = shift; my $ddb_ref = shift; - return unless exists $stats_ref->{'Controller Writes Before'}; + return unless exists $stats_ref->{'Measurements'}{'Total'}{'Controller Writes Before'}; - my $before = $stats_ref->{'Controller Writes Before'}; - my $after = $stats_ref->{'Controller Writes After'}; + my $before = $stats_ref->{'Measurements'}{'Total'}{'Controller Writes Before'}; + my $after = $stats_ref->{'Measurements'}{'Total'}{'Controller Writes After'}; my $diff = $after - $before; my $units = $ddb_ref->{'Controller Writes'}{'Unit'} // BYTES_PER_MB_BASE2; @@ -109,9 +110,11 @@ sub compute_controller_writes($$) $stats_ref->{'Notes'} .= "Ctlr writes too small ($diff). WAF is wrong; " if $diff < 5; - $stats_ref->{'Controller Writes'} = ( $after - $before ) * $units; + $stats_ref->{'Measurements'}{'Total'}{'Controller Writes'} = + ( $after - $before ) * $units; - $stats_ref->{'Controller Writes'} += $stats_ref->{'Host Writes'} + $stats_ref->{'Measurements'}{'Total'}{'Controller Writes'} += + $stats_ref->{'Measurements'}{'Total'}{'Host Writes'} if exists $ddb_ref->{'Controller Writes'}{'Additive'}; } @@ -120,13 +123,14 @@ sub compute_file_system_waf($$) my $stats_ref = shift; my $ddb_ref = shift; - return unless exists $stats_ref->{'Host Writes'}; + return unless exists $stats_ref->{'Measurements'}{'Total'}{'Host Writes'}; my $host_writes_in_GB = - $stats_ref->{'Host Writes'} / BYTES_PER_GB_BASE2; + $stats_ref->{'Measurements'}{'Total'}{'Host Writes'} / + BYTES_PER_GB_BASE2; - $stats_ref->{'Filesystem Write Amplification'} = - $host_writes_in_GB / $stats_ref->{'GB Write'}; + $stats_ref->{'Measurements'}{'Total'}{'Filesystem Write Amplification'} = + $host_writes_in_GB / $stats_ref->{'Measurements'}{'Total'}{'GB Write'}; } sub compute_drive_waf($$) @@ -135,13 +139,14 @@ sub compute_drive_waf($$) my $ddb_ref = shift; return unless - exists $stats_ref->{'Host Writes'} and - exists $stats_ref->{'Controller Writes'}; + exists $stats_ref->{'Measurements'}{'Total'}{'Host Writes'} and + exists $stats_ref->{'Measurements'}{'Total'}{'Controller Writes'}; - my $ctrl_writes = $stats_ref->{'Controller Writes'}; - my $host_writes = $stats_ref->{'Host Writes'}; + my $ctrl_writes = $stats_ref->{'Measurements'}{'Total'}{'Controller Writes'}; + my $host_writes = $stats_ref->{'Measurements'}{'Total'}{'Host Writes'}; - $stats_ref->{'Drive Write Amplification'} = $ctrl_writes / $host_writes + $stats_ref->{'Measurements'}{'Total'}{'Drive Write Amplification'} = + $ctrl_writes / $host_writes unless $host_writes == 0; } @@ -150,13 +155,15 @@ sub compute_total_waf($$) my $stats_ref = shift; my $ddb_ref = shift; - return unless exists $stats_ref->{'Controller Writes'}; + return + unless exists $stats_ref->{'Measurements'}{'Total'}{'Controller Writes'}; my $ctrl_writes_in_GB = - $stats_ref->{'Controller Writes'} / BYTES_PER_GB_BASE2; + $stats_ref->{'Measurements'}{'Total'}{'Controller Writes'} / + BYTES_PER_GB_BASE2; - $stats_ref->{'Total Write Amplification'} = - $ctrl_writes_in_GB / $stats_ref->{'GB Write'}; + $stats_ref->{'Measurements'}{'Total'}{'Total Write Amplification'} = + $ctrl_writes_in_GB / $stats_ref->{'Measurements'}{'Total'}{'GB Write'}; } sub compute_nand_metrics($$) @@ -164,16 +171,22 @@ sub compute_nand_metrics($$) my $stats_ref = shift; my $ddb_ref = shift; - return unless exists $stats_ref->{'Drive Write Amplification'}; + return unless exists + $stats_ref->{'Measurements'}{'Total'}{'Drive Write Amplification'}; - my $drive_waf = $stats_ref->{'Drive Write Amplification'}; - my $app_write_bw = $stats_ref->{'MB/sec Write'}; - my $app_gb_written = $stats_ref->{'GB Write'}; + my $drive_waf = + $stats_ref->{'Measurements'}{'Total'}{'Drive Write Amplification'}; + my $app_write_bw = + $stats_ref->{'Measurements'}{'Total'}{'MB/sec Write'}; + my $app_gb_written = + $stats_ref->{'Measurements'}{'Total'}{'GB Write'}; - $stats_ref->{'NAND Writes (GB)'} = $app_gb_written * $drive_waf; + $stats_ref->{'Measurements'}{'Total'}{'NAND Writes (GB)'} = + $app_gb_written * $drive_waf; - $stats_ref->{'NAND Write BW (MB/sec)'} = $app_write_bw * $drive_waf - if exists $stats_ref->{'MB/sec Write'}; + $stats_ref->{'Measurements'}{'Total'}{'NAND Write BW (MB/sec)'} = + $app_write_bw * $drive_waf + if exists $stats_ref->{'Measurements'}{'Total'}{'MB/sec Write'}; } sub compute_dwpd($$) @@ -183,10 +196,10 @@ sub compute_dwpd($$) return unless exists $ddb_ref->{'Rated PE Cycles'} and - exists $stats_ref->{'Drive Write Amplification'}; + exists $stats_ref->{'Measurements'}{'Total'}{'Drive Write Amplification'}; my $rated_cycles = $ddb_ref->{'Rated PE Cycles'}; - my $waf = $stats_ref->{'Drive Write Amplification'}; + my $waf = $stats_ref->{'Measurements'}{'Total'}{'Drive Write Amplification'}; return unless $waf > 0; @@ -213,8 +226,10 @@ sub compute_dwpd($$) my $adjusted_cycles = $rated_cycles * ( $total_nand_bytes / $mapped_bytes ); - $stats_ref->{'DWPD 3yr'} = $adjusted_cycles / ( $waf * 3 * 365 ); - $stats_ref->{'DWPD 5yr'} = $adjusted_cycles / ( $waf * 5 * 365 ); + $stats_ref->{'Measurements'}{'Total'}{'DWPD 3yr'} = + $adjusted_cycles / ( $waf * 3 * 365 ); + $stats_ref->{'Measurements'}{'Total'}{'DWPD 5yr'} = + $adjusted_cycles / ( $waf * 5 * 365 ); } sub test_contains_writes($) @@ -222,9 +237,9 @@ sub test_contains_writes($) my $stats_ref = shift; die "Writes Mix is not defined\n" - unless exists $stats_ref->{'W Mix'}; + unless exists $stats_ref->{'Workloads'}{'Total'}{'W Mix'}; - return $stats_ref->{'W Mix'} > 0; + return $stats_ref->{'Workloads'}{'Total'}{'W Mix'} > 0; } 1; diff --git a/lib/LogmanParser.pm b/lib/LogmanParser.pm index 842a0d4..5214fc4 100644 --- a/lib/LogmanParser.pm +++ b/lib/LogmanParser.pm @@ -80,18 +80,20 @@ sub parse($$) if( $counter_name =~ /Processor Time/ ) { - $stats_ref->{'CPU Util'} = $average / 100; + $stats_ref->{'Measurements'}{'Total'}{'CPU Util'} = $average / 100; $cpu_seen = 1; } elsif( $counter_name =~ /Disk Queue Length/ ) { - $stats_ref->{'Measured QD'} = $average; + $stats_ref->{'Measurements'}{'Total'}{'Measured QD'} = $average; $qd_seen = 1; # Raise a warning if the percentage difference between # actual QD and measured QD exceeds 90% - my $target_qd = $stats_ref->{'QD'}; - my $measured_qd = $stats_ref->{'Measured QD'}; + my $target_qd = + $stats_ref->{'Workloads'}{'Total'}{'QD'}; + my $measured_qd = + $stats_ref->{'Measurements'}{'Total'}{'Measured QD'}; my $qd_threshold = 90; my $qd_diff = abs($target_qd - $measured_qd) / $target_qd * 100; @@ -105,11 +107,11 @@ sub parse($$) } elsif( $counter_name =~ /System Driver Total Bytes/ ) { - $stats_ref->{'Driver Memory (MB)'} = $average / ( 1024 * 1024 ); + $stats_ref->{'Measurements'}{'Total'}{'Driver Memory (MB)'} = $average / ( 1024 * 1024 ); } elsif( $counter_name =~ /System Driver Resident Bytes/ ) { - $stats_ref->{'Driver Resident Memory (MB)'} = $average / ( 1024 * 1024 ); + $stats_ref->{'Measurements'}{'Total'}{'Driver Resident Memory (MB)'} = $average / ( 1024 * 1024 ); } else { diff --git a/lib/Power.pm b/lib/Power.pm index 362d76c..15dc841 100644 --- a/lib/Power.pm +++ b/lib/Power.pm @@ -122,7 +122,7 @@ sub parse() $average = $sum / $count; } - $stats_ref->{'System Power (W)'} = $average; + $stats_ref->{'Measurements'}{'Total'}{'System Power (W)'} = $average; return 1; } diff --git a/lib/PreconditionRunner.pm b/lib/PreconditionRunner.pm index b3b9d11..abf6ae8 100644 --- a/lib/PreconditionRunner.pm +++ b/lib/PreconditionRunner.pm @@ -30,6 +30,7 @@ use strict; use warnings; use Moose; use Util; +use DiskSpdRunner; has 'target' => ( is => 'ro', @@ -95,12 +96,53 @@ sub write_num_passes sub run_to_steady_state($) { my $self = shift; - my %args = @_; my $msg_prefix = $args{'msg_prefix'}; - my $output_dir = $args{'output_dir'}; my $test_ref = $args{'test_ref'}; + my $output_dir = $args{'output_dir'}; + + # preconditioner.exe does not support multiple files + # Future work: implement preconditioning in diskspd + # and consolidate this + if( $test_ref->{'target_count'} > 1 ) + { + $self->run_with_diskspd( $msg_prefix, $test_ref, $output_dir ); + } + else + { + $self->run_with_preconditioner( $msg_prefix, $test_ref, $output_dir ); + } +} + +sub run_with_diskspd($$$) +{ + my $self = shift; + my $msg_prefix = shift; + my $test_ref = shift; + my $output_dir = shift; + + my $run_time = '3600'; + $run_time = '10' if ( $self->cmd_line->demo_mode ); + $run_time = '0' if ( $pretend ); + + print $msg_prefix . "static-length ($run_time sec)\n"; + + my $iogen = DiskSpdRunner->new( + target => $self->target, + cmd_line => $self->cmd_line, + output_dir => $output_dir + ); + + $iogen->run( $test_ref, 'precondition' ); +} + +sub run_with_preconditioner($$$) +{ + my $self = shift; + my $msg_prefix = shift; + my $test_ref = shift; + my $output_dir = shift; my $write_percentage = $test_ref->{'write_percentage'} // die; my $access_pattern = $test_ref->{'access_pattern'} // die; diff --git a/lib/Recipe.pm b/lib/Recipe.pm index 66a3232..7bb8c90 100644 --- a/lib/Recipe.pm +++ b/lib/Recipe.pm @@ -147,7 +147,13 @@ sub try_legalize_arguments } } - # N.B.: You can return zero here to skip this test + # skip multitarget tests that are ill-defined + if( $step_ref->{'target_count'} > 1 ) + { + return 0 unless $step_ref->{'xml_profile'}; + return 0 unless $self->cmd_line->io_generator eq 'diskspd'; + return 0 if $self->cmd_line->raw_disk; + } return 1; } @@ -183,6 +189,37 @@ sub warn_illegal_args warn "$msg\n\n"; } } + + # warn about poorly-defined multitarget tests + if( $step_ref->{'target_count'} > 1 ) + { + unless( $step_ref->{'xml_profile'} ) + { + my $msg = "\tWarning!\n"; + $msg .= "\tRecipe defines multiple targets and no xml profile.\n"; + $msg .= "\tSkipping step ". $self->current_step . "."; + + warn "$msg\n\n"; + } + + unless( $self->cmd_line->io_generator eq 'diskspd' ) + { + my $msg = "\tWarning!\n"; + $msg .= "\tMultitarget tests must use Diskspd.\n"; + $msg .= "\tSkipping step ". $self->current_step . "."; + + warn "$msg\n\n"; + } + + if( $self->cmd_line->raw_disk ) + { + my $msg = "\tWarning!\n"; + $msg .= "\tMultitarget tests cannot run without a file system.\n"; + $msg .= "\tSkipping step ". $self->current_step . "."; + + warn "$msg\n\n"; + } + } } sub canonicalize_step @@ -194,12 +231,16 @@ sub canonicalize_step return unless $kind ~~ [qw( test precondition )]; + $step_ref->{'target_count'} = $step_ref->{'target_count'} // 1; + # create read_percentage from write_percentage $step_ref->{'read_percentage'} = - 100 - $step_ref->{'write_percentage'}; + 100 - $step_ref->{'write_percentage'} + unless $step_ref->{'target_count'} > 1; # ensure description can be used as a unique filename - $step_ref->{'description'} = make_legal_filename( $step_ref->{'description'}); + $step_ref->{'description'} = + make_legal_filename( $step_ref->{'description'}); } sub apply_overrides @@ -210,13 +251,15 @@ sub apply_overrides my $kind = $step_ref->{'kind'}; - return unless $kind eq 'test'; + return unless $kind eq 'test' or 'precondition'; if( defined $self->cmd_line->test_time_override ) { $step_ref->{'run_time'} = $self->cmd_line->test_time_override; } + return unless $kind eq 'test'; + if( defined $self->cmd_line->warmup_time_override ) { $step_ref->{'warmup_time'} = $self->cmd_line->warmup_time_override; @@ -258,7 +301,7 @@ sub handle_step my $step_number = $self->current_step; my %step = make_step( $kind, $step_number, @step_args ); - + canonicalize_step( \%step ); $self->apply_overrides( \%step ); $self->warn_illegal_args( \%step ) if $do_warnings; @@ -283,17 +326,18 @@ sub handle_step $self->advance_current_step(); } - + return $scalar_retval if $context eq 'scalar'; return @list_retval if $context eq 'list'; } -sub generate_header($$$$) +sub generate_header($$$$$) { my $file_name = shift; my $package = shift; my $be_permissive = shift; - my $be_quiet = shift; + my $quiet_stderr = shift; + my $quiet_stdout = shift; my $header = <<"HEADER"; package $package; @@ -322,12 +366,15 @@ HEADER $header .= "no warnings 'uninitialized';\n"; } - if( $be_quiet ) + # Replace STDOUT and STDERR with NUL + if( $quiet_stderr ) { - # Replace STDOUT and STDERR with NUL - $header .= "local *STDOUT; open STDOUT, '>NUL';\n"; $header .= "local *STDERR; open STDERR, '>NUL';\n"; } + if( $quiet_stdout ) + { + $header .= "local *STDOUT; open STDOUT, '>NUL';\n"; + } # Improve the quality of diagnostic messages $header .= qq(\n# line 1 "$file_name"\n); @@ -354,7 +401,8 @@ sub execute_recipe my $recipe_warnings = $args{'recipe_warnings'} // 0; my $permissive_perl = $args{'permissive_perl'} // 0; - my $quiet_stdio = $args{'quiet_stdio'} // 0; + my $quiet_stderr = $args{'quiet_stderr'} // 0; + my $quiet_stdout = $args{'quiet_stdout'} // 0; state $eval_count = 0; @@ -414,7 +462,8 @@ sub execute_recipe $file_name, $package, $permissive_perl, - $quiet_stdio + $quiet_stderr, + $quiet_stdout ); $eval_string .= $recipe_str; @@ -445,7 +494,8 @@ sub execute_recipe $self->file_name, $package, $permissive_perl, - $quiet_stdio + $quiet_stderr, + $quiet_stdout ); $eval_string .= $self->recipe_string; @@ -466,22 +516,22 @@ sub execute_recipe sub check_unique_test_descriptions { - my $self = shift; + my $self = shift; - my %desc_hash = (); + my %desc_hash = (); - foreach my $step (@{$self->steps}) - { - - my $desc = $step->{'description'}; - if ( $desc && $step->{'kind'} eq "test" ) - { - die "Test description '$desc' is not unique" - if ( $desc_hash{ $desc } ); - $desc_hash{ $desc } = 1; - } - } + foreach my $step (@{$self->steps}) + { + + my $desc = $step->{'description'}; + if ( $desc && $step->{'kind'} eq "test" ) + { + die "Test description '$desc' is not unique" + if ( $desc_hash{ $desc } ); + $desc_hash{ $desc } = 1; + } + } } sub get_num_steps @@ -583,7 +633,7 @@ sub get_test_macro_string $str .= q( purge() if $args{'purge'} // 1; ) if $self->target->do_purge; - $str .= q( initialize() if $args{'initialize'} // 1; ) + $str .= q( initialize( %args ) if $args{'initialize'} // 1; ) if $self->target->do_initialize; $str .= q( precondition( %args ) if $args{'precondition'} // 1; ) @@ -641,7 +691,8 @@ sub BUILD $self->execute_recipe( recipe_warnings => 1, permissive_perl => 1, - quiet_stdio => 1, + quiet_stderr => 0, + quiet_stdout => 1, callback => sub { push @{$self->steps}, {@_}; } ); } @@ -687,20 +738,29 @@ sub get_announcement_message my $step_ref = shift; my $msg; - my $command = $step_ref->{'command'}; + my $command = $step_ref->{'command'}; if( $step_ref->{'kind'} eq 'test' ) { - my $short_pattern = - $step_ref->{'access_pattern'} eq "random" ? "rnd" : "seq"; - - $msg = sprintf( " %4s, %3s, %3d%% read, %3d%% wri, QD=%3d", - $step_ref->{'block_size'}, - $short_pattern, - $step_ref->{'read_percentage'}, - $step_ref->{'write_percentage'}, - $step_ref->{'queue_depth'} - ); + if( $step_ref->{'target_count'} > 1 ) + { + $msg = $step_ref->{'target_count'} . + "-target test using profile " . + $step_ref->{'xml_profile'}; + } + else + { + my $short_pattern = + $step_ref->{'access_pattern'} eq "random" ? "rnd" : "seq"; + + $msg = sprintf( " %4s, %3s, %3d%% read, %3d%% wri, QD=%3d", + $step_ref->{'block_size'}, + $short_pattern, + $step_ref->{'read_percentage'}, + $step_ref->{'write_percentage'}, + $step_ref->{'queue_depth'} + ); + } } elsif( $step_ref->{'kind'} eq 'idle' ) { @@ -788,6 +848,7 @@ sub run_step { $self->target->initialize( msg_prefix => $progress, + test_ref => $step_ref ); } elsif( $kind eq 'precondition' ) @@ -800,7 +861,7 @@ sub run_step } elsif( $kind eq 'test' ) { - $self->target->prepare() + $self->target->prepare( $step_ref ) unless $self->target->is_prepared(); unless( $pretend or $self->cmd_line->raw_disk ) diff --git a/lib/SmartCtlParser.pm b/lib/SmartCtlParser.pm index b767a2f..7a83184 100644 --- a/lib/SmartCtlParser.pm +++ b/lib/SmartCtlParser.pm @@ -148,15 +148,15 @@ sub parse_attributes($$$) if( $attribute_id == $host_writes ) { - $stats_ref->{"Host Writes$suffix"} = $raw_value; + $stats_ref->{'Measurements'}{'Total'}{"Host Writes$suffix"} = $raw_value; } elsif( $attribute_id == $ctrl_writes ) { - $stats_ref->{"Controller Writes$suffix"} = $raw_value; + $stats_ref->{'Measurements'}{'Total'}{"Controller Writes$suffix"} = $raw_value; } elsif( $attribute_id == $wear_range ) { - $stats_ref->{"Wear Range$suffix"} = $raw_value; + $stats_ref->{'Measurements'}{'Total'}{"Wear Range$suffix"} = $raw_value; } } diff --git a/lib/SqlioParser.pm b/lib/SqlioParser.pm index ff65836..aadf842 100644 --- a/lib/SqlioParser.pm +++ b/lib/SqlioParser.pm @@ -182,10 +182,11 @@ sub parse($$) process_percentiles( $stats_ref ); } -sub parse_cmd_line($$) +sub parse_workload($$$) { my $self = shift; + my $dir = shift; my $cmd_line = shift; my $stats_ref = shift; diff --git a/lib/StorageToolParser.pm b/lib/StorageToolParser.pm index 8b7f2da..8f6da50 100644 --- a/lib/StorageToolParser.pm +++ b/lib/StorageToolParser.pm @@ -162,7 +162,7 @@ sub parse_attributes($$$) { do_simple_extract( $line, - $stats_ref, + $stats_ref->{'Measurements'}{'Total'}, \@extract_rules_log_entries, suffix => $suffix ); } diff --git a/lib/Target.pm b/lib/Target.pm index f9d076d..80dbf24 100644 --- a/lib/Target.pm +++ b/lib/Target.pm @@ -369,6 +369,7 @@ sub purge sub prepare { my $self = shift; + my $test_ref = shift; return if $self->is_prepared; @@ -397,21 +398,30 @@ sub prepare # Support testing less then 100% of the disk $size = int( $size * $self->cmd_line->active_range / 100 ); - # Round to an even increment of 2MB. - # Idea is to ensure the file size is an even multiple - # of pretty much any block size we might test. - $size = int( $size / BYTES_IN_2MB ) * BYTES_IN_2MB; + my $file_count = $test_ref->{'target_count'} // 1; - $self->_file_name( $self->volume . "\\$TEST_FILE_NAME" ); + # Divide active range evenly among all files + $size = int( $size / $file_count ); - if( -e $self->file_name and not $pretend ) + for my $file_num ( 1 .. $file_count ) { - die "Error: target file " . $self->file_name . " exists!\n"; + # Round to an even increment of 2MB. + # Idea is to ensure the file size is an even multiple + # of pretty much any block size we might test. + $size = int( $size / BYTES_IN_2MB ) * BYTES_IN_2MB; + + my $file_name = $self->volume . "\\$TEST_FILE_STUB$file_num.dat"; + $self->_file_name( $file_name ); + + if( -e $self->file_name and not $pretend ) + { + die "Error: target file " . $self->file_name . " exists!\n"; + } + + fast_create_file( $self->file_name, $size ) + or die "Couldn't create $size byte file: " . + $self->file_name . "\n"; } - - fast_create_file( $self->file_name, $size ) - or die "Couldn't create $size byte file: " . - $self->file_name . "\n"; } if( defined $self->volume ) @@ -476,7 +486,7 @@ sub initialize if( defined $test_ref ) { # Future work: allow for custom init pattern - ... + # ... } unless( $self->do_initialize ) @@ -497,7 +507,7 @@ sub initialize return; } - $self->prepare() unless $self->is_prepared(); + $self->prepare( $test_ref ) unless $self->is_prepared(); $self->precondition_runner->write_num_passes( msg_prefix => $msg_prefix . "Initializing: ", @@ -515,7 +525,7 @@ sub precondition my $msg_prefix = $args{'msg_prefix'} // die; my $output_dir = $args{'output_dir'} // die; my $test_ref = $args{'test_ref'} // die; - + unless( $self->do_precondition ) { my $skip_requested = @@ -534,8 +544,8 @@ sub precondition return; } - $self->prepare() unless $self->is_prepared(); - + $self->prepare( $test_ref ) unless $self->is_prepared(); + $self->precondition_runner->run_to_steady_state( msg_prefix => $msg_prefix . "Preconditioning: ", output_dir => $output_dir, diff --git a/lib/Util.pm b/lib/Util.pm index 0520246..e7790cd 100644 --- a/lib/Util.pm +++ b/lib/Util.pm @@ -43,6 +43,22 @@ use Win32::API; use Win32::Process; use Encode; +use Module::Load::Conditional 'check_install'; + +if( check_install( module => 'XML::LibXML' ) ) +{ + # require runs at execution time, whereas use runs at compile time + require XML::LibXML; +} +else +{ + warn "The XML::LibXML package from CPAN is required and not installed.\n"; + warn "Please install it via command line:\n"; + warn "\t\$> ppm install XML::LibXML \\\\ for ActiveState perl installs\n"; + warn "\t\$> cpan XML::LibXML \\\\ for Strawberry perl installs\n"; + exit( -1 ); +} + no if $PERL_VERSION >= 5.017011, warnings => 'experimental::smartmatch'; @@ -59,7 +75,7 @@ use vars qw(@ISA @EXPORT); BYTES_PER_KB_BASE10 BYTES_PER_SECTOR BYTES_IN_2MB - $TEST_FILE_NAME + $TEST_FILE_STUB $script_name $script_dir $recipes_dir @@ -135,7 +151,7 @@ use constant BYTES_PER_KB_BASE10 => 1000; use constant BYTES_PER_SECTOR => 512; use constant BYTES_IN_2MB => 1024 * 1024 * 2; -our $TEST_FILE_NAME = 'testfile.dat'; +our $TEST_FILE_STUB = 'testfile'; our $script_name = basename( $PROGRAM_NAME, ".cmd" ); our $script_dir = dirname( $PROGRAM_NAME ); @@ -333,7 +349,7 @@ sub human_to_bytes($) return ( $human << 10 ) if $human =~ s/(\d+)\s*KB?$/$1/i; return $human if $human =~ s/(\d+)\s*B?$/$1/i; - die "Cannot parse the human-readable value"; + die "Cannot parse the human-readable value, \"$human\""; } sub human_to_kilobytes($) @@ -500,7 +516,7 @@ sub detect_scep_and_warn() You have the following options: - Run on a machine without SCEP - Disable SCEP real-time protection - - Exclude $TEST_FILE_NAME from SCEP scan. + - Exclude $TEST_FILE_STUB n.dat from SCEP scan. WARNING } diff --git a/parse_results.cmd b/parse_results.cmd index e1459ad..9528f7a 100644 --- a/parse_results.cmd +++ b/parse_results.cmd @@ -129,8 +129,9 @@ EXAMPLES END_USAGE } -sub parse_warmup_file($$) +sub parse_warmup_file($$$) { + my $dir = shift; my $file_name = shift; my $stats_ref = shift; @@ -138,7 +139,7 @@ sub parse_warmup_file($$) my %warmup_stats; - parse_test_file( $file_name, \%warmup_stats ); + parse_test_file( $dir, $file_name, \%warmup_stats ); # Inject into the main stats hash with "Warmup" prefix foreach my $key ( keys %warmup_stats ) @@ -179,8 +180,9 @@ sub parse_background_file($$) close $LOG; } -sub parse_test_file($$) +sub parse_test_file($$$) { + my $dir = shift; my $file_name = shift; my $stats_ref = shift; @@ -208,57 +210,64 @@ sub parse_test_file($$) die "Unknown IO generator\n"; } - $iogen->parse_cmd_line( $cmd_line, $stats_ref ); + $iogen->parse_workload( $dir, $cmd_line, $stats_ref ); $iogen->parse( $LOG, $stats_ref ); close $LOG; } -sub compute_rw_amounts($) +sub compute_rw_amounts($$) { - my $stats_ref = shift; + my $workload_stats = shift; + my $measurement_stats = shift; - my $total_IOs = $stats_ref->{'IOs Total'} // 0; - my $total_GB = $total_IOs * $stats_ref->{'Access Size'} / KB_PER_GB; - $stats_ref->{'GB Total'} = $total_GB; - - my $read_frac = $stats_ref->{'R Mix'} / 100; - my $write_frac = $stats_ref->{'W Mix'} / 100; + # aggregate data from multitarget test does not have one IO size + my $total_IOs = $measurement_stats->{'IOs Total'} // 0; + my $io_size = $workload_stats->{'Access Size'} // 0; + my $total_GB = $total_IOs * $io_size / KB_PER_GB; + + $measurement_stats->{'GB Total'} = $total_GB; + + my $read_frac = $workload_stats->{'R Mix'} / 100; + my $write_frac = $workload_stats->{'W Mix'} / 100; # The following are *estimates* (not measured) # If the workload generator measures this directly, do not overwrite - unless( exists $stats_ref->{'IOs Read'} ) + unless( exists $measurement_stats->{'IOs Read'} ) { - $stats_ref->{'IOs Read'} = $total_IOs * $read_frac; - $stats_ref->{'Notes'} .= "IOs Read is an estimate; "; + $measurement_stats->{'IOs Read'} = $total_IOs * $read_frac; + $measurement_stats->{'Notes'} .= "IOs Read is an estimate; "; } - unless( exists $stats_ref->{'IOs Write'} ) + unless( exists $measurement_stats->{'IOs Write'} ) { - $stats_ref->{'IOs Write'} = $total_IOs * $write_frac; - $stats_ref->{'Notes'} .= "IOs Write is an estimate; "; + $measurement_stats->{'IOs Write'} = $total_IOs * $write_frac; + $measurement_stats->{'Notes'} .= "IOs Write is an estimate; "; } - unless( exists $stats_ref->{'GB Read'} ) + unless( exists $measurement_stats->{'GB Read'} ) { - $stats_ref->{'GB Read'} = $total_GB * $read_frac; - $stats_ref->{'Notes'} .= "GB Read is an estimate; "; + $measurement_stats->{'GB Read'} = $total_GB * $read_frac; + $measurement_stats->{'Notes'} .= "GB Read is an estimate; "; } - unless( exists $stats_ref->{'GB Write'} ) + unless( exists $measurement_stats->{'GB Write'} ) { - $stats_ref->{'GB Write'} = $total_GB * $write_frac; - $stats_ref->{'Notes'} .= "GB Write is an estimate; "; + $measurement_stats->{'GB Write'} = $total_GB * $write_frac; + $measurement_stats->{'Notes'} .= "GB Write is an estimate; "; } + } -sub compute_steady_state_error_and_warn($) +sub compute_steady_state_error_and_warn($$$) { my $stats_ref = shift; + my $warmup_stats = shift; + my $measurement_stats = shift; - my $warmup_IOPS = $stats_ref->{'Warmup IOPS Total'}; - my $test_IOPS = $stats_ref->{'IOPS Total'}; + my $warmup_IOPS = $warmup_stats->{'IOPS Total'}; + my $test_IOPS = $measurement_stats->{'IOPS Total'}; return unless defined $warmup_IOPS; @@ -278,7 +287,7 @@ sub compute_steady_state_error_and_warn($) $stats_ref->{'Notes'} .= "No steady-state?; "; } - $stats_ref->{'Steady-State Error'} = $rel_diff; + $measurement_stats->{'Steady-State Error'} = $rel_diff; } sub get_timestamp_from_smart_file($) @@ -338,7 +347,9 @@ my @cols = { name => 'Display Name'}, { name => 'User Capacity (GB)' }, { name => 'Partition Size (GB)' }, + { name => 'Raw Disk' }, { name => 'Test Description' }, + { name => 'Target' }, { name => 'Timestamp', format => 'mm/dd/yyyy HH:mm:ss', @@ -365,6 +376,7 @@ my @cols = }, { name => 'Alignment', + type => 'io_pattern' }, { name => 'Access Type', @@ -684,6 +696,7 @@ sub parse_directories(@) generate_timestamp( $base_name, \%file_stats ); parse_warmup_file( + $dir, "warmup-$base_name.txt", \%file_stats ); @@ -694,13 +707,25 @@ sub parse_directories(@) ); parse_test_file( + $dir, $test_file_name, \%file_stats ); - compute_rw_amounts( \%file_stats ); - - compute_steady_state_error_and_warn( \%file_stats ); + foreach my $workload ( keys $file_stats{'Workloads'} ) + { + compute_rw_amounts( + $file_stats{'Workloads'}{$workload}, + $file_stats{'Measurements'}{$workload} + ); + + compute_steady_state_error_and_warn( + \%file_stats, + $file_stats{'Warmup Measurements'}{$workload}, + $file_stats{'Measurements'}{$workload} + ); + + } if( $try_precondition ) { @@ -780,11 +805,11 @@ sub parse_directories(@) } # When extracting, we store a statistic's value directly in a hash. -# Later, we might want to track other information related to that +# Later, we track other information related to that # statistic. For example, outlier status, or formatting rules. To # make this possible, but still enable straight-forward extraction, # we run this helper function post-extraction to create a 'value' -# namespace, making room for other things. +# namespace and to make room for other things. sub inject_value_namespace($) { # BEFORE: @@ -803,10 +828,26 @@ sub inject_value_namespace($) foreach my $key ( keys %$stats_ref ) { - my $value = $stats_ref->{$key}; - delete $stats_ref->{$key}; + if( $key =~ /Measurements/ or $key =~ /Workloads/ ) + { + foreach my $target ( keys $stats_ref->{$key} ) + { + foreach my $sub_key ( keys $stats_ref->{$key}{$target} ) + { + my $value = $stats_ref->{$key}{$target}{$sub_key}; + delete $stats_ref->{$key}{$target}{$sub_key}; - $stats_ref->{$key}{'value'} = $value; + $stats_ref->{$key}{$target}{$sub_key}{'value'} = $value; + } + } + } + else + { + my $value = $stats_ref->{$key}; + delete $stats_ref->{$key}; + + $stats_ref->{$key}{'value'} = $value; + } } } @@ -831,7 +872,7 @@ sub build_test_description_to_io_pattern_hash(@) next if defined $test_description_to_io_pattern{$test_desc}; my %fields = - map { $_ => $stats_ref->{$_} } get_io_pattern_cols(); + map { $_ => $stats_ref->{'Workloads'}{'Total'}{$_} } get_io_pattern_cols(); $test_description_to_io_pattern{$test_desc} = \%fields } @@ -859,11 +900,11 @@ sub inject_default_compressibility(@) foreach my $stats_ref ( @all_stats ) { my $directory = $stats_ref->{'Directory'}; - my $compress = $stats_ref->{'Compressibility'}; + my $compress = $stats_ref->{'Workloads'}{'Total'}{'Compressibility'}; $all_compress{$directory}{$compress}++; } - + my %default_compress; foreach my $directory ( keys %all_compress ) @@ -1203,7 +1244,7 @@ sub get_matching_values($$\@) return grep { defined $_ } # prune undefined vals - map { $_->{ $col_name }{'value'} } @matching; + map { $_->{'Measurements'}{'Total'}{ $col_name }{'value'} } @matching; } sub is_good_outlier($$$$) @@ -1234,9 +1275,8 @@ sub is_bad_outlier($$$$) ); } -sub find_outliers($\@) +sub find_outliers(\@) { - my $workbook = shift; my @all_stats = @{+shift}; my %outliers; @@ -1261,7 +1301,7 @@ sub find_outliers($\@) foreach my $test_stats ( filter_by_test_description( $test_desc, @all_stats ) ) { - my $val = $test_stats->{$col_name}{'value'}; + my $val = $test_stats->{'Measurements'}{'Total'}{$col_name}{'value'}; next unless defined $val; @@ -1368,46 +1408,60 @@ sub generate_raw_data_sheet($\@) foreach my $test_stats ( @all_stats ) { - my $col_num = 0; - - foreach my $col ( @cols ) + foreach my $target ( keys $test_stats->{'Workloads'} ) { - my $col_name = $col->{'name'}; - my $format_str = $col->{'format'}; - - my $value = $test_stats->{$col_name}{'value'}; - - if( defined $col->{'protect'} ) - { - $value = protect_excel_string( $value ); - } + my $col_num = 0; - # Convert Time::Piece object to Excel date format - if( ref $value eq 'Time::Piece' ) + foreach my $col ( @cols ) { - $value = unix_date_to_excel_date( $value->epoch ) - } + my $col_name = $col->{'name'}; + my $format_str = $col->{'format'}; + + my $value = $test_stats->{$col_name}{'value'}; + + # special-case entries + $value = $target if $col_name eq 'Target'; + $value = $test_stats->{'Warmup Measurements'}{$target}{'MB/sec Total'}{'value'} + if $col_name eq 'Warmup MB/sec Total'; + + # entries in the workload and measurement sub-arrays + $value = $test_stats->{'Workloads'}{$target}{$col_name}{'value'} + if exists $test_stats->{'Workloads'}{$target}{$col_name}; + $value = $test_stats->{'Measurements'}{$target}{$col_name}{'value'} + if exists $test_stats->{'Measurements'}{$target}{$col_name}; + + if( defined $col->{'protect'} ) + { + $value = protect_excel_string( $value ); + } - my $format_obj; + # Convert Time::Piece object to Excel date format + if( ref $value eq 'Time::Piece' ) + { + $value = unix_date_to_excel_date( $value->epoch ) + } - if( defined $format_str ) - { - $format_obj = - get_format_obj( $workbook, $test_stats, $col_name ); + my $format_obj; - $format_obj->set_num_format( $format_str ); - - # Our percentages are 0 to 100. Excel prefers 0.0 to 1.0. - $value /= 100 if defined $value and $format_str =~ /%/; + if( defined $format_str ) + { + $format_obj = + get_format_obj( $workbook, $test_stats, $col_name ); - } + $format_obj->set_num_format( $format_str ); + + # Our percentages are 0 to 100. Excel prefers 0.0 to 1.0. + $value /= 100 if defined $value and $format_str =~ /%/; - $raw_sheet->write( $row_num, $col_num, $value, $format_obj ); + } - $col_num++; - } + $raw_sheet->write( $row_num, $col_num, $value, $format_obj ); + + $col_num++; + } - $row_num++; + $row_num++; + } } $raw_sheet->autofilter( 0, 0, 0, $#cols ); @@ -1467,7 +1521,7 @@ sub extract_raw_scores(\@) my $metric = $col->{'name'}; - my $value = $test_stats->{$metric}{'value'}; + my $value = $test_stats->{'Measurements'}{'Total'}{$metric}{'value'}; # Every metric doesn't make sense for every test $raw_scores{$test_desc}{$metric}{$device_id} = $value @@ -1642,7 +1696,7 @@ sub compute_weights_as_fractions(\%) foreach my $test_desc ( @unique_test_descriptions ) { my %io_pattern = %{$test_description_to_io_pattern{$test_desc}}; - + foreach my $metric ( @metrics ) { $total_weight += $weight_func->( $metric, %io_pattern ); @@ -2013,15 +2067,21 @@ die "Nothing to do\n" unless scalar @all_stats > 0; post_process_stats( @all_stats ); +generate_raw_data_sheet( $workbook, @all_stats ); + +# process the data for outliers and scores print "Detecting outliers...\n"; -my %outliers = find_outliers( $workbook, @all_stats ); +my %outliers = find_outliers( @all_stats ); -print "Computing scores...\n"; +print "Writing XLSX file...\n"; +hilight_outliers( $workbook, %outliers, @all_stats ); -my $scores_invalid = 0; +print "Computing scores...\n"; my %raw_scores = extract_raw_scores( @all_stats ); +my $scores_invalid = 0; + unless( scores_hash_is_valid( %raw_scores ) ) { warn "Error while generating raw scores\n"; @@ -2045,8 +2105,6 @@ unless( scores_hash_is_valid( %normalized_scores ) ) warn "Error while generating normalized scores\n"; $scores_invalid = 1; } - -warn "Scores sheets will not be generated\n" if $scores_invalid; # Scoring policies compute a weight with arbitrary range. # Here we convert to a fractional weight between 0 and 1. @@ -2061,10 +2119,6 @@ my %final_scores = combine_scores( %weighted_score_components ); keys %final_scores == $num_input_dirs or warn "Warning: could not generate score for 1+ directories.\n"; -print "Writing XLSX file...\n"; -hilight_outliers( $workbook, %outliers, @all_stats ); -generate_raw_data_sheet( $workbook, @all_stats ); - generate_scores_sheets( $workbook, %raw_scores, @@ -2076,6 +2130,8 @@ generate_scores_sheets( ) unless $scores_invalid; +warn "Scores sheets will not be generated\n" if $scores_invalid; + $workbook->close() or die "Error closing workbook $outfile: $!\n"; print_sanitization_decoder_ring() if $sanitize; diff --git a/recipes/14hr_multitarget.rcp b/recipes/14hr_multitarget.rcp new file mode 100644 index 0000000..4586757 --- /dev/null +++ b/recipes/14hr_multitarget.rcp @@ -0,0 +1,56 @@ +# vim: set filetype=perl: + +# StorScore +# +# Copyright (c) Microsoft Corporation +# +# All rights reserved. +# +# MIT License +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. + +#include 'four_corners.rcp'; + +test( + description => "All Sequential Streams", + target_count => 64, + xml_profile => "64seq.xml", + initialize => 0, + warmup_time => 60, + run_time => 3600, +); + +test( + description => "Single Random Stream", + target_count => 64, + xml_profile => "63seq_1rand.xml", + initialize => 0, + warmup_time => 60, + run_time => 3600, +); + +test( + description => "Single Sequential Stream", + target_count => 64, + xml_profile => "1seq_63rand.xml", + initialize => 0, + warmup_time => 60, + run_time => 3600, +); diff --git a/recipes/xmlProfiles/1seq_63rand.xml b/recipes/xmlProfiles/1seq_63rand.xml new file mode 100644 index 0000000..688e6a7 --- /dev/null +++ b/recipes/xmlProfiles/1seq_63rand.xml @@ -0,0 +1,1490 @@ + + 0 + text + false + + + false + true + false + false + 0 + 0 + 1000 + 0 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + + + diff --git a/recipes/xmlProfiles/63seq_1rand.xml b/recipes/xmlProfiles/63seq_1rand.xml new file mode 100644 index 0000000..688e6a7 --- /dev/null +++ b/recipes/xmlProfiles/63seq_1rand.xml @@ -0,0 +1,1490 @@ + + 0 + text + false + + + false + true + false + false + 0 + 0 + 1000 + 0 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + + + diff --git a/recipes/xmlProfiles/64seq.xml b/recipes/xmlProfiles/64seq.xml new file mode 100644 index 0000000..50edf82 --- /dev/null +++ b/recipes/xmlProfiles/64seq.xml @@ -0,0 +1,1427 @@ + + 0 + text + false + + + false + true + false + false + 0 + 0 + 1000 + 0 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + 4096 + 100 + 1 + 1 + 0 + false + false + false + false + true + true + + random + + + false + 0 + 0 + 0 + 3 + + + + + diff --git a/recipes/xmlProfiles/sample.xml b/recipes/xmlProfiles/sample.xml new file mode 100644 index 0000000..484ab25 --- /dev/null +++ b/recipes/xmlProfiles/sample.xml @@ -0,0 +1,65 @@ + + 0 + text + false + + + false + true + false + false + 0 + 0 + 1000 + 0 + + + 4096 + 0 + false + false + false + false + true + true + + random + + + false + 4096 + 0 + 0 + 1 + 0 + 0 + 2 + 3 + + + 4096 + 0 + false + false + false + false + true + true + + random + + + false + 4096 + 0 + 0 + 1 + 0 + 0 + 2 + 3 + + + + + diff --git a/recipes/xml_sample.rcp b/recipes/xml_sample.rcp new file mode 100644 index 0000000..1352739 --- /dev/null +++ b/recipes/xml_sample.rcp @@ -0,0 +1,46 @@ +# vim: set filetype=perl: + +# StorScore +# +# Copyright (c) Microsoft Corporation +# +# All rights reserved. +# +# MIT License +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. + +test( + description => "Multitarget Sample" + target_count => 2, + xml_profile => "sample.xml", + initialize => 0, + warmup_time => 5, + run_time => 60, +); + +test( + description => "4k Random Reads", + write_percentage => 0, + access_pattern => 'random', + block_size => '4K', + queue_depth => 1, + warmup_time => 10, + run_time => 60, +);