mdbx-test: redesign fitting internal parameters of "ttl" & "nested" testcases.

Change-Id: I3ade4ba9d78c00ff6911c3e35738f7dcbf63de64
This commit is contained in:
Leonid Yuriev 2020-05-24 19:52:50 +03:00
parent d83a765dbe
commit bfad1f7086
4 changed files with 143 additions and 110 deletions

View File

@ -15,6 +15,25 @@
#include "test.h" #include "test.h"
#include <cmath> #include <cmath>
/* LY: тест "эмуляцией time-to-live" с вложенными транзакциями:
* - организуется "скользящее окно", которое каждую транзакцию сдвигается
* вперед вдоль числовой оси.
* - по переднему краю "скользящего окна" записи добавляются в таблицу,
* а по заднему удаляются.
* - количество добавляемых/удаляемых записей псевдослучайно зависит
* от номера транзакции, но с экспоненциальным распределением.
* - размер "скользящего окна" также псевдослучайно зависит от номера
* транзакции с "отрицательным" экспоненциальным распределением
* MAX_WIDTH - exp(rnd(N)), при уменьшении окна сдвигается задний
* край и удаляются записи позади него.
* - групповое добавление данных в начало окна и групповое удаление в конце,
* преимущественно выполняются во вложенных транзакциях.
* - меньшая часть запускаемых вложенных транзакций отменяется, с последующим
* продолжением итераций с состояния предыдущиего коммита.
*
* Таким образом имитируется поведение таблицы с TTL: записи стохастически
* добавляются и удаляются, и изредка происходят массивные удаления. */
bool testcase_nested::setup() { bool testcase_nested::setup() {
if (!inherited::setup()) if (!inherited::setup())
return false; return false;
@ -56,18 +75,6 @@ bool testcase_nested::teardown() {
return inherited::teardown() && ok; return inherited::teardown() && ok;
} }
static unsigned edge2window(uint64_t edge, unsigned window_max) {
const double rnd = u64_to_double1(bleach64(edge));
const unsigned window = window_max - std::lrint(std::pow(window_max, rnd));
return window;
}
static unsigned edge2count(uint64_t edge, unsigned count_max) {
const double rnd = u64_to_double1(prng64_map1_white(edge));
const unsigned count = std::lrint(std::pow(count_max, rnd));
return count;
}
void testcase_nested::push_txn() { void testcase_nested::push_txn() {
MDBX_txn *txn; MDBX_txn *txn;
unsigned flags = unsigned flags =
@ -172,7 +179,7 @@ bool testcase_nested::trim_tail(unsigned window_width) {
if (unlikely(!keyvalue_maker.increment(tail_serial, 1))) if (unlikely(!keyvalue_maker.increment(tail_serial, 1)))
failure("nested: unexpected key-space overflow on the tail"); failure("nested: unexpected key-space overflow on the tail");
} }
report(1); report(tail_count);
} }
} else if (!fifo.empty()) { } else if (!fifo.empty()) {
log_verbose("nested: purge state %" PRIu64 " - %" PRIu64 ", fifo-items %zu", log_verbose("nested: purge state %" PRIu64 " - %" PRIu64 ", fifo-items %zu",
@ -220,39 +227,6 @@ retry:
} }
bool testcase_nested::run() { bool testcase_nested::run() {
/* LY: тест "эмуляцией time-to-live" с вложенными транзакциями:
* - организуется "скользящее окно", которое каждую транзакцию сдвигается
* вперед вдоль числовой оси.
* - по переднему краю "скользящего окна" записи добавляются в таблицу,
* а по заднему удаляются.
* - количество добавляемых/удаляемых записей псевдослучайно зависит
* от номера транзакции, но с экспоненциальным распределением.
* - размер "скользящего окна" также псевдослучайно зависит от номера
* транзакции с "отрицательным" экспоненциальным распределением
* MAX_WIDTH - exp(rnd(N)), при уменьшении окна сдвигается задний
* край и удаляются записи позади него.
* - групповое добавление данных в начало окна и групповое уделение в конце,
* в половине случаев выполняются во вложенных транзакциях.
* - половина запускаемых вложенных транзакций отменяется, последуюим
* повтором групповой операции.
*
* Таким образом имитируется поведение таблицы с TTL: записи стохастически
* добавляются и удаляются, но изредка происходят массивные удаления. */
/* LY: для параметризации используем подходящие параметры, которые не имеют
* здесь смысла в первоначальном значении. */
const unsigned window_max_lower = 333;
const unsigned count_max_lower = 333;
const unsigned window_max = (config.params.batch_read > window_max_lower)
? config.params.batch_read
: window_max_lower;
const unsigned count_max = (config.params.batch_write > count_max_lower)
? config.params.batch_write
: count_max_lower;
log_verbose("nested: using `batch_read` value %u for window_max", window_max);
log_verbose("nested: using `batch_write` value %u for count_max", count_max);
uint64_t seed = uint64_t seed =
prng64_map2_white(config.params.keygen.seed) + config.actor_id; prng64_map2_white(config.params.keygen.seed) + config.actor_id;
@ -262,10 +236,9 @@ bool testcase_nested::run() {
unsigned loops = 0; unsigned loops = 0;
while (true) { while (true) {
const uint64_t salt = prng64_white(seed) /* mdbx_txn_id(txn_guard.get()) */; const uint64_t salt = prng64_white(seed) /* mdbx_txn_id(txn_guard.get()) */;
const unsigned window_width = (!should_continue() || flipcoin_x4()) const unsigned window_width =
? 0 (!should_continue() || flipcoin_x4()) ? 0 : edge2window(salt);
: edge2window(salt, window_max); const unsigned head_count = edge2count(salt);
const unsigned head_count = edge2count(salt, count_max);
log_debug("nested: step #%zu (serial %" PRIu64 log_debug("nested: step #%zu (serial %" PRIu64
", window %u, count %u) salt %" PRIu64, ", window %u, count %u) salt %" PRIu64,
nops_completed, serial, window_width, head_count, salt); nops_completed, serial, window_width, head_count, salt);
@ -301,7 +274,9 @@ bool testcase_nested::run() {
} }
loops += 1; loops += 1;
} else if (fifo.empty()) { } else if (fifo.empty()) {
log_notice("nested: done %u whole loops", loops); log_notice("nested: done %u whole loops, %" PRIu64 " ops, %" PRIu64
" items",
loops, nops_completed, serial);
break; break;
} else { } else {
log_notice("nested: done, wait for empty, skip head-grow"); log_notice("nested: done, wait for empty, skip head-grow");

View File

@ -394,7 +394,8 @@ bool testcase::should_continue(bool check_timeout_only) const {
result = false; result = false;
} }
if (!check_timeout_only && nops_target && nops_completed >= nops_target) if (!check_timeout_only && config.params.test_nops &&
nops_completed >= config.params.test_nops)
result = false; result = false;
if (result) if (result)

View File

@ -148,7 +148,6 @@ protected:
bool signalled{false}; bool signalled{false};
bool need_speculum_assign{false}; bool need_speculum_assign{false};
unsigned nops_target{config.params.test_nops};
size_t nops_completed{0}; size_t nops_completed{0};
chrono::time start_timestamp; chrono::time start_timestamp;
keygen::buffer key; keygen::buffer key;
@ -229,18 +228,7 @@ public:
virtual ~testcase() {} virtual ~testcase() {}
}; };
class testcase_ttl : public testcase {
using inherited = testcase;
public:
testcase_ttl(const actor_config &config, const mdbx_pid_t pid)
: testcase(config, pid) {}
bool run() override;
};
class testcase_hill : public testcase { class testcase_hill : public testcase {
using inherited = testcase;
public: public:
testcase_hill(const actor_config &config, const mdbx_pid_t pid) testcase_hill(const actor_config &config, const mdbx_pid_t pid)
: testcase(config, pid) {} : testcase(config, pid) {}
@ -293,8 +281,26 @@ public:
bool run() override; bool run() override;
}; };
class testcase_nested : public testcase { class testcase_ttl : public testcase {
using inherited = testcase; using inherited = testcase;
protected:
struct {
unsigned max_window_size{0};
unsigned max_step_size{0};
} sliding;
unsigned edge2window(uint64_t edge);
unsigned edge2count(uint64_t edge);
public:
testcase_ttl(const actor_config &config, const mdbx_pid_t pid)
: inherited(config, pid) {}
bool setup() override;
bool run() override;
};
class testcase_nested : public testcase_ttl {
using inherited = testcase_ttl;
using FIFO = std::deque<std::pair<uint64_t, unsigned>>; using FIFO = std::deque<std::pair<uint64_t, unsigned>>;
uint64_t serial{0}; uint64_t serial{0};
@ -317,7 +323,7 @@ class testcase_nested : public testcase {
public: public:
testcase_nested(const actor_config &config, const mdbx_pid_t pid) testcase_nested(const actor_config &config, const mdbx_pid_t pid)
: testcase(config, pid) {} : inherited(config, pid) {}
bool setup() override; bool setup() override;
bool run() override; bool run() override;
bool teardown() override; bool teardown() override;

View File

@ -16,16 +16,97 @@
#include <cmath> #include <cmath>
#include <deque> #include <deque>
static unsigned edge2window(uint64_t edge, unsigned window_max) { /* LY: тест "эмуляцией time-to-live":
const double rnd = u64_to_double1(bleach64(edge)); * - организуется "скользящее окно", которое двигается вперед вдоль
const unsigned window = window_max - std::lrint(std::pow(window_max, rnd)); * числовой оси каждую транзакцию.
return window; * - по переднему краю "скользящего окна" записи добавляются в таблицу,
* а по заднему удаляются.
* - количество добавляемых/удаляемых записей псевдослучайно зависит
* от номера транзакции, но с экспоненциальным распределением.
* - размер "скользящего окна" также псевдослучайно зависит от номера
* транзакции с "отрицательным" экспоненциальным распределением
* MAX_WIDTH - exp(rnd(N)), при уменьшении окна сдвигается задний
* край и удаляются записи позади него.
*
* Таким образом имитируется поведение таблицы с TTL: записи стохастически
* добавляются и удаляются, но изредка происходит массивное удаление.
*/
unsigned testcase_ttl::edge2count(uint64_t edge) {
const double rnd = u64_to_double1(prng64_map1_white(edge));
const unsigned count = std::lrint(std::pow(sliding.max_step_size, rnd));
// average value: (X - 1) / ln(X), where X = sliding.max_step_size
return count;
} }
static unsigned edge2count(uint64_t edge, unsigned count_max) { unsigned testcase_ttl::edge2window(uint64_t edge) {
const double rnd = u64_to_double1(prng64_map1_white(edge)); const double rnd = u64_to_double1(bleach64(edge));
const unsigned count = std::lrint(std::pow(count_max, rnd)); const unsigned size = sliding.max_window_size -
return count; std::lrint(std::pow(sliding.max_window_size, rnd));
// average value: Y - (Y - 1) / ln(Y), where Y = sliding.max_window_size
return size;
}
static inline double estimate(const double x, const double y) {
/* среднее кол-во операций N = X' * Y', где X' и Y' средние значения
* размера окна и кол-ва добавляемых за один шаг записей:
* X' = (X - 1) / ln(X), где X = sliding.max_step_size
* Y' = Y - (Y - 1) / ln(Y), где Y = sliding.max_window_size */
return (x - 1) / std::log(x) * (y - (y - 1) / std::log(y));
}
bool testcase_ttl::setup() {
const unsigned window_top_lower =
7 /* нижний предел для верхней границы диапазона, в котором будет
стохастически колебаться размер окна */
;
const unsigned count_top_lower =
7 /* нижний предел для верхней границы диапазона, в котором будет
стохастически колебаться кол-во записей добавляемых на одном шаге */
;
/* для параметризации используем подходящие параметры,
* которые не имеют здесь смысла в первоначальном значении. */
const double ratio =
double(config.params.batch_read ? config.params.batch_read : 1) /
double(config.params.batch_write ? config.params.batch_write : 1);
/* проще найти двоичным поиском (вариация метода Ньютона) */
double hi = config.params.test_nops, lo = 1;
double x = std::sqrt(hi + lo) / ratio;
while (hi > lo) {
const double n = estimate(x, x * ratio);
if (n > config.params.test_nops)
hi = x - 1;
else
lo = x + 1;
x = (hi + lo) / 2;
}
sliding.max_step_size = std::lrint(x);
if (sliding.max_step_size < count_top_lower)
sliding.max_step_size = count_top_lower;
sliding.max_window_size = std::lrint(x * ratio);
if (sliding.max_window_size < window_top_lower)
sliding.max_window_size = window_top_lower;
while (estimate(sliding.max_step_size, sliding.max_window_size) >
config.params.test_nops * 2.0) {
if (ratio * sliding.max_step_size > sliding.max_window_size) {
if (sliding.max_step_size < count_top_lower)
break;
sliding.max_step_size = sliding.max_step_size * 7 / 8;
} else {
if (sliding.max_window_size < window_top_lower)
break;
sliding.max_window_size = sliding.max_window_size * 7 / 8;
}
}
log_verbose("come up window_max %u from `batch_read`",
sliding.max_window_size);
log_verbose("come up step_max %u from `batch_write`", sliding.max_step_size);
return inherited::setup();
} }
bool testcase_ttl::run() { bool testcase_ttl::run() {
@ -35,36 +116,6 @@ bool testcase_ttl::run() {
return false; return false;
} }
/* LY: тест "эмуляцией time-to-live":
* - организуется "скользящее окно", которое двигается вперед вдоль
* числовой оси каждую транзакцию.
* - по переднему краю "скользящего окна" записи добавляются в таблицу,
* а по заднему удаляются.
* - количество добавляемых/удаляемых записей псевдослучайно зависит
* от номера транзакции, но с экспоненциальным распределением.
* - размер "скользящего окна" также псевдослучайно зависит от номера
* транзакции с "отрицательным" экспоненциальным распределением
* MAX_WIDTH - exp(rnd(N)), при уменьшении окна сдвигается задний
* край и удаляются записи позади него.
*
* Таким образом имитируется поведение таблицы с TTL: записи стохастически
* добавляются и удаляются, но изредка происходят массивные удаления.
*/
/* LY: для параметризации используем подходящие параметры, которые не имеют
* здесь смысла в первоначальном значении. */
const unsigned window_max_lower = 333;
const unsigned count_max_lower = 333;
const unsigned window_max = (config.params.batch_read > window_max_lower)
? config.params.batch_read
: window_max_lower;
const unsigned count_max = (config.params.batch_write > count_max_lower)
? config.params.batch_write
: count_max_lower;
log_verbose("ttl: using `batch_read` value %u for window_max", window_max);
log_verbose("ttl: using `batch_write` value %u for count_max", count_max);
uint64_t seed = uint64_t seed =
prng64_map2_white(config.params.keygen.seed) + config.actor_id; prng64_map2_white(config.params.keygen.seed) + config.actor_id;
keyvalue_maker.setup(config.params, config.actor_id, 0 /* thread_number */); keyvalue_maker.setup(config.params, config.actor_id, 0 /* thread_number */);
@ -85,10 +136,9 @@ bool testcase_ttl::run() {
while (true) { while (true) {
const uint64_t salt = prng64_white(seed) /* mdbx_txn_id(txn_guard.get()) */; const uint64_t salt = prng64_white(seed) /* mdbx_txn_id(txn_guard.get()) */;
const unsigned window_width = (!should_continue() || flipcoin_x4()) const unsigned window_width =
? 0 (!should_continue() || flipcoin_x4()) ? 0 : edge2window(salt);
: edge2window(salt, window_max); unsigned head_count = edge2count(salt);
unsigned head_count = edge2count(salt, count_max);
log_debug("ttl: step #%zu (serial %" PRIu64 log_debug("ttl: step #%zu (serial %" PRIu64
", window %u, count %u) salt %" PRIu64, ", window %u, count %u) salt %" PRIu64,
nops_completed, serial, window_width, head_count, salt); nops_completed, serial, window_width, head_count, salt);
@ -115,7 +165,7 @@ bool testcase_ttl::run() {
if (unlikely(!keyvalue_maker.increment(tail_serial, 1))) if (unlikely(!keyvalue_maker.increment(tail_serial, 1)))
failure("ttl: unexpected key-space overflow on the tail"); failure("ttl: unexpected key-space overflow on the tail");
} }
report(1); report(tail_count);
} }
} else { } else {
log_trace("ttl: purge state"); log_trace("ttl: purge state");
@ -185,7 +235,8 @@ bool testcase_ttl::run() {
} }
loops += 1; loops += 1;
} else if (fifo.empty()) { } else if (fifo.empty()) {
log_notice("ttl: done %u whole loops", loops); log_notice("ttl: done %u whole loops, %" PRIu64 " ops, %" PRIu64 " items",
loops, nops_completed, serial);
rc = true; rc = true;
break; break;
} else { } else {