Stxxl  1.4.0
io/verify_disks.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002  *  io/verify_disks.cpp
00003  *
00004  *  Part of the STXXL. See http://stxxl.sourceforge.net
00005  *
00006  *  Copyright (C) 2002-2003 Roman Dementiev <dementiev@mpi-sb.mpg.de>
00007  *  Copyright (C) 2007-2010 Andreas Beckmann <beckmann@cs.uni-frankfurt.de>
00008  *
00009  *  Distributed under the Boost Software License, Version 1.0.
00010  *  (See accompanying file LICENSE_1_0.txt or copy at
00011  *  http://www.boost.org/LICENSE_1_0.txt)
00012  **************************************************************************/
00013 
00014 /*
00015    example gnuplot command for the output of this program:
00016    (x-axis: disk offset in GiB, y-axis: bandwidth in MiB/s)
00017 
00018    plot \
00019         "disk.log" using ($3/1024):($14) w l title "read", \
00020         "disk.log" using ($3/1024):($7)  w l title "write"
00021  */
00022 
00023 #include <iomanip>
00024 #include <vector>
00025 
00026 #include <stxxl/io>
00027 #include <stxxl/aligned_alloc>
00028 #include <stxxl/timer>
00029 
00030 
00031 using stxxl::request_ptr;
00032 using stxxl::file;
00033 using stxxl::timer;
00034 using stxxl::timestamp;
00035 
00036 
00037 #ifdef BLOCK_ALIGN
00038  #undef BLOCK_ALIGN
00039 #endif
00040 
00041 #define BLOCK_ALIGN  4096
00042 
00043 //#define NOREAD
00044 
00045 //#define DO_ONLY_READ
00046 
00047 #define POLL_DELAY 1000
00048 
00049 #define RAW_ACCESS
00050 
00051 //#define WATCH_TIMES
00052 
00053 
00054 #ifdef WATCH_TIMES
00055 void watch_times(request_ptr reqs[], unsigned n, double * out)
00056 {
00057     bool * finished = new bool[n];
00058     unsigned count = 0;
00059     for (unsigned i = 0; i < n; i++)
00060         finished[i] = false;
00061 
00062     while (count != n)
00063     {
00064         usleep(POLL_DELAY);
00065         unsigned i = 0;
00066         for (i = 0; i < n; i++)
00067         {
00068             if (!finished[i])
00069                 if (reqs[i]->poll())
00070                 {
00071                     finished[i] = true;
00072                     out[i] = timestamp();
00073                     count++;
00074                 }
00075         }
00076     }
00077     delete[] finished;
00078 }
00079 
00080 
00081 void out_stat(double start, double end, double * times, unsigned n, const std::vector<std::string> & names)
00082 {
00083     for (unsigned i = 0; i < n; i++)
00084     {
00085         std::cout << i << " " << names[i] << " took " <<
00086         100. * (times[i] - start) / (end - start) << " %" << std::endl;
00087     }
00088 }
00089 #endif
00090 
00091 #define MB (1024 * 1024)
00092 #define GB (1024 * 1024 * 1024)
00093 
00094 void usage(const char * argv0)
00095 {
00096     std::cout << "Usage: " << argv0 << " pattern offset length [block_size [batch_size]] [nd] [r|w] [--] diskfile..." << std::endl;
00097     std::cout << "    'pattern' is a 32bit unsigned value like '0', '0xffffffff', ..." << std::endl;
00098     std::cout << "    starting 'offset' and 'length' are given in GiB," << std::endl;
00099     std::cout << "    'block_size' (default 8) in MiB (in B if it has a suffix B)," << std::endl;
00100     std::cout << "     increase 'batch_size' (default 1)" << std::endl;
00101     std::cout << "    to submit several I/Os at once and report average rate" << std::endl;
00102 #ifdef RAW_ACCESS
00103     std::cout << "    open mode: includes O_DIRECT unless the 'nd' flag is given" << std::endl;
00104 #endif
00105     std::cout << "    ops: write and reread (default), (r)ead only, (w)rite only" << std::endl;
00106     std::cout << "    length == 0 implies till end of space (please ignore the write error)" << std::endl;
00107     std::cout << "    Memory consumption: block_size * batch_size * num_disks" << std::endl;
00108     exit(-1);
00109 }
00110 
00111 // returns throughput in MiB/s
00112 inline double throughput(double bytes, double seconds)
00113 {
00114     if (seconds == 0.0)
00115         return 0.0;
00116     return bytes / (1024 * 1024) / seconds;
00117 }
00118 
00119 int main(int argc, char * argv[])
00120 {
00121     if (argc < 5)
00122         usage(argv[0]);
00123 
00124     unsigned pattern = strtol(argv[1], NULL, 0);
00125     stxxl::int64 offset = stxxl::int64(GB) * stxxl::int64(atoi(argv[2]));
00126     stxxl::int64 length = stxxl::int64(GB) * stxxl::int64(atoi(argv[3]));
00127     stxxl::int64 endpos = offset + length;
00128     stxxl::int64 block_size = 0;
00129     stxxl::int64 batch_size = 0;
00130 
00131     bool verify_failed = false;
00132     bool do_read = true, do_write = true;
00133     bool direct_io = true;
00134     int first_disk_arg = 4;
00135 
00136     if (first_disk_arg < argc)
00137         block_size = atoi(argv[first_disk_arg]);
00138     if (block_size > 0) {
00139         int l = strlen(argv[first_disk_arg]);
00140         if (argv[first_disk_arg][l - 1] == 'B' || argv[first_disk_arg][l - 1] == 'b') {
00141             // suffix B means exact size
00142         } else {
00143             block_size *= MB;
00144         }
00145         ++first_disk_arg;
00146     } else {
00147         block_size = 8 * MB;
00148     }
00149 
00150     if (first_disk_arg < argc)
00151         batch_size = atoi(argv[first_disk_arg]);
00152     if (batch_size > 0) {
00153         ++first_disk_arg;
00154     } else {
00155         batch_size = 1;
00156     }
00157 
00158     if (first_disk_arg < argc && (strcmp("nd", argv[first_disk_arg]) == 0 || strcmp("ND", argv[first_disk_arg]) == 0)) {
00159         direct_io = false;
00160         ++first_disk_arg;
00161     }
00162 
00163     if (first_disk_arg < argc && (strcmp("r", argv[first_disk_arg]) == 0 || strcmp("R", argv[first_disk_arg]) == 0)) {
00164         do_write = false;
00165         ++first_disk_arg;
00166     } else if (first_disk_arg < argc && (strcmp("w", argv[first_disk_arg]) == 0 || strcmp("W", argv[first_disk_arg]) == 0)) {
00167         do_read = false;
00168         ++first_disk_arg;
00169     }
00170 
00171     if (first_disk_arg < argc && strcmp("--", argv[first_disk_arg]) == 0) {
00172         ++first_disk_arg;
00173     }
00174 
00175     std::vector<std::string> disks_arr;
00176 
00177     if (!(first_disk_arg < argc))
00178         usage(argv[0]);
00179 
00180     for (int ii = first_disk_arg; ii < argc; ii++)
00181     {
00182         std::cout << "# Add disk: " << argv[ii] << std::endl;
00183         disks_arr.push_back(argv[ii]);
00184     }
00185 
00186     const unsigned ndisks = disks_arr.size();
00187 
00188     const stxxl::unsigned_type step_size = block_size * batch_size;
00189     const unsigned block_size_int = block_size / sizeof(int);
00190     const stxxl::int64 step_size_int = step_size / sizeof(int);
00191 
00192     unsigned * buffer = (unsigned *)stxxl::aligned_alloc<BLOCK_ALIGN>(step_size * ndisks);
00193     file ** disks = new file *[ndisks];
00194     request_ptr * reqs = new request_ptr[ndisks * batch_size];
00195 #ifdef WATCH_TIMES
00196     double * r_finish_times = new double[ndisks];
00197     double * w_finish_times = new double[ndisks];
00198 #endif
00199     double totaltimeread = 0, totaltimewrite = 0;
00200     stxxl::int64 totalsizeread = 0, totalsizewrite = 0;
00201 
00202     for (unsigned i = 0; i < ndisks * step_size_int; i++)
00203         buffer[i] = pattern;
00204 
00205     for (unsigned i = 0; i < ndisks; i++)
00206     {
00207         int openmode = file::CREAT | file::RDWR;
00208         if (direct_io) {
00209 #ifdef RAW_ACCESS
00210             openmode |= file::DIRECT;
00211 #endif
00212         }
00213 
00214 #ifdef BOOST_MSVC
00215         disks[i] = new stxxl::wincall_file(disks_arr[i], openmode, i);
00216 #else
00217         disks[i] = new stxxl::syscall_file(disks_arr[i], openmode, i);
00218 #endif
00219     }
00220 
00221 #ifdef DO_ONLY_READ
00222     do_write = false;
00223 #endif
00224 #ifdef NOREAD
00225     do_read = false;
00226 #endif
00227 
00228     const char * myrev = "$Revision$";
00229     const char * myself = strrchr(argv[0], '/');
00230     if (!myself || !*(++myself))
00231         myself = argv[0];
00232     std::cout << "# " << myself << " " << myrev << std::endl;
00233     std::cout << "# Step size: "
00234               << step_size << " bytes per disk ("
00235               << batch_size << " block" << (batch_size == 1 ? "" : "s") << " of "
00236               << block_size << " bytes)  pattern=0x"
00237               << std::hex << pattern << std::dec << std::endl;
00238     timer t_total(true);
00239     try {
00240         while (offset < endpos)
00241         {
00242             const stxxl::int64 current_step_size = std::min<stxxl::int64>(step_size, endpos - offset);
00243             const stxxl::int64 current_step_size_int = current_step_size / sizeof(int);
00244             const unsigned current_num_blocks = stxxl::div_ceil(current_step_size, block_size);
00245 
00246             std::cout << "Disk offset    " << std::setw(8) << offset / MB << " MiB: " << std::fixed;
00247 
00248             double begin = timestamp(), end, elapsed;
00249 
00250             if (do_write) {
00251                 // write block number (512 byte blocks) into each block at position 42 * sizeof(unsigned)
00252                 for (unsigned j = 42, b = offset >> 9; j < current_step_size_int; j += 512 / sizeof(unsigned), ++b)
00253                 {
00254                     for (unsigned i = 0; i < ndisks; i++)
00255                         buffer[current_step_size_int * i + j] = b;
00256                 }
00257                 for (unsigned i = 0; i < ndisks; i++)
00258                 {
00259                     for (unsigned j = 0; j < current_num_blocks; j++)
00260                         reqs[i * current_num_blocks + j] =
00261                             disks[i]->awrite(buffer + current_step_size_int * i + j * block_size_int,
00262                                              offset + j * block_size,
00263                                              block_size,
00264                                              stxxl::default_completion_handler());
00265                 }
00266 
00267  #ifdef WATCH_TIMES
00268                 watch_times(reqs, ndisks, w_finish_times);
00269  #else
00270                 wait_all(reqs, ndisks * current_num_blocks);
00271  #endif
00272 
00273                 end = timestamp();
00274                 elapsed = end - begin;
00275                 totalsizewrite += current_step_size;
00276                 totaltimewrite += elapsed;
00277             } else {
00278                 elapsed = 0.0;
00279             }
00280 
00281 #if 0
00282             std::cout << "WRITE\nDisks: " << ndisks
00283                       << " \nElapsed time: " << end - begin
00284                       << " \nThroughput: " << int(double(current_step_size * ndisks) / MB / (end - begin))
00285                       << " MiB/s \nPer one disk:"
00286                       << int(double(current_step_size) / MB / (end - begin)) << " MiB/s"
00287                       << std::endl;
00288 #endif
00289 
00290  #ifdef WATCH_TIMES
00291             out_stat(begin, end, w_finish_times, ndisks, disks_arr);
00292  #endif
00293             std::cout << std::setw(2) << ndisks << " * "
00294                       << std::setw(8) << std::setprecision(3) << (throughput(current_step_size, elapsed)) << " = "
00295                       << std::setw(8) << std::setprecision(3) << (throughput(current_step_size, elapsed) * ndisks) << " MiB/s write,";
00296 
00297 
00298             begin = timestamp();
00299 
00300             if (do_read) {
00301                 for (unsigned i = 0; i < ndisks; i++)
00302                 {
00303                     for (unsigned j = 0; j < current_num_blocks; j++)
00304                         reqs[i * current_num_blocks + j] = disks[i]->aread(buffer + current_step_size_int * i + j * block_size_int,
00305                                                                            offset + j * block_size,
00306                                                                            block_size,
00307                                                                            stxxl::default_completion_handler());
00308                 }
00309 
00310  #ifdef WATCH_TIMES
00311                 watch_times(reqs, ndisks, r_finish_times);
00312  #else
00313                 wait_all(reqs, ndisks * current_num_blocks);
00314  #endif
00315 
00316                 end = timestamp();
00317                 elapsed = end - begin;
00318                 totalsizeread += current_step_size;
00319                 totaltimeread += elapsed;
00320             } else {
00321                 elapsed = 0.0;
00322             }
00323 
00324 #if 0
00325             std::cout << "READ\nDisks: " << ndisks
00326                       << " \nElapsed time: " << end - begin
00327                       << " \nThroughput: " << int(double(current_step_size * ndisks) / MB / (end - begin))
00328                       << " MiB/s \nPer one disk:"
00329                       << int(double(current_step_size) / MB / (end - begin)) << " MiB/s"
00330                       << std::endl;
00331 #endif
00332 
00333             std::cout << std::setw(2) << ndisks << " * "
00334                       << std::setw(8) << std::setprecision(3) << (throughput(current_step_size, elapsed)) << " = "
00335                       << std::setw(8) << std::setprecision(3) << (throughput(current_step_size, elapsed) * ndisks) << " MiB/s read";
00336 
00337 #ifdef WATCH_TIMES
00338             out_stat(begin, end, r_finish_times, ndisks, disks_arr);
00339 #endif
00340 
00341             if (do_read) {
00342                 for (unsigned d = 0; d < ndisks; ++d) {
00343                     for (unsigned s = 0; s < (current_step_size >> 9); ++s) {
00344                         unsigned i = d * current_step_size_int + s * (512 / sizeof(unsigned)) + 42;
00345                         unsigned b = (offset >> 9) + s;
00346                         if (buffer[i] != b) {
00347                             verify_failed = true;
00348                             std::cout << "Error on disk " << d << " sector " << std::hex << std::setw(8) << b
00349                                       << "  got: " << std::hex << std::setw(8) << buffer[i] << " wanted: " << std::hex << std::setw(8) << b
00350                                       << std::dec << std::endl;
00351                         }
00352                         buffer[i] = pattern;
00353                     }
00354                 }
00355                 for (unsigned i = 0; i < ndisks * current_step_size_int; i++)
00356                 {
00357                     if (buffer[i] != pattern)
00358                     {
00359                         verify_failed = true;
00360                         int ibuf = i / current_step_size_int;
00361                         int pos = i % current_step_size_int;
00362 
00363                         std::cout << "Error on disk " << ibuf << " position " << std::hex << std::setw(8) << offset + pos * sizeof(int)
00364                                   << "  got: " << std::hex << std::setw(8) << buffer[i] << " wanted: " << std::hex << std::setw(8) << pattern
00365                                   << std::dec << std::endl;
00366 
00367                         i = (ibuf + 1) * current_step_size_int; // jump to next
00368                     }
00369                 }
00370             }
00371             std::cout << std::endl;
00372 
00373             offset += current_step_size;
00374         }
00375     }
00376     catch (const std::exception & ex)
00377     {
00378         std::cout << std::endl;
00379         STXXL_ERRMSG(ex.what());
00380     }
00381     t_total.stop();
00382 
00383     std::cout << "=============================================================================================" << std::endl;
00384     // the following line of output is parsed by misc/diskbench-avgplot.sh
00385     std::cout << "# Average over " << std::setw(8) << stxxl::STXXL_MAX(totalsizewrite, totalsizeread) / MB << " MiB: ";
00386     std::cout << std::setw(2) << ndisks << " * "
00387               << std::setw(8) << std::setprecision(3) << (throughput(totalsizewrite, totaltimewrite)) << " = "
00388               << std::setw(8) << std::setprecision(3) << (throughput(totalsizewrite, totaltimewrite) * ndisks) << " MiB/s write,";
00389     std::cout << std::setw(2) << ndisks << " * "
00390               << std::setw(8) << std::setprecision(3) << (throughput(totalsizeread, totaltimeread)) << " = "
00391               << std::setw(8) << std::setprecision(3) << (throughput(totalsizeread, totaltimeread) * ndisks) << " MiB/s read"
00392               << std::endl;
00393     if (totaltimewrite != 0.0)
00394         std::cout << "# Write time   " << std::setw(8) << std::setprecision(3) << totaltimewrite << " s" << std::endl;
00395     if (totaltimeread != 0.0)
00396         std::cout << "# Read time    " << std::setw(8) << std::setprecision(3) << totaltimeread << " s" << std::endl;
00397     std::cout << "# Non-I/O time " << std::setw(8) << std::setprecision(3) << (t_total.seconds() - totaltimewrite - totaltimeread) << " s, average throughput "
00398               << std::setw(8) << std::setprecision(3) << (throughput(totalsizewrite + totalsizeread, t_total.seconds() - totaltimewrite - totaltimeread) * ndisks) << " MiB/s"
00399               << std::endl;
00400     std::cout << "# Total time   " << std::setw(8) << std::setprecision(3) << t_total.seconds() << " s, average throughput "
00401               << std::setw(8) << std::setprecision(3) << (throughput(totalsizewrite + totalsizeread, t_total.seconds()) * ndisks) << " MiB/s"
00402               << std::endl;
00403 
00404 #ifdef WATCH_TIMES
00405     delete[] r_finish_times;
00406     delete[] w_finish_times;
00407 #endif
00408     delete[] reqs;
00409     for (unsigned i = 0; i < ndisks; i++)
00410         delete disks[i];
00411     delete[] disks;
00412     stxxl::aligned_dealloc<BLOCK_ALIGN>(buffer);
00413 
00414     return verify_failed ? 1 : 0;
00415 }
00416 
00417 // vim: et:ts=4:sw=4
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines