http://stxxl.sourceforge.net
<dementiev@mpi-sb.mpg.de>
<beckmann@cs.uni-frankfurt.de>
<singler@ira.uka.de>
<tb@panthema.net>
http://www.boost.org/LICENSE_1_0.txt
#include <iomanip>
#include <vector>
#include <stxxl/io>
#include <stxxl/aligned_alloc>
#include <stxxl/timer>
#include <stxxl/bits/version.h>
#include <stxxl/bits/common/cmdline.h>
using stxxl::request_ptr;
using stxxl::file;
using stxxl::timestamp;
#ifdef BLOCK_ALIGN
#undef BLOCK_ALIGN
#endif
#define BLOCK_ALIGN 4096
#define POLL_DELAY 1000
#if STXXL_WINDOWS
const char* default_file_type = "wincall";
#else
const char* default_file_type = "syscall";
#endif
#ifdef WATCH_TIMES
void watch_times(request_ptr reqs[], unsigned n, double* out)
{
bool* finished = new bool[n];
unsigned count = 0;
for (unsigned i = 0; i < n; i++)
finished[i] = false;
while (count != n)
{
usleep(POLL_DELAY);
unsigned i = 0;
for (i = 0; i < n; i++)
{
if (!finished[i])
if (reqs[i]->poll())
{
finished[i] = true;
out[i] = timestamp();
count++;
}
}
}
delete[] finished;
}
void out_stat(double start, double end, double* times, unsigned n, const std::vector<std::string>& names)
{
for (unsigned i = 0; i < n; i++)
{
std::cout << i << " " << names[i] << " took " <<
100. * (times[i] - start) / (end - start) << " %" << std::endl;
}
}
#endif
#define MB (1024 * 1024)
static inline double throughput(stxxl::int64 bytes, double seconds)
{
if (seconds == 0.0)
return 0.0;
return bytes / (1024 * 1024) / seconds;
}
int benchmark_files(int argc, char* argv[])
{
stxxl::uint64 offset = 0, length = 0;
bool no_direct_io = false;
bool sync_io = false;
bool resize_after_open = false;
std::string file_type = default_file_type;
stxxl::uint64 block_size = 0;
unsigned int batch_size = 1;
std::string opstr = "wv";
unsigned pattern = 0;
std::vector<std::string> files_arr;
stxxl::cmdline_parser cp;
cp.add_param_bytes("length", "Length to write in file.", length);
cp.add_param_stringlist("filename", "File path to run benchmark on.", files_arr);
cp.add_bytes('o', "offset", "Starting offset to write in file.", offset);
cp.add_flag(0, "no-direct", "open files without O_DIRECT", no_direct_io);
cp.add_flag(0, "sync", "open files with O_SYNC|O_DSYNC|O_RSYNC", sync_io);
cp.add_flag(0, "resize", "resize the file size after opening, needed e.g. for creating mmap files", resize_after_open);
cp.add_bytes(0, "block_size", "block size for operations (default 8 MiB)", block_size);
cp.add_uint(0, "batch_size", "increase (default 1) to submit several I/Os at once and report average rate", batch_size);
cp.add_string('f', "file-type",
"Method to open file (syscall|mmap|wincall|boostfd|...) default: " + file_type, file_type);
cp.add_string('p', "operations",
"[w]rite pattern, [r]ead without verification, read and [v]erify pattern (default: 'wv')", opstr);
cp.add_uint(0, "pattern",
"32-bit pattern to write (default: block index)", pattern);
cp.set_description("Open a file using one of STXXL's file abstractions and perform write/read/verify tests on the file. "
"Block sizes and batch size can be adjusted via command line. "
"If length == 0 , then operation will continue till end of space (please ignore the write error). "
"Memory consumption: block_size * batch_size * num_files");
if (!cp.process(argc, argv))
return -1;
stxxl::uint64 endpos = offset + length;
if (block_size == 0)
block_size = 8 * MB;
if (batch_size == 0)
batch_size = 1;
bool do_read = false, do_write = false, do_verify = false;
if (opstr.find("nd") != std::string::npos || opstr.find("ND") != std::string::npos) {
no_direct_io = true;
}
if (opstr.find('r') != std::string::npos || opstr.find('R') != std::string::npos) {
do_read = true;
}
if (opstr.find('v') != std::string::npos || opstr.find('V') != std::string::npos) {
do_verify = true;
}
if (opstr.find('w') != std::string::npos || opstr.find('W') != std::string::npos) {
do_write = true;
}
const char* myself = strrchr(argv[0], '/');
if (!myself || !*(++myself))
myself = argv[0];
std::cout << "# " << myself << " " << stxxl::get_version_string_long();
#if STXXL_DIRECT_IO_OFF
std::cout << " STXXL_DIRECT_IO_OFF";
#endif
std::cout << std::endl;
for (size_t ii = 0; ii < files_arr.size(); ii++)
{
std::cout << "# Add file: " << files_arr[ii] << std::endl;
}
const size_t nfiles = files_arr.size();
bool verify_failed = false;
const stxxl::unsigned_type step_size = block_size * batch_size;
const stxxl::uint64 block_size_int = block_size / sizeof(int);
const stxxl::uint64 step_size_int = step_size / sizeof(int);
unsigned* buffer = (unsigned*)stxxl::aligned_alloc<BLOCK_ALIGN>(step_size * nfiles);
file** files = new file*[nfiles];
request_ptr* reqs = new request_ptr[nfiles * batch_size];
#ifdef WATCH_TIMES
double* r_finish_times = new double[nfiles];
double* w_finish_times = new double[nfiles];
#endif
double totaltimeread = 0, totaltimewrite = 0;
stxxl::int64 totalsizeread = 0, totalsizewrite = 0;
for (unsigned i = 0; i < nfiles * step_size_int; i++)
buffer[i] = (pattern ? pattern : i);
for (unsigned i = 0; i < nfiles; i++)
{
int openmode = file::CREAT | file::RDWR;
if (!no_direct_io) {
openmode |= file::DIRECT;
}
if (sync_io) {
openmode |= file::SYNC;
}
files[i] = stxxl::create_file(file_type, files_arr[i], openmode, i);
if (resize_after_open)
files[i]->set_size(endpos);
}
std::cout << "# Step size: "
<< step_size << " bytes per file ("
<< batch_size << " block" << (batch_size == 1 ? "" : "s") << " of "
<< block_size << " bytes)"
<< " file_type=" << file_type
<< " O_DIRECT=" << (no_direct_io ? "no" : "yes")
<< " O_SYNC=" << (sync_io ? "yes" : "no")
<< std::endl;
stxxl::timer t_total(true);
try {
while (offset + stxxl::uint64(step_size) <= endpos || length == 0)
{
const stxxl::uint64 current_step_size = (length == 0) ? stxxl::int64(step_size) : std::min<stxxl::int64>(step_size, endpos - offset);
const stxxl::uint64 current_step_size_int = current_step_size / sizeof(int);
const stxxl::uint64 current_num_blocks = stxxl::div_ceil(current_step_size, block_size);
std::cout << "File offset " << std::setw(8) << offset / MB << " MiB: " << std::fixed;
double begin = timestamp(), end = begin, elapsed;
if (do_write)
{
for (stxxl::uint64 j = 42, b = offset >> 9; j < current_step_size_int; j += 512 / sizeof(unsigned), ++b)
{
for (unsigned i = 0; i < nfiles; i++)
buffer[current_step_size_int * i + j] = (unsigned int)b;
}
for (unsigned i = 0; i < nfiles; i++)
{
for (unsigned j = 0; j < current_num_blocks; j++)
reqs[i * current_num_blocks + j] =
files[i]->awrite(buffer + current_step_size_int * i + j * block_size_int,
offset + j * block_size,
block_size,
stxxl::default_completion_handler());
}
#ifdef WATCH_TIMES
watch_times(reqs, nfiles, w_finish_times);
#else
wait_all(reqs, nfiles * current_num_blocks);
#endif
end = timestamp();
elapsed = end - begin;
totalsizewrite += current_step_size;
totaltimewrite += elapsed;
}
else {
elapsed = 0.0;
}
#if 0
std::cout << "# WRITE\nFiles: " << nfiles
<< " \nElapsed time: " << end - begin
<< " \nThroughput: " << int(double(current_step_size * nfiles) / MB / (end - begin))
<< " MiB/s \nPer one file:"
<< int(double(current_step_size) / MB / (end - begin)) << " MiB/s"
<< std::endl;
#endif
#ifdef WATCH_TIMES
out_stat(begin, end, w_finish_times, nfiles, files_arr);
#endif
std::cout << std::setw(2) << nfiles << " * "
<< std::setw(8) << std::setprecision(3) << (throughput(current_step_size, elapsed)) << " = "
<< std::setw(8) << std::setprecision(3) << (throughput(current_step_size, elapsed) * nfiles) << " MiB/s write,";
begin = end = timestamp();
if (do_read || do_verify)
{
for (unsigned i = 0; i < nfiles; i++)
{
for (unsigned j = 0; j < current_num_blocks; j++)
reqs[i * current_num_blocks + j] = files[i]->aread(buffer + current_step_size_int * i + j * block_size_int,
offset + j * block_size,
block_size,
stxxl::default_completion_handler());
}
#ifdef WATCH_TIMES
watch_times(reqs, nfiles, r_finish_times);
#else
wait_all(reqs, nfiles * current_num_blocks);
#endif
end = timestamp();
elapsed = end - begin;
totalsizeread += current_step_size;
totaltimeread += elapsed;
}
else {
elapsed = 0.0;
}
#if 0
std::cout << "# READ\nFiles: " << nfiles
<< " \nElapsed time: " << end - begin
<< " \nThroughput: " << int(double(current_step_size * nfiles) / MB / (end - begin))
<< " MiB/s \nPer one file:"
<< int(double(current_step_size) / MB / (end - begin)) << " MiB/s"
<< std::endl;
#endif
std::cout << std::setw(2) << nfiles << " * "
<< std::setw(8) << std::setprecision(3) << (throughput(current_step_size, elapsed)) << " = "
<< std::setw(8) << std::setprecision(3) << (throughput(current_step_size, elapsed) * nfiles) << " MiB/s read";
#ifdef WATCH_TIMES
out_stat(begin, end, r_finish_times, nfiles, files_arr);
#endif
if (do_verify)
{
for (unsigned d = 0; d < nfiles; ++d)
{
for (unsigned s = 0; s < (current_step_size >> 9); ++s) {
stxxl::uint64 i = d * current_step_size_int + s * (512 / sizeof(unsigned)) + 42;
stxxl::uint64 b = (offset >> 9) + s;
if (buffer[i] != b)
{
verify_failed = true;
std::cout << "Error on file " << d << " sector " << std::hex << std::setw(8) << b
<< " got: " << std::hex << std::setw(8) << buffer[i] << " wanted: " << std::hex << std::setw(8) << b
<< std::dec << std::endl;
}
buffer[i] = (pattern ? pattern : (unsigned int)i);
}
}
for (stxxl::uint64 i = 0; i < nfiles * current_step_size_int; i++)
{
if (buffer[i] != (pattern ? pattern : i))
{
stxxl::int64 ibuf = i / current_step_size_int;
stxxl::uint64 pos = i % current_step_size_int;
std::cout << std::endl
<< "Error on file " << ibuf << " position " << std::hex << std::setw(8) << offset + pos * sizeof(int)
<< " got: " << std::hex << std::setw(8) << buffer[i] << " wanted: " << std::hex << std::setw(8) << i
<< std::dec << std::endl;
i = (ibuf + 1) * current_step_size_int;
verify_failed = true;
}
}
}
std::cout << std::endl;
offset += current_step_size;
}
}
catch (const std::exception& ex)
{
std::cout << std::endl;
STXXL_ERRMSG(ex.what());
}
t_total.stop();
std::cout << "=============================================================================================" << std::endl;
std::cout << "# Average over " << std::setw(8) << stxxl::STXXL_MAX(totalsizewrite, totalsizeread) / MB << " MiB: ";
std::cout << std::setw(2) << nfiles << " * "
<< std::setw(8) << std::setprecision(3) << (throughput(totalsizewrite, totaltimewrite)) << " = "
<< std::setw(8) << std::setprecision(3) << (throughput(totalsizewrite, totaltimewrite) * nfiles) << " MiB/s write,";
std::cout << std::setw(2) << nfiles << " * "
<< std::setw(8) << std::setprecision(3) << (throughput(totalsizeread, totaltimeread)) << " = "
<< std::setw(8) << std::setprecision(3) << (throughput(totalsizeread, totaltimeread) * nfiles) << " MiB/s read"
<< std::endl;
if (totaltimewrite != 0.0)
std::cout << "# Write time " << std::setw(8) << std::setprecision(3) << totaltimewrite << " s" << std::endl;
if (totaltimeread != 0.0)
std::cout << "# Read time " << std::setw(8) << std::setprecision(3) << totaltimeread << " s" << std::endl;
std::cout << "# Non-I/O time " << std::setw(8) << std::setprecision(3) << (t_total.seconds() - totaltimewrite - totaltimeread) << " s, average throughput "
<< std::setw(8) << std::setprecision(3) << (throughput(totalsizewrite + totalsizeread, t_total.seconds() - totaltimewrite - totaltimeread) * nfiles) << " MiB/s"
<< std::endl;
std::cout << "# Total time " << std::setw(8) << std::setprecision(3) << t_total.seconds() << " s, average throughput "
<< std::setw(8) << std::setprecision(3) << (throughput(totalsizewrite + totalsizeread, t_total.seconds()) * nfiles) << " MiB/s"
<< std::endl;
if (do_verify)
{
std::cout << "# Verify: " << (verify_failed ? "FAILED." : "all okay.") << std::endl;
}
#ifdef WATCH_TIMES
delete[] r_finish_times;
delete[] w_finish_times;
#endif
delete[] reqs;
for (unsigned i = 0; i < nfiles; i++)
delete files[i];
delete[] files;
stxxl::aligned_dealloc<BLOCK_ALIGN>(buffer);
return (verify_failed ? 1 : 0);
}