[base] Factor out sequence_generator

This commit is contained in:
Ming-Hung Tsai 2020-07-27 11:32:50 +08:00
parent e62022a200
commit 105b8ec1cf
4 changed files with 198 additions and 149 deletions

View File

@ -44,6 +44,7 @@ SOURCE=\
base/file_utils.cc \ base/file_utils.cc \
base/progress_monitor.cc \ base/progress_monitor.cc \
base/rolling_hash.cc \ base/rolling_hash.cc \
base/sequence_generator.cc \
base/xml_utils.cc \ base/xml_utils.cc \
block-cache/block_cache.cc \ block-cache/block_cache.cc \
block-cache/copier.cc \ block-cache/copier.cc \

View File

@ -1,5 +1,5 @@
#include "base/io_generator.h" #include "base/io_generator.h"
#include "base/run_set.h" #include "base/sequence_generator.h"
#include <stdexcept> #include <stdexcept>
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
@ -26,150 +26,6 @@ namespace {
//-------------------------------- //--------------------------------
class sequence_generator {
public:
typedef std::shared_ptr<sequence_generator> ptr;
virtual uint64_t next() = 0;
};
// - 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 {
public:
forward_sequence_generator(uint64_t begin,
uint64_t size,
uint64_t step)
: begin_(begin),
end_(begin + (size / step) * step),
step_(step),
current_(begin),
rounds_(0) {
if (size < step)
throw std::runtime_error("size must be greater than the step");
}
forward_sequence_generator()
: begin_(0), end_(1), step_(1),
current_(0), rounds_(0) {
}
uint64_t next() {
uint64_t r = current_;
current_ += step_;
if (current_ >= end_) {
current_ = begin_;
++rounds_;
}
return r;
}
void reset(uint64_t begin, uint64_t size, uint64_t step = 1) {
begin_ = begin;
end_ = begin + (size / step) * step;
step_ = step;
current_ = begin;
rounds_ = 0;
}
uint64_t get_rounds() {
return rounds_;
}
private:
uint64_t begin_;
uint64_t end_;
uint64_t step_;
uint64_t current_;
uint64_t rounds_;
};
// - 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 {
public:
// TODO: load random seeds
random_sequence_generator(uint64_t begin,
uint64_t size,
uint64_t step,
unsigned seq_nr = 1)
: begin_(begin),
nr_steps_(size / step),
step_(step),
max_forward_steps_(seq_nr),
nr_generated_(0)
{
if (!max_forward_steps_ || max_forward_steps_ > nr_steps_)
throw std::runtime_error("invalid number of forward steps");
if (max_forward_steps_ > 1)
reset_forward_generator();
}
uint64_t next() {
// FIXME: eliminate if-else
uint64_t step_idx = (max_forward_steps_ > 1) ?
next_forward_step() : next_random_step();
rand_map_.add(step_idx);
++nr_generated_;
// wrap-around
if (nr_generated_ == nr_steps_) {
rand_map_.clear();
nr_generated_ = 0;
}
return begin_ + step_idx * step_;
}
private:
void reset_forward_generator() {
uint64_t begin = next_random_step();
unsigned seq_nr = (std::rand() % max_forward_steps_) + 1;
forward_gen_.reset(begin, seq_nr);
}
uint64_t next_forward_step() {
uint64_t step_idx;
bool found = true;
while (found) {
step_idx = forward_gen_.next();
found = rand_map_.member(step_idx);
if (found || forward_gen_.get_rounds())
reset_forward_generator();
}
return step_idx;
}
uint64_t next_random_step() const {
uint64_t step_idx;
bool found = true;
while (found) {
step_idx = std::rand() % nr_steps_;
found = rand_map_.member(step_idx);
}
return step_idx;
}
uint64_t begin_;
uint64_t nr_steps_;
uint64_t step_;
unsigned max_forward_steps_;
base::run_set<uint64_t> rand_map_;
uint64_t nr_generated_;
forward_sequence_generator forward_gen_;
};
//--------------------------------
class op_generator { class op_generator {
public: public:
typedef std::shared_ptr<op_generator> ptr; typedef std::shared_ptr<op_generator> ptr;
@ -242,17 +98,17 @@ namespace {
sequence_generator::ptr sequence_generator::ptr
base_io_generator::create_offset_generator(io_generator_options const &opts) { base_io_generator::create_offset_generator(io_generator_options const &opts) {
sequence_generator *gen; sequence_generator::ptr gen;
if (opts.pattern_.is_random()) if (opts.pattern_.is_random())
gen = new random_sequence_generator(opts.offset_, gen = create_random_sequence_generator(opts.offset_,
opts.size_, opts.block_size_, opts.size_, opts.block_size_,
opts.nr_seq_blocks_); opts.nr_seq_blocks_);
else else
gen = new forward_sequence_generator(opts.offset_, gen = create_forward_sequence_generator(opts.offset_,
opts.size_, opts.block_size_); opts.size_, opts.block_size_);
return sequence_generator::ptr(gen); return gen;
} }
op_generator::ptr op_generator::ptr

165
base/sequence_generator.cc Normal file
View File

@ -0,0 +1,165 @@
#include "base/run_set.h"
#include "base/sequence_generator.h"
#include <stdexcept>
//----------------------------------------------------------------
namespace {
// - 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 base::sequence_generator {
public:
forward_sequence_generator(uint64_t begin,
uint64_t size,
uint64_t step)
: begin_(begin),
end_(begin + (size / step) * step),
step_(step),
current_(begin),
rounds_(0) {
if (size < step)
throw std::runtime_error("size must be greater than the step");
}
forward_sequence_generator()
: begin_(0), end_(1), step_(1),
current_(0), rounds_(0) {
}
uint64_t next() {
uint64_t r = current_;
current_ += step_;
if (current_ >= end_) {
current_ = begin_;
++rounds_;
}
return r;
}
void reset(uint64_t begin, uint64_t size, uint64_t step = 1) {
begin_ = begin;
end_ = begin + (size / step) * step;
step_ = step;
current_ = begin;
rounds_ = 0;
}
uint64_t get_rounds() {
return rounds_;
}
private:
uint64_t begin_;
uint64_t end_;
uint64_t step_;
uint64_t current_;
uint64_t rounds_;
};
// - 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 base::sequence_generator {
public:
// TODO: load random seeds
random_sequence_generator(uint64_t begin,
uint64_t size,
uint64_t step,
unsigned seq_nr = 1)
: begin_(begin),
nr_steps_(size / step),
step_(step),
max_forward_steps_(seq_nr),
nr_generated_(0)
{
if (!max_forward_steps_ || max_forward_steps_ > nr_steps_)
throw std::runtime_error("invalid number of forward steps");
if (max_forward_steps_ > 1)
reset_forward_generator();
}
uint64_t next() {
// FIXME: eliminate if-else
uint64_t step_idx = (max_forward_steps_ > 1) ?
next_forward_step() : next_random_step();
rand_map_.add(step_idx);
++nr_generated_;
// wrap-around
if (nr_generated_ == nr_steps_) {
rand_map_.clear();
nr_generated_ = 0;
}
return begin_ + step_idx * step_;
}
private:
void reset_forward_generator() {
uint64_t begin = next_random_step();
unsigned seq_nr = (std::rand() % max_forward_steps_) + 1;
forward_gen_.reset(begin, seq_nr);
}
uint64_t next_forward_step() {
uint64_t step_idx;
bool found = true;
while (found) {
step_idx = forward_gen_.next();
found = rand_map_.member(step_idx);
if (found || forward_gen_.get_rounds())
reset_forward_generator();
}
return step_idx;
}
uint64_t next_random_step() const {
uint64_t step_idx;
bool found = true;
while (found) {
step_idx = std::rand() % nr_steps_;
found = rand_map_.member(step_idx);
}
return step_idx;
}
uint64_t begin_;
uint64_t nr_steps_;
uint64_t step_;
unsigned max_forward_steps_;
base::run_set<uint64_t> rand_map_;
uint64_t nr_generated_;
forward_sequence_generator forward_gen_;
};
}
//----------------------------------------------------------------
base::sequence_generator::ptr
base::create_forward_sequence_generator(uint64_t begin,
uint64_t size,
uint64_t step)
{
return sequence_generator::ptr(
new forward_sequence_generator(begin, size, step));
}
base::sequence_generator::ptr
base::create_random_sequence_generator(uint64_t begin,
uint64_t size,
uint64_t step,
unsigned seq_nr)
{
return sequence_generator::ptr(
new random_sequence_generator(begin, size, step, seq_nr));
}
//----------------------------------------------------------------

27
base/sequence_generator.h Normal file
View File

@ -0,0 +1,27 @@
#ifndef BASE_SEQUENCE_GENERATOR_H
#define BASE_SEQUENCE_GENERATOR_H
#include <memory>
//----------------------------------------------------------------
namespace base {
class sequence_generator {
public:
typedef std::shared_ptr<sequence_generator> ptr;
virtual uint64_t next() = 0;
};
sequence_generator::ptr
create_forward_sequence_generator(uint64_t begin, uint64_t size,
uint64_t step);
sequence_generator::ptr
create_random_sequence_generator(uint64_t begin, uint64_t size,
uint64_t step, unsigned seq_nr = 1);
}
//----------------------------------------------------------------
#endif