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