2020-06-12 16:03:49 +05:30
|
|
|
#include "base/io_generator.h"
|
2020-07-10 12:49:38 +05:30
|
|
|
#include "persistent-data/run_set.h"
|
2020-06-12 16:03:49 +05:30
|
|
|
#include <stdexcept>
|
|
|
|
#include <cstdlib>
|
|
|
|
#include <cstring>
|
|
|
|
|
|
|
|
using namespace base;
|
|
|
|
|
|
|
|
//----------------------------------------------------------------
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
std::pair<char const*, io_pattern::pattern> 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]);
|
|
|
|
|
|
|
|
//--------------------------------
|
|
|
|
|
2020-07-24 13:20:43 +05:30
|
|
|
class sequence_generator {
|
2020-06-12 16:03:49 +05:30
|
|
|
public:
|
2020-07-24 13:20:43 +05:30
|
|
|
typedef std::shared_ptr<sequence_generator> ptr;
|
2020-06-12 16:03:49 +05:30
|
|
|
|
2020-07-24 13:20:43 +05:30
|
|
|
virtual uint64_t next() = 0;
|
2020-06-12 16:03:49 +05:30
|
|
|
};
|
|
|
|
|
2020-07-24 13:20:43 +05:30
|
|
|
// - The maximum generated value is not greater than (begin+size)
|
|
|
|
// - The generated values are not aligned to the step, if the begin
|
|
|
|
// value is not aligned.
|
|
|
|
class forward_sequence_generator: public sequence_generator {
|
2020-06-12 16:03:49 +05:30
|
|
|
public:
|
2020-07-24 13:20:43 +05:30
|
|
|
forward_sequence_generator(uint64_t begin,
|
|
|
|
uint64_t size,
|
|
|
|
uint64_t step)
|
|
|
|
: begin_(begin),
|
|
|
|
end_(begin + (size / step) * step),
|
|
|
|
step_(step),
|
|
|
|
current_(begin) {
|
|
|
|
if (size < step)
|
|
|
|
throw std::runtime_error("size must be greater than the step");
|
2020-06-12 16:03:49 +05:30
|
|
|
}
|
|
|
|
|
2020-07-24 13:20:43 +05:30
|
|
|
uint64_t next() {
|
|
|
|
uint64_t r = current_;
|
|
|
|
current_ += step_;
|
|
|
|
if (current_ >= end_) {
|
2020-06-12 16:03:49 +05:30
|
|
|
current_ = begin_;
|
2020-06-26 20:34:15 +05:30
|
|
|
}
|
2020-06-12 16:03:49 +05:30
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2020-07-24 13:20:43 +05:30
|
|
|
uint64_t begin_;
|
|
|
|
uint64_t end_;
|
|
|
|
uint64_t step_;
|
|
|
|
uint64_t current_;
|
2020-06-12 16:03:49 +05:30
|
|
|
};
|
|
|
|
|
2020-07-24 13:20:43 +05:30
|
|
|
// - The maximum generated value is not greater than (begin+size)
|
|
|
|
// - The generated values are not aligned to the step, if the begin
|
|
|
|
// value is not aligned.
|
|
|
|
class random_sequence_generator: public sequence_generator {
|
2020-06-12 16:03:49 +05:30
|
|
|
public:
|
2020-07-24 13:20:43 +05:30
|
|
|
// TODO: load random seeds
|
|
|
|
random_sequence_generator(uint64_t begin,
|
|
|
|
uint64_t size,
|
|
|
|
uint64_t step)
|
|
|
|
: begin_(begin),
|
|
|
|
nr_steps_(size / step),
|
|
|
|
step_(step),
|
2020-07-10 12:49:38 +05:30
|
|
|
nr_generated_(0) {
|
2020-06-12 16:03:49 +05:30
|
|
|
}
|
|
|
|
|
2020-07-24 13:20:43 +05:30
|
|
|
uint64_t next() {
|
|
|
|
uint64_t step_idx;
|
2020-07-10 12:49:38 +05:30
|
|
|
|
|
|
|
bool found = true;
|
|
|
|
while (found) {
|
2020-07-24 13:20:43 +05:30
|
|
|
step_idx = std::rand() % nr_steps_;
|
|
|
|
found = rand_map_.member(step_idx);
|
2020-07-10 12:49:38 +05:30
|
|
|
}
|
2020-07-24 13:20:43 +05:30
|
|
|
rand_map_.add(step_idx);
|
2020-07-10 12:49:38 +05:30
|
|
|
++nr_generated_;
|
|
|
|
|
|
|
|
// wrap-around
|
2020-07-24 13:20:43 +05:30
|
|
|
if (nr_generated_ == nr_steps_) {
|
|
|
|
rand_map_.clear();
|
2020-07-10 12:49:38 +05:30
|
|
|
nr_generated_ = 0;
|
|
|
|
}
|
|
|
|
|
2020-07-24 13:20:43 +05:30
|
|
|
return begin_ + step_idx * step_;
|
2020-06-12 16:03:49 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2020-07-24 13:20:43 +05:30
|
|
|
uint64_t begin_;
|
|
|
|
uint64_t nr_steps_;
|
|
|
|
uint64_t step_;
|
2020-07-10 12:49:38 +05:30
|
|
|
|
2020-07-24 13:20:43 +05:30
|
|
|
base::run_set<uint64_t> rand_map_;
|
2020-07-10 12:49:38 +05:30
|
|
|
uint64_t nr_generated_;
|
2020-06-12 16:03:49 +05:30
|
|
|
};
|
|
|
|
|
|
|
|
//--------------------------------
|
|
|
|
|
|
|
|
class op_generator {
|
|
|
|
public:
|
|
|
|
typedef std::shared_ptr<op_generator> 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<unsigned>(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);
|
2020-06-19 21:34:01 +05:30
|
|
|
virtual bool next(base::io &next_io);
|
2020-06-12 16:03:49 +05:30
|
|
|
|
|
|
|
private:
|
2020-07-24 13:20:43 +05:30
|
|
|
sequence_generator::ptr
|
2020-06-12 16:03:49 +05:30
|
|
|
create_offset_generator(io_generator_options const &opts);
|
|
|
|
|
|
|
|
op_generator::ptr
|
|
|
|
create_op_generator(io_generator_options const &opts);
|
|
|
|
|
2020-07-24 13:20:43 +05:30
|
|
|
sequence_generator::ptr offset_gen_;
|
2020-06-12 16:03:49 +05:30
|
|
|
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_) {
|
|
|
|
}
|
|
|
|
|
2020-06-19 21:34:01 +05:30
|
|
|
bool base_io_generator::next(base::io &next_io) {
|
2020-06-12 16:03:49 +05:30
|
|
|
if (io_size_finished_ >= io_size_total_)
|
2020-06-19 21:34:01 +05:30
|
|
|
return false;
|
2020-06-12 16:03:49 +05:30
|
|
|
|
|
|
|
next_io.op_ = op_gen_->next_op();
|
2020-07-24 13:20:43 +05:30
|
|
|
next_io.sector_ = offset_gen_->next();
|
2020-06-12 16:03:49 +05:30
|
|
|
next_io.size_ = block_size_;
|
|
|
|
|
|
|
|
io_size_finished_ += block_size_;
|
2020-06-19 21:34:01 +05:30
|
|
|
|
|
|
|
return true;
|
2020-06-12 16:03:49 +05:30
|
|
|
}
|
|
|
|
|
2020-07-24 13:20:43 +05:30
|
|
|
sequence_generator::ptr
|
2020-06-12 16:03:49 +05:30
|
|
|
base_io_generator::create_offset_generator(io_generator_options const &opts) {
|
2020-07-24 13:20:43 +05:30
|
|
|
sequence_generator *gen;
|
|
|
|
|
2020-06-12 16:03:49 +05:30
|
|
|
if (opts.pattern_.is_random())
|
2020-07-24 13:20:43 +05:30
|
|
|
gen = new random_sequence_generator(opts.offset_,
|
|
|
|
opts.size_, opts.block_size_);
|
|
|
|
else
|
|
|
|
gen = new forward_sequence_generator(opts.offset_,
|
|
|
|
opts.size_, opts.block_size_);
|
|
|
|
|
|
|
|
return sequence_generator::ptr(gen);
|
2020-06-12 16:03:49 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------
|