<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://sqlblog.com/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>Search results matching tags 'tools' and 'Testing'</title><link>http://sqlblog.com/search/SearchResults.aspx?o=DateDescending&amp;tag=tools,Testing&amp;orTags=0</link><description>Search results matching tags 'tools' and 'Testing'</description><dc:language>en-US</dc:language><generator>CommunityServer 2.1 SP2 (Build: 61129.1)</generator><item><title>Geek City: Poor (Wo)Man's Load Testing</title><link>http://sqlblog.com/blogs/kalen_delaney/archive/2007/10/24/geek-city-poor-womans-load-testing.aspx</link><pubDate>Wed, 24 Oct 2007 19:28:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:3082</guid><dc:creator>Kalen Delaney</dc:creator><description>&lt;BLOCKQUOTE&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;Last July, &lt;A title="Run a batch multiple times" href="http://sqlblog.com/blogs/kalen_delaney/archive/2007/07/22/did-you-know-run-a-batch-multiple-times.aspx"&gt;I posted about a way to run a batch multiple times&lt;/A&gt; when you use GO as the batch separator. I usually take advantage of this technique when I am loading test data into a table.&lt;/P&gt;
&lt;P&gt;I was mentioning this feature to my class last week in Minneapolis, and one of the students thought that this feature had another use. He thought it would be great to use it to simulate load testing. One connection could execute a procedure and terminate the batch with GO 100 and another connection could be started that did the same thing. You could open up multiple query windows using Management Studio (or Query Analyzer) and have each one run a procedure, or even a statement, over and over again. You could also have different connections run different stored procedures or statements. This will allow you to detect problems with concurrency, which most query tuning techniques do not take into account.&lt;/P&gt;
&lt;P&gt;In the past, I had recommended using the Replay feature of SQL Server Profiler to simulate a "poor man's load testing" environment, but this great idea would make it even easier!&lt;/P&gt;
&lt;P&gt;Of course, for the rich (wo)man, there is also very nice load testing software available. Whether rich or poor, it's crucial to make sure you test your queries and procedures with multiple users. &lt;/P&gt;
&lt;P&gt;Have fun!&lt;/P&gt;
&lt;P&gt;&lt;FONT color=#ff00ff&gt;~Kalen&lt;/FONT&gt;&lt;/P&gt;</description></item><item><title>Parse the sqlio.exe Output</title><link>http://sqlblog.com/blogs/linchi_shea/archive/2007/02/21/parse-the-sqlio-exe-output.aspx</link><pubDate>Thu, 22 Feb 2007 03:12:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:888</guid><dc:creator>Linchi Shea</dc:creator><description>&lt;P&gt;Let's say you run sqlio.exe as follows:&lt;/P&gt;
&lt;DIV style="BACKGROUND-COLOR:#e4e4e4;"&gt;&lt;FONT face="Courier New" size=2&gt;&lt;SPAN style="BACKGROUND-COLOR:#e4e4e4;"&gt;D:\sqlIO -kW -t32 -s30 -dE -o1 -fsequential -b8 -BH -LS Testfile.dat &lt;/SPAN&gt;&lt;/FONT&gt;&lt;/DIV&gt;
&lt;P&gt;&lt;FONT face="Courier New" size=2&gt;The output typically may look like this:&lt;/FONT&gt;&lt;/P&gt;
&lt;DIV style="BACKGROUND-COLOR:#e4e4e4;"&gt;&lt;PRE&gt;sqlio v1.5.SG
using system counter for latency timings, 1999980000 counts per second
32 threads writing for 30 secs to file E:Testfile.dat
	using 8KB sequential IOs
	enabling multiple I/Os per thread with 1 outstanding
	buffering set to use hardware disk cache (but not file cache)
using current size: 20000 MB for file: E:Testfile.dat
initialization done
CUMULATIVE DATA:
throughput metrics:
&lt;B&gt;IOs/sec:  7851.80
MBs/sec:    61.34
&lt;/B&gt;latency metrics:
&lt;B&gt;Min_Latency(ms): 0
Avg_Latency(ms): 3
Max_Latency(ms): 18
&lt;/B&gt;histogram:
ms: 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24+
%:  6  7 12 16 30 19  6  2  1  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0&lt;/PRE&gt;&lt;/DIV&gt;
&lt;P&gt;&lt;FONT face="Courier New" size=2&gt;The data you most likely want to get from the above output include the throughput metrics and the latency metrics (highlighted in boldface). Now, getting the throughput and latency metrics from the above single output is easy. But very rarely you'd just run sqlio.exe once to get a single data point, which in itself would not be very useful. &lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New" size=2&gt;Often, you'd run sqlio.exe in a series with a range of parameter values such as different outstanding I/Os so that you can profile an I/O path. You can write a script to drive sqlio.exe with different parameters. Or, you can accomplish the I/O profiling with a simple batch file such as the following with the outstanding I/Os ranging from 1 through 128:&lt;/FONT&gt;&lt;/P&gt;
&lt;DIV style="BACKGROUND-COLOR:#e4e4e4;"&gt;&lt;FONT face="Courier New" size=2&gt;sqlIO -kW -t32 -s30 -dE -o1 -fsequential -b8 -BH -LS Testfile.dat&lt;BR&gt;sleep 30&lt;BR&gt;sqlIO -kW -t32 -s30 -dE -o2 -fsequential -b8 -BH -LS Testfile.dat&lt;BR&gt;sleep 30&lt;BR&gt;sqlIO -kW -t32 -s30 -dE -o4 -fsequential -b8 -BH -LS Testfile.dat&lt;BR&gt;sleep 30&lt;BR&gt;sqlIO -kW -t32 -s30 -dE -o8 -fsequential -b8 -BH -LS Testfile.dat&lt;BR&gt;sleep 30&lt;BR&gt;sqlIO -kW -t32 -s30 -dE -o16 -fsequential -b8 -BH -LS Testfile.dat&lt;BR&gt;sleep 30&lt;BR&gt;sqlIO -kW -t32 -s30 -dE -o32 -fsequential -b8 -BH -LS Testfile.dat&lt;BR&gt;sleep 30&lt;BR&gt;sqlIO -kW -t32 -s30 -dE -o64 -fsequential -b8 -BH -LS Testfile.dat&lt;BR&gt;sleep 30&lt;BR&gt;sqlIO -kW -t32 -s30 -dE -o128 -fsequential -b8 -BH -LS Testfile.dat&lt;BR&gt;sleep 30&lt;/FONT&gt;&lt;/DIV&gt;
&lt;P&gt;&lt;FONT face="Courier New" size=2&gt;In practice, your batch file may be much longer than this when you want to test, for instance, different I/O paths and different block sizes. In any event, you'll end up with lengthy output and/or have to deal with the output of a batch file multiple times or even many times when you repeat your test to check for data consistency. &lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New" size=2&gt;Manually extracting throughput metrics and latency metrics is simply out of the question.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New" size=2&gt;Fortunately, parsing the sqlio.exe output to extract the metrics data is rather simple with any language that supports regular expressions and facilitates text manipulation. Below is a Perl script I use to parse the sqlio.exe output and dump the metrics data and the sqlio.exe command-line parameter values into a csv formatted text file for import into Excel.&lt;/FONT&gt;&lt;/P&gt;
&lt;DIV style="BACKGROUND-COLOR:#e4e4e4;"&gt;&lt;PRE&gt;&lt;FONT size=2&gt;use strict;

my $logFile = shift or 
    die "***Err: $0 expects a log file on the commend line.\n";

my ($drive, $thread, $rw, $duration, $iotype, $depth, $size, $ref);
open(LOG, $logFile) or 
     die "***Err: caould not open $logFile for reads.\n";
while (&amp;lt;LOG&amp;gt;) {
    if (/sqlio(?:\.exe)?\s+  
                \-k(\w)\s+
                \-t(\d+)\s+
                \-s(\d+)\s+
                \-d(\w+)\s+
                \-o(\d+)\s+
                \-f(\w+)\s+
                \-b(\d+)/ix) {
        ($rw, $thread, $duration, $drive, $depth, $iotype, $size) 
                        = ($1, $2, $3, $4, $5, $6, $7);
        $thread =~ /^\d+$/ or 
            die "***Err: Thread parameter $thread must be a number.\n";
        if ($iotype =~ /(random|sequential)/i) {
            $iotype = ucfirst($iotype);
        }
        else {
            die "***Err: IOType parameter must be random or sequential.\n";
        }
        if ($rw =~/^R/i) {
            $rw = 'Reads';
        }
        elsif ($rw =~ /^W/i) {
            $rw = 'Writes';
        }
        else {
            die "***Err: Read/Write parameter $rw must be R or W.\n";
        }
        
        $size =~ /^\d+$/ or
            die "***Err: Block size parameter $size must be a number.\n";
        $depth =~ /^\d+$/ or
            die "***Err: Queue depth parameter $depth must be a number.\n";
    }                        
    if (/IOs\/sec:\s+([\.\d]+)/i) {
           $ref-&amp;gt;{$drive}-&amp;gt;{$rw}-&amp;gt;{$iotype}-&amp;gt;{$thread}-&amp;gt;{$depth}-&amp;gt;{$size}-&amp;gt;{IOps} 
                       = int($1 + 0.5);
           next;
    }
    if (/MBs\/sec:\s+([\.\d]+)/i) {
           $ref-&amp;gt;{$drive}-&amp;gt;{$rw}-&amp;gt;{$iotype}-&amp;gt;{$thread}-&amp;gt;{$depth}-&amp;gt;{$size}-&amp;gt;{MBps} 
                       = int($1 + 0.5);
           next;
    }
    if (/Avg\_Latency\(ms\):\s+([\.\d]+)/i) {
           $ref-&amp;gt;{$drive}-&amp;gt;{$rw}-&amp;gt;{$iotype}-&amp;gt;{$thread}-&amp;gt;{$depth}-&amp;gt;{$size}-&amp;gt;{Avg_Latency} 
                       = int($1 + 0.5);
           next;
    }
    if (/Min\_Latency\(ms\):\s+([\.\d]+)/i) {
           $ref-&amp;gt;{$drive}-&amp;gt;{$rw}-&amp;gt;{$iotype}-&amp;gt;{$thread}-&amp;gt;{$depth}-&amp;gt;{$size}-&amp;gt;{Min_Latency} 
                       = int($1 + 0.5);
           next;
    }
    if (/Max\_Latency\(ms\):\s+([\.\d]+)/i) {
           $ref-&amp;gt;{$drive}-&amp;gt;{$rw}-&amp;gt;{$iotype}-&amp;gt;{$thread}-&amp;gt;{$depth}-&amp;gt;{$size}-&amp;gt;{Max_Latency} 
                       = int($1 + 0.5);
           next;
    }
}
close(LOG);

foreach my $drive (sort keys %{$ref}) {
   foreach my $rw (sort keys %{$ref-&amp;gt;{$drive}}) {
      foreach my $iotype (sort keys %{$ref-&amp;gt;{$drive}-&amp;gt;{$rw}}) {            &lt;BR&gt;&lt;/FONT&gt;&lt;FONT size=2&gt;        foreach my $thread (sort {$a &amp;lt;=&amp;gt; $b} keys %{$ref-&amp;gt;{$drive}-&amp;gt;{$rw}-&amp;gt;{$iotype}}) {

          print "\n$drive ${size}K $iotype $rw\n";
          print "Type,Threads,Depth,Block_Size,IOps,MBps,Avg_Latency(ms),Min_Latency(ms),Max_Latency(ms)\n";
          foreach my $depth (sort {$a &amp;lt;=&amp;gt; $b} &lt;BR&gt;                                  keys %{$ref-&amp;gt;{$drive}-&amp;gt;{$rw}-&amp;gt;{$iotype}-&amp;gt;{$thread}}) {
             foreach my $size (sort {$a &amp;lt;=&amp;gt; $b} &lt;BR&gt;                                  keys %{$ref-&amp;gt;{$drive}-&amp;gt;{$rw}-&amp;gt;{$iotype}-&amp;gt;{$thread}-&amp;gt;{$depth}}) {            
                print "$iotype,$thread,$depth,${size}K" . 
                   "," . &lt;BR&gt;                   $ref-&amp;gt;{$drive}-&amp;gt;{$rw}-&amp;gt;{$iotype}-&amp;gt;{$thread}-&amp;gt;{$depth}-&amp;gt;{$size}-&amp;gt;{IOps} .
                   "," . &lt;BR&gt;                   $ref-&amp;gt;{$drive}-&amp;gt;{$rw}-&amp;gt;{$iotype}-&amp;gt;{$thread}-&amp;gt;{$depth}-&amp;gt;{$size}-&amp;gt;{MBps} . 
                   "," . &lt;BR&gt;                   $ref-&amp;gt;{$drive}-&amp;gt;{$rw}-&amp;gt;{$iotype}-&amp;gt;{$thread}-&amp;gt;{$depth}-&amp;gt;{$size}-&amp;gt;{Avg_Latency} . 
                   "," . &lt;BR&gt;                   $ref-&amp;gt;{$drive}-&amp;gt;{$rw}-&amp;gt;{$iotype}-&amp;gt;{$thread}-&amp;gt;{$depth}-&amp;gt;{$size}-&amp;gt;{Min_Latency} . 
                   "," . &lt;BR&gt;                   $ref-&amp;gt;{$drive}-&amp;gt;{$rw}-&amp;gt;{$iotype}-&amp;gt;{$thread}-&amp;gt;{$depth}-&amp;gt;{$size}-&amp;gt;{Max_Latency} . &lt;BR&gt;                   "\n";
                }
             }
          }
        }
    }
}&lt;/FONT&gt;&lt;/PRE&gt;&lt;/DIV&gt;
&lt;P&gt;&lt;FONT face="Courier New" size=2&gt;This is really just a throw-away script. If you need to arrange the columns or the rows differently, you should modify the script to suit your own need. Or if you run sqlio.exe with the parameters in different order, you'll have to change the regular expression that parses the sqlio.exe command line. I can't predict your requirements (heck, I can't even forecast my own requirements) to put the script in a canned program. But I have no reason at all to worry about the constantly changing requirements or the circumstances I have not programmed the script to handle, because all I have to do is to change the script as I see the need. No heavy initial investment at all! That's the beauty of using a simple throw-away script like this with the source code in plain view.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New" size=2&gt;The script has saved me a lot of time working with sqlio.exe. Hopefully, you'll find it useful as well if you happen to use sqlio.exe.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;You can download the script from the attachment of this blog.&lt;/P&gt;</description></item></channel></rss>