Stxxl
1.4.0
|
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