Simplify string generation

This commit is contained in:
Junhyun Shim 2022-04-20 16:26:48 +02:00
parent d202d482d9
commit b7e493f030
5 changed files with 75 additions and 90 deletions

View File

@ -40,8 +40,8 @@ void ResumableStateForPopulate::postNextTick() {
void ResumableStateForPopulate::runOneTick() {
const auto num_commit_every = args.txnspec.ops[OP_INSERT][OP_COUNT];
for (auto i = key_checkpoint; i <= key_end; i++) {
genKey(keystr, KEY_PREFIX, args, i);
randomString(valstr, args.value_length);
genKey(keystr.data(), KEY_PREFIX, args, i);
randomString(valstr.data(), args.value_length);
tx.set(keystr, valstr);
stats.incrOpCount(OP_INSERT);
if (i == key_end || (i - key_begin + 1) % num_commit_every == 0) {

View File

@ -62,8 +62,8 @@ struct ResumableStateForPopulate : std::enable_shared_from_this<ResumableStateFo
int key_end)
: logr(logr), db(db), tx(tx), io_context(io_context), args(args), stats(stats), stopcount(stopcount),
key_begin(key_begin), key_end(key_end), key_checkpoint(key_begin) {
keystr.reserve(args.key_length);
valstr.reserve(args.value_length);
keystr.resize(args.key_length);
valstr.resize(args.value_length);
}
void runOneTick();
void postNextTick();
@ -105,9 +105,9 @@ struct ResumableStateForRunWorkload : std::enable_shared_from_this<ResumableStat
OpIterator iter)
: logr(logr), db(db), tx(tx), io_context(io_context), args(args), stats(stats), stopcount(stopcount),
signal(signal), max_iters(max_iters), iter(iter), needs_commit(false) {
key1.reserve(args.key_length);
key2.reserve(args.key_length);
val.reserve(args.value_length);
key1.resize(args.key_length);
key2.resize(args.key_length);
val.resize(args.value_length);
}
void signalEnd() noexcept { stopcount.fetch_add(1); }
bool ended() noexcept {

View File

@ -88,15 +88,21 @@ thread_local Logger logr = Logger(MainProcess{}, VERBOSE_DEFAULT);
/* cleanup database */
int cleanup(Transaction tx, Arguments const& args) {
auto beginstr = ByteString{};
beginstr.reserve(args.key_length);
genKeyPrefix(beginstr, KEY_PREFIX, args);
beginstr.push_back(0);
auto endstr = ByteString{};
endstr.reserve(args.key_length);
genKeyPrefix(endstr, KEY_PREFIX, args);
endstr.push_back(0xff);
const auto prefix_len = args.prefixpadding ? args.key_length - args.row_digits : intSize(KEY_PREFIX);
auto genprefix = [&args](ByteString& s) {
const auto padding_len = args.key_length - intSize(KEY_PREFIX) - args.row_digits;
auto pos = 0;
if (args.prefixpadding) {
memset(s.data(), 'x', padding_len);
pos += padding_len;
}
const auto key_prefix_len = intSize(KEY_PREFIX);
memcpy(&s[pos], KEY_PREFIX.data(), key_prefix_len);
};
auto beginstr = ByteString(prefix_len + 1, '\0');
genprefix(beginstr);
auto endstr = ByteString(prefix_len + 1, '\xff');
genprefix(endstr);
auto watch = Stopwatch(StartAtCtor{});
@ -133,8 +139,8 @@ int populate(Transaction tx,
auto keystr = ByteString{};
auto valstr = ByteString{};
keystr.reserve(args.key_length);
valstr.reserve(args.value_length);
keystr.resize(args.key_length);
valstr.resize(args.value_length);
const auto num_commit_every = args.txnspec.ops[OP_INSERT][OP_COUNT];
const auto num_seconds_trace_every = args.txntrace;
auto watch_total = Stopwatch(StartAtCtor{});
@ -145,9 +151,9 @@ int populate(Transaction tx,
for (auto i = key_begin; i <= key_end; i++) {
/* sequential keys */
genKey(keystr, KEY_PREFIX, args, i);
genKey(keystr.data(), KEY_PREFIX, args, i);
/* random values */
randomString(valstr, args.value_length);
randomString(valstr.data(), args.value_length);
while (thread_tps > 0 && xacts >= thread_tps /* throttle */) {
if (toIntegerSeconds(watch_throttle.stop().diff()) >= 1) {
@ -369,11 +375,11 @@ int runWorkload(Transaction tx,
// reuse memory for keys to avoid realloc overhead
auto key1 = ByteString{};
key1.reserve(args.key_length);
key1.resize(args.key_length);
auto key2 = ByteString{};
key2.reserve(args.key_length);
key2.resize(args.key_length);
auto val = ByteString{};
val.reserve(args.value_length);
val.resize(args.value_length);
/* main transaction loop */
while (1) {

View File

@ -124,7 +124,7 @@ const std::array<Operation, MAX_OP> opTable{
} },
{ StepKind::IMM,
[](Transaction& tx, Arguments const& args, ByteString& key, ByteString&, ByteString& value) {
randomString(value, args.value_length);
randomString(value.data(), args.value_length);
tx.set(key, value);
return Future();
} } },
@ -133,10 +133,9 @@ const std::array<Operation, MAX_OP> opTable{
{ "INSERT",
{ { StepKind::IMM,
[](Transaction& tx, Arguments const& args, ByteString& key, ByteString&, ByteString& value) {
genKeyPrefix(key, KEY_PREFIX, args);
// concat([padding], key_prefix, random_string): reasonably unique
randomString<false /*clear-before-append*/>(key, args.key_length - static_cast<int>(key.size()));
randomString(value, args.value_length);
// key[0..args.key_length] := concat(key_prefix, random_string)
randomString(key.data() + intSize(KEY_PREFIX), args.key_length - intSize(KEY_PREFIX));
randomString(value.data(), args.value_length);
tx.set(key, value);
return Future();
} } },
@ -145,20 +144,17 @@ const std::array<Operation, MAX_OP> opTable{
{ "INSERTRANGE",
{ { StepKind::IMM,
[](Transaction& tx, Arguments const& args, ByteString& key, ByteString&, ByteString& value) {
genKeyPrefix(key, KEY_PREFIX, args);
const auto prefix_len = static_cast<int>(key.size());
randomString(value.data(), args.value_length);
// key[0..args.key_length] := concat(prefix, random_string, num[0..range_digits])
const auto range = args.txnspec.ops[OP_INSERTRANGE][OP_RANGE];
assert(range > 0);
const auto range_digits = digits(range);
assert(args.key_length - prefix_len >= range_digits);
const auto rand_len = args.key_length - prefix_len - range_digits;
// concat([padding], prefix, random_string, range_digits)
randomString<false /*clear-before-append*/>(key, rand_len);
randomString(value, args.value_length);
const auto random_len = args.key_length - intSize(KEY_PREFIX) - range_digits;
randomString(&key[intSize(KEY_PREFIX)], random_len);
for (auto i = 0; i < range; i++) {
fmt::format_to(std::back_inserter(key), "{0:0{1}d}", i, range_digits);
numericWithFill(&key[args.key_length - range_digits], range_digits, i);
tx.set(key, value);
key.resize(key.size() - static_cast<size_t>(range_digits));
}
return Future();
} } },
@ -167,7 +163,7 @@ const std::array<Operation, MAX_OP> opTable{
{ "OVERWRITE",
{ { StepKind::IMM,
[](Transaction& tx, Arguments const& args, ByteString& key, ByteString&, ByteString& value) {
randomString(value, args.value_length);
randomString(value.data(), args.value_length);
tx.set(key, value);
return Future();
} } },
@ -184,10 +180,8 @@ const std::array<Operation, MAX_OP> opTable{
{ "SETCLEAR",
{ { StepKind::COMMIT,
[](Transaction& tx, Arguments const& args, ByteString& key, ByteString&, ByteString& value) {
genKeyPrefix(key, KEY_PREFIX, args);
const auto prefix_len = static_cast<int>(key.size());
randomString<false /*clear-before-append*/>(key, args.key_length - prefix_len);
randomString(value, args.value_length);
randomString(&key[KEY_PREFIX.size()], args.key_length - intSize(KEY_PREFIX));
randomString(value.data(), args.value_length);
tx.set(key, value);
return tx.commit().eraseType();
} },
@ -210,26 +204,19 @@ const std::array<Operation, MAX_OP> opTable{
{ "SETCLEARRANGE",
{ { StepKind::COMMIT,
[](Transaction& tx, Arguments const& args, ByteString& key_begin, ByteString& key, ByteString& value) {
genKeyPrefix(key, KEY_PREFIX, args);
const auto prefix_len = static_cast<int>(key.size());
const auto range = args.txnspec.ops[OP_SETCLEARRANGE][OP_RANGE];
randomString(value.data(), args.value_length);
// key[0..args.key_length] := concat(prefix, random_string, num[0..range_digits])
const auto range = args.txnspec.ops[OP_INSERTRANGE][OP_RANGE];
assert(range > 0);
const auto range_digits = digits(range);
assert(args.key_length - prefix_len >= range_digits);
const auto rand_len = args.key_length - prefix_len - range_digits;
// concat([padding], prefix, random_string, range_digits)
randomString<false /*clear-before-append*/>(key, rand_len);
randomString(value, args.value_length);
for (auto i = 0; i <= range; i++) {
fmt::format_to(std::back_inserter(key), "{0:0{1}d}", i, range_digits);
if (i == range)
break; // preserve "exclusive last"
// preserve first key for step 1
if (i == 0)
key_begin = key;
const auto random_len = args.key_length - intSize(KEY_PREFIX) - range_digits;
randomString(&key[KEY_PREFIX.size()], random_len);
for (auto i = 0; i < range; i++) {
numericWithFill(&key[args.key_length - range_digits], range_digits, i);
tx.set(key, value);
// preserve last key for step 1
key.resize(key.size() - static_cast<size_t>(range_digits));
if (i == 0)
key_begin.assign(key);
}
return tx.commit().eraseType();
} },

View File

@ -28,6 +28,7 @@
#include <cassert>
#include <chrono>
#include <cstdint>
#include <string_view>
#include <type_traits>
#include <fmt/format.h>
@ -48,15 +49,16 @@ force_inline int nextKey(Arguments const& args) {
return urand(0, args.rows - 1);
}
force_inline int intSize(std::string_view sv) {
return static_cast<int>(sv.size());
}
/* random string */
template <bool Clear = true, typename Char>
void randomString(std::basic_string<Char>& str, int len) {
if constexpr (Clear)
str.clear();
template <typename Char>
void randomString(Char* str, int len) {
assert(len >= 0);
str.reserve(str.size() + static_cast<size_t>(len));
for (auto i = 0; i < len; i++) {
str.push_back('!' + urand(0, 'z' - '!')); /* generage a char from '!' to 'z' */
str[i] = ('!' + urand(0, 'z' - '!')); /* generate a char from '!' to 'z' */
}
}
@ -105,47 +107,37 @@ int computeThreadPortion(int val, int p_idx, int t_idx, int total_p, int total_t
/* get the number of digits */
int digits(int num);
/* fill (str) with configured key prefix: i.e. non-numeric part
* (str) is appended with concat([padding], PREFIX)
*/
template <bool Clear = true, typename Char>
void genKeyPrefix(std::basic_string<Char>& str, std::string_view prefix, Arguments const& args) {
// concat('x' * padding_len, key_prefix)
if constexpr (Clear)
str.clear();
const auto padding_len =
args.prefixpadding ? (args.key_length - args.row_digits - static_cast<int>(prefix.size())) : 0;
assert(padding_len >= 0);
str.reserve(str.size() + padding_len + prefix.size());
for (auto i = 0; i < padding_len; i++)
str.push_back('x');
str.append(reinterpret_cast<Char const*>(prefix.data()), prefix.size());
/* fill memory slice [str, str + len) as stringified, zero-padded num */
template <typename Char>
void numericWithFill(Char* str, int len, int num) {
static_assert(sizeof(Char) == 1);
assert(num >= 0);
for (auto i = len - 1; i >= 0; i--) {
str[i] = (num % 10) + '0';
num /= 10;
}
}
/* generate a key for a given key number */
/* prefix is "mako" by default, prefixpadding = 1 means 'x' will be in front rather than trailing the keyname */
template <typename Char>
void genKey(std::basic_string<Char>& str, std::string_view prefix, Arguments const& args, int num) {
void genKey(Char* str, std::string_view prefix, Arguments const& args, int num) {
static_assert(sizeof(Char) == 1);
str.clear();
str.resize(args.key_length, 'x');
memset(str, 'x', args.key_length);
const auto prefix_len = static_cast<int>(prefix.size());
auto pos = args.prefixpadding ? (args.key_length - prefix_len - args.row_digits) : 0;
memcpy(&str[pos], prefix.data(), prefix.size());
memcpy(&str[pos], prefix.data(), prefix_len);
pos += prefix_len;
for (auto i = 0; i < args.row_digits; i++) {
str[pos + (args.row_digits - i - 1)] = (num % 10) + '0';
num /= 10;
}
numericWithFill(&str[pos], args.row_digits, num);
}
template <typename Char>
void prepareKeys(int op, std::basic_string<Char>& key1, std::basic_string<Char>& key2, Arguments const& args) {
const auto key1_num = nextKey(args);
genKey(key1, KEY_PREFIX, args, key1_num);
genKey(key1.data(), KEY_PREFIX, args, key1_num);
if (args.txnspec.ops[op][OP_RANGE] > 0) {
const auto key2_num = std::min(key1_num + args.txnspec.ops[op][OP_RANGE] - 1, args.rows - 1);
genKey(key2, KEY_PREFIX, args, key2_num);
genKey(key2.data(), KEY_PREFIX, args, key2_num);
}
}