diff --git a/Makefile.in b/Makefile.in index 59f8f14..31ba2f7 100644 --- a/Makefile.in +++ b/Makefile.in @@ -40,6 +40,7 @@ SOURCE=\ base/error_state.cc \ base/error_string.cc \ base/grid_layout.cc \ + base/io_generator.cc \ base/file_utils.cc \ base/progress_monitor.cc \ base/rolling_hash.cc \ diff --git a/base/io.h b/base/io.h new file mode 100644 index 0000000..75f2d3a --- /dev/null +++ b/base/io.h @@ -0,0 +1,25 @@ +#ifndef BASE_IO_H +#define BASE_IO_H + +#include "base/types.h" +#include + +//---------------------------------------------------------------- + +namespace base { + enum req_op { + REQ_OP_READ, + REQ_OP_WRITE, + REQ_OP_DISCARD + }; + + struct io { + unsigned op_; + sector_t sector_; + sector_t size_; + }; +} + +//---------------------------------------------------------------- + +#endif diff --git a/base/io_generator.cc b/base/io_generator.cc new file mode 100644 index 0000000..0543d31 --- /dev/null +++ b/base/io_generator.cc @@ -0,0 +1,240 @@ +#include "base/io_generator.h" +#include +#include +#include + +using namespace base; + +//---------------------------------------------------------------- + +namespace { + std::pair patterns[] = { + {"read", io_pattern::READ}, + {"write", io_pattern::WRITE}, + {"trim", io_pattern::TRIM}, + {"readwrite", io_pattern::READ_WRITE}, + {"trimwrite", io_pattern::TRIM_WRITE}, + {"randread", io_pattern::RAND_READ}, + {"randwrite", io_pattern::RAND_WRITE}, + {"randtrim", io_pattern::RAND_TRIM}, + {"randrw", io_pattern::RAND_RW}, + {"randtw", io_pattern::RAND_TW} + }; + + unsigned const nr_patterns = sizeof(patterns) / sizeof(patterns[0]); + + //-------------------------------- + + class offset_generator { + public: + typedef std::shared_ptr ptr; + + virtual base::sector_t next_offset() = 0; + }; + + class sequential_offset_generator: public offset_generator { + public: + sequential_offset_generator(base::sector_t offset, + base::sector_t size, + base::sector_t block_size) + : block_size_(block_size), + begin_(offset), + end_(offset + size), + current_(offset) { + if (size < block_size) + throw std::runtime_error("size must be greater than block_size"); + } + + base::sector_t next_offset() { + sector_t r = current_; + current_ += block_size_; + if (current_ > end_) + current_ = begin_; + return r; + } + + private: + unsigned block_size_; + base::sector_t begin_; + base::sector_t end_; + base::sector_t current_; + }; + + class random_offset_generator: public offset_generator { + public: + random_offset_generator(sector_t offset, + sector_t size, + sector_t block_size) + : block_begin_(offset / block_size), + nr_blocks_(size / block_size), + block_size_(block_size) { + } + + sector_t next_offset() { + return ((std::rand() % nr_blocks_) + block_begin_) * block_size_; + } + + private: + uint64_t block_begin_; + uint64_t nr_blocks_; + unsigned block_size_; + }; + + //-------------------------------- + + class op_generator { + public: + typedef std::shared_ptr ptr; + + op_generator(base::req_op op1) + : op1_(op1), op2_(op1), op1_pct_(100) { + } + + op_generator(base::req_op op1, + base::req_op op2, + unsigned op1_pct) + : op1_(op1), op2_(op2), op1_pct_(op1_pct) { + if (op1_pct > 100) + throw std::runtime_error("invalid percentage"); + } + + base::req_op next_op() { + if (static_cast(std::rand()) % 100 > op1_pct_) + return op2_; + return op1_; + } + + private: + base::req_op op1_; + base::req_op op2_; + unsigned op1_pct_; + }; + + //-------------------------------- + + class base_io_generator: public io_generator { + public: + base_io_generator(io_generator_options const &opts); + virtual bool has_next(); + virtual void next(base::io &next_io); + + private: + offset_generator::ptr + create_offset_generator(io_generator_options const &opts); + + op_generator::ptr + create_op_generator(io_generator_options const &opts); + + offset_generator::ptr offset_gen_; + op_generator::ptr op_gen_; + sector_t block_size_; + size_t io_size_finished_; + size_t io_size_total_; + }; + + base_io_generator::base_io_generator(io_generator_options const &opts) + : offset_gen_(create_offset_generator(opts)), + op_gen_(create_op_generator(opts)), + block_size_(opts.block_size_), + io_size_finished_(0), + io_size_total_(opts.io_size_) { + } + + bool base_io_generator::has_next() { + return io_size_finished_ < io_size_total_; + } + + void base_io_generator::next(base::io &next_io) { + if (io_size_finished_ >= io_size_total_) + throw std::runtime_error(""); + + next_io.op_ = op_gen_->next_op(); + next_io.sector_ = offset_gen_->next_offset(); + next_io.size_ = block_size_; + + io_size_finished_ += block_size_; + } + + offset_generator::ptr + base_io_generator::create_offset_generator(io_generator_options const &opts) { + if (opts.pattern_.is_random()) + return offset_generator::ptr( + new random_offset_generator(opts.offset_, + opts.size_, + opts.block_size_)); + + return offset_generator::ptr( + new sequential_offset_generator(opts.offset_, + opts.size_, + opts.block_size_)); + } + + op_generator::ptr + base_io_generator::create_op_generator(io_generator_options const &opts) { + // FIXME: elimiate the switch-case and hide enum values + switch (opts.pattern_.val_) { + case io_pattern::READ: + case io_pattern::RAND_READ: + return op_generator::ptr(new op_generator(base::REQ_OP_READ)); + case io_pattern::WRITE: + case io_pattern::RAND_WRITE: + return op_generator::ptr(new op_generator(base::REQ_OP_WRITE)); + case io_pattern::TRIM: + case io_pattern::RAND_TRIM: + return op_generator::ptr(new op_generator(base::REQ_OP_DISCARD)); + case io_pattern::READ_WRITE: + case io_pattern::RAND_RW: + return op_generator::ptr(new op_generator(base::REQ_OP_READ, + base::REQ_OP_WRITE, + 50)); + case io_pattern::TRIM_WRITE: + case io_pattern::RAND_TW: + return op_generator::ptr(new op_generator(base::REQ_OP_DISCARD, + base::REQ_OP_WRITE, + 50)); + default: + throw std::runtime_error("unknown pattern"); + } + } +} + +//---------------------------------------------------------------- + +io_pattern::io_pattern() + : val_(pattern::READ) { +} + +io_pattern::io_pattern(char const *pattern) { + parse(pattern); +} + +void +io_pattern::parse(char const *pattern) { + bool found = false; + unsigned i = 0; + for (i = 0; i < nr_patterns; i++) { + if (!strcmp(patterns[i].first, pattern)) { + found = true; + break; + } + } + + if (!found) + throw std::runtime_error("unknow pattern"); + + val_ = patterns[i].second; +} + +bool +io_pattern::is_random() const { + return val_ & pattern::RANDOM; +} + +//---------------------------------------------------------------- + +io_generator::ptr +base::create_io_generator(io_generator_options const &opts) { + return io_generator::ptr(new base_io_generator(opts)); +} + +//---------------------------------------------------------------- diff --git a/base/io_generator.h b/base/io_generator.h new file mode 100644 index 0000000..4a33550 --- /dev/null +++ b/base/io_generator.h @@ -0,0 +1,55 @@ +#ifndef BASE_IO_GENERATOR_H +#define BASE_IO_GENERATOR_H + +#include "base/io.h" +#include + +//---------------------------------------------------------------- + +namespace base { + struct io_pattern { + enum pattern { + READ = 1 << 1, + WRITE = 1 << 2, + TRIM = 1 << 3, + RANDOM = 1 << 8, + READ_WRITE = READ | WRITE, + TRIM_WRITE = WRITE | TRIM, + RAND_READ = READ | RANDOM, + RAND_WRITE = WRITE | RANDOM, + RAND_TRIM = TRIM | RANDOM, + RAND_RW = READ_WRITE | RANDOM, + RAND_TW = TRIM_WRITE | RANDOM, + }; + + io_pattern(); + io_pattern(char const *pattern); + void parse(char const *pattern); + bool is_random() const; + + pattern val_; + }; + + struct io_generator_options { + io_pattern pattern_; + sector_t offset_; + sector_t block_size_; + sector_t size_; + sector_t io_size_; + }; + + class io_generator { + public: + typedef std::shared_ptr ptr; + + virtual bool has_next() = 0; + virtual void next(base::io &next_io) = 0; + }; + + io_generator::ptr + create_io_generator(io_generator_options const &opts); +} + +//---------------------------------------------------------------- + +#endif diff --git a/base/types.h b/base/types.h new file mode 100644 index 0000000..7e56139 --- /dev/null +++ b/base/types.h @@ -0,0 +1,15 @@ +#ifndef BASE_TYPES_H +#define BASE_TYPES_H + +#include + +//---------------------------------------------------------------- + +namespace base { + using sector_t = uint64_t; + unsigned const SECTOR_SHIFT = 9; +} + +//---------------------------------------------------------------- + +#endif