/* * Copyright (C) 2011-2017 Redis Labs Ltd. * * This file is part of memtier_benchmark. * * memtier_benchmark is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 2. * * memtier_benchmark is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with memtier_benchmark. If not, see . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #ifdef HAVE_ASSERT_H #include #endif #include "obj_gen.h" #include "memtier_benchmark.h" random_generator::random_generator() { set_seed(0); } void random_generator::set_seed(int seed) { seed++; //http://stackoverflow.com/questions/27386470/srand0-and-srand1-give-the-same-results #ifdef HAVE_RANDOM_R memset(&m_data_blob, 0, sizeof(m_data_blob)); memset(m_state_array, 0, sizeof(m_state_array)); int ret = initstate_r(seed, m_state_array, sizeof(m_state_array), &m_data_blob); assert(ret == 0); #elif (defined HAVE_DRAND48) memset(&m_data_blob, 0, sizeof(m_data_blob)); size_t seed_size = sizeof(seed); //get MIN size between seed and m_data_blob if (seed_size > sizeof(m_data_blob)) seed_size = sizeof(m_data_blob); memcpy(&m_data_blob, &seed, seed_size); #endif } unsigned long long random_generator::get_random() { unsigned long long llrn; #ifdef HAVE_RANDOM_R int32_t rn; // max is RAND_MAX, which is usually 2^31-1 (although can be as low as 2^16-1, which we ignore now) // this is fine, especially considering that random_r is a nonstandard glibc extension // it returns a positive int32_t, so either way the MSB is off int ret = random_r(&m_data_blob, &rn); assert(ret == 0); llrn = rn; llrn = llrn << 31; ret = random_r(&m_data_blob, &rn); assert(ret == 0); llrn |= rn; #elif (defined HAVE_DRAND48) long rn; // jrand48's range is -2^31..+2^31 (i.e. all 32 bits) rn = jrand48(m_data_blob); llrn = rn; llrn = llrn << 32; rn = jrand48(m_data_blob); llrn |= rn & 0xffffffff; // reset the sign extension bits of negative numbers llrn &= 0x8000000000000000; // avoid any trouble from sign mismatch and negative numbers #else #error no random function #endif return llrn; } unsigned long long random_generator::get_random_max() const { #ifdef HAVE_RANDOM_R return 0x3fffffffffffffff;//62 bits #elif (defined HAVE_DRAND48) return 0x7fffffffffffffff;//63 bits #endif } //returns a value surrounding 0 double gaussian_noise::gaussian_distribution(const double &stddev) { // Box–Muller transform (Marsaglia polar method) if (m_hasSpare) { m_hasSpare = false; return stddev * m_spare; } m_hasSpare = true; double u, v, s; do { u = (get_random() / ((double) get_random_max())) * 2 - 1; v = (get_random() / ((double) get_random_max())) * 2 - 1; s = u * u + v * v; } while(s >= 1 || s == 0); s = sqrt(-2.0 * log(s) / s); m_spare = v * s; return stddev * u * s; } unsigned long long gaussian_noise::gaussian_distribution_range(double stddev, double median, unsigned long long min, unsigned long long max) { if (min==max) return min; unsigned long long len = max-min; double val; if (median == 0) median = len / 2.0 + min + 0.5; if (stddev == 0) stddev = len / 6.0; assert(median > min && median < max); do { val = gaussian_distribution(stddev) + median; } while(val < min || val > max + 1); return val; } object_generator::object_generator(size_t n_key_iterators/*= OBJECT_GENERATOR_KEY_ITERATORS*/) : m_data_size_type(data_size_unknown), m_data_size_pattern(NULL), m_random_data(false), m_expiry_min(0), m_expiry_max(0), m_key_prefix(NULL), m_key_min(0), m_key_max(0), m_key_stddev(0), m_key_median(0), m_value_buffer(NULL), m_random_fd(-1), m_value_buffer_size(0), m_value_buffer_mutation_pos(0) { m_next_key.resize(n_key_iterators, 0); m_data_size.size_list = NULL; } object_generator::object_generator(const object_generator& copy) : m_data_size_type(copy.m_data_size_type), m_data_size(copy.m_data_size), m_data_size_pattern(copy.m_data_size_pattern), m_random_data(copy.m_random_data), m_expiry_min(copy.m_expiry_min), m_expiry_max(copy.m_expiry_max), m_key_prefix(copy.m_key_prefix), m_key_min(copy.m_key_min), m_key_max(copy.m_key_max), m_key_stddev(copy.m_key_stddev), m_key_median(copy.m_key_median), m_value_buffer(NULL), m_random_fd(-1), m_value_buffer_size(0), m_value_buffer_mutation_pos(0) { if (m_data_size_type == data_size_weighted && m_data_size.size_list != NULL) { m_data_size.size_list = new config_weight_list(*m_data_size.size_list); } alloc_value_buffer(copy.m_value_buffer); m_next_key.resize(copy.m_next_key.size(), 0); } object_generator::~object_generator() { if (m_value_buffer != NULL) free(m_value_buffer); if (m_data_size_type == data_size_weighted && m_data_size.size_list != NULL) { delete m_data_size.size_list; } if (m_random_fd != -1) { close(m_random_fd); m_random_fd = -1; } } object_generator* object_generator::clone(void) { return new object_generator(*this); } void object_generator::set_random_seed(int seed) { m_random.set_seed(seed); } void object_generator::alloc_value_buffer(void) { unsigned int size = 0; if (m_value_buffer != NULL) free(m_value_buffer), m_value_buffer = NULL; if (m_data_size_type == data_size_fixed) size = m_data_size.size_fixed; else if (m_data_size_type == data_size_range) size = m_data_size.size_range.size_max; else if (m_data_size_type == data_size_weighted) { size = m_data_size.size_list->largest(); } m_value_buffer_size = size; if (size > 0) { m_value_buffer = (char*) malloc(size); assert(m_value_buffer != NULL); if (!m_random_data) { memset(m_value_buffer, 'x', size); } else { if (m_random_fd == -1) { m_random_fd = open("/dev/urandom", O_RDONLY); assert(m_random_fd != -1); } char buf1[64] = { 0 }; char buf2[64] = { 0 }; unsigned int buf1_idx = sizeof(buf1); unsigned int buf2_idx = sizeof(buf2); char *d = m_value_buffer; int ret; int iter = 0; while (d - m_value_buffer < size) { if (buf1_idx == sizeof(buf1)) { buf1_idx = 0; buf2_idx++; if (buf2_idx >= sizeof(buf2)) { iter++; if (iter == 20) { ret = read(m_random_fd, buf1, sizeof(buf1)); assert(ret > -1); ret = read(m_random_fd, buf2, sizeof(buf2)); assert(ret > -1); buf1_idx = buf2_idx = iter = 0; } } } *d = buf1[buf1_idx] ^ buf2[buf2_idx] ^ iter; d++; buf1_idx++; } } } } void object_generator::alloc_value_buffer(const char* copy_from) { unsigned int size = 0; if (m_value_buffer != NULL) free(m_value_buffer), m_value_buffer = NULL; if (m_data_size_type == data_size_fixed) size = m_data_size.size_fixed; else if (m_data_size_type == data_size_range) size = m_data_size.size_range.size_max; else if (m_data_size_type == data_size_weighted) size = m_data_size.size_list->largest(); m_value_buffer_size = size; if (size > 0) { m_value_buffer = (char*) malloc(size); assert(m_value_buffer != NULL); memcpy(m_value_buffer, copy_from, size); } } void object_generator::set_random_data(bool random_data) { m_random_data = random_data; } void object_generator::set_data_size_fixed(unsigned int size) { m_data_size_type = data_size_fixed; m_data_size.size_fixed = size; alloc_value_buffer(); } void object_generator::set_data_size_range(unsigned int size_min, unsigned int size_max) { m_data_size_type = data_size_range; m_data_size.size_range.size_min = size_min; m_data_size.size_range.size_max = size_max; alloc_value_buffer(); } void object_generator::set_data_size_list(config_weight_list* size_list) { if (m_data_size_type == data_size_weighted && m_data_size.size_list != NULL) { delete m_data_size.size_list; } m_data_size_type = data_size_weighted; m_data_size.size_list = new config_weight_list(*size_list); alloc_value_buffer(); } void object_generator::set_data_size_pattern(const char* pattern) { m_data_size_pattern = pattern; } void object_generator::set_expiry_range(unsigned int expiry_min, unsigned int expiry_max) { m_expiry_min = expiry_min; m_expiry_max = expiry_max; } void object_generator::set_key_prefix(const char *key_prefix) { m_key_prefix = key_prefix; } void object_generator::set_key_range(unsigned long long key_min, unsigned long long key_max) { m_key_min = key_min; m_key_max = key_max; } void object_generator::set_key_distribution(double key_stddev, double key_median) { m_key_stddev = key_stddev; m_key_median = key_median; } // return a random number between r_min and r_max unsigned long long object_generator::random_range(unsigned long long r_min, unsigned long long r_max) { unsigned long long rn = m_random.get_random(); return (rn % (r_max - r_min + 1)) + r_min; } // return a random number between r_min and r_max using normal distribution according to r_stddev unsigned long long object_generator::normal_distribution(unsigned long long r_min, unsigned long long r_max, double r_stddev, double r_median) { return m_random.gaussian_distribution_range(r_stddev, r_median, r_min, r_max); } unsigned long long object_generator::get_key_index(int iter) { assert(iter < static_cast(m_next_key.size()) && iter >= OBJECT_GENERATOR_KEY_GAUSSIAN); unsigned long long k; if (iter==OBJECT_GENERATOR_KEY_RANDOM) { k = random_range(m_key_min, m_key_max); } else if(iter==OBJECT_GENERATOR_KEY_GAUSSIAN) { k = normal_distribution(m_key_min, m_key_max, m_key_stddev, m_key_median); } else { if (m_next_key[iter] < m_key_min) m_next_key[iter] = m_key_min; k = m_next_key[iter]; m_next_key[iter]++; if (m_next_key[iter] > m_key_max) m_next_key[iter] = m_key_min; } return k; } const char* object_generator::get_key(int iter, unsigned int *len) { unsigned int l; m_key_index = get_key_index(iter); // format key l = snprintf(m_key_buffer, sizeof(m_key_buffer)-1, "%s%llu", m_key_prefix, m_key_index); if (len != NULL) *len = l; return m_key_buffer; } data_object* object_generator::get_object(int iter) { // compute key (void) get_key(iter, NULL); // compute value unsigned int new_size = 0; get_value(m_key_index, &new_size); // compute expiry unsigned int expiry = get_expiry(); // set object m_object.set_key(m_key_buffer, strlen(m_key_buffer)); m_object.set_value(m_value_buffer, new_size); m_object.set_expiry(expiry); return &m_object; } const char* object_generator::get_key_prefix() { return m_key_prefix; } const char* object_generator::get_value(unsigned long long key_index, unsigned int *len) { // compute size unsigned int new_size = 0; if (m_data_size_type == data_size_fixed) { new_size = m_data_size.size_fixed; } else if (m_data_size_type == data_size_range) { if (m_data_size_pattern && *m_data_size_pattern=='S') { double a = (key_index-m_key_min)/static_cast(m_key_max-m_key_min); new_size = (m_data_size.size_range.size_max-m_data_size.size_range.size_min)*a + m_data_size.size_range.size_min; } else { new_size = random_range(m_data_size.size_range.size_min > 0 ? m_data_size.size_range.size_min : 1, m_data_size.size_range.size_max); } } else if (m_data_size_type == data_size_weighted) { new_size = m_data_size.size_list->get_next_size(); } else { assert(0); } // modify object content in case of random data if (m_random_data) { m_value_buffer[m_value_buffer_mutation_pos++]++; if (m_value_buffer_mutation_pos >= m_value_buffer_size) m_value_buffer_mutation_pos = 0; } *len = new_size; return m_value_buffer; } unsigned int object_generator::get_expiry() { // compute expiry unsigned int expiry = 0; if (m_expiry_max > 0) { expiry = random_range(m_expiry_min, m_expiry_max); } return expiry; } /////////////////////////////////////////////////////////////////////////// data_object::data_object() : m_key(NULL), m_key_len(0), m_value(NULL), m_value_len(0), m_expiry(0) { } data_object::~data_object() { clear(); } void data_object::clear(void) { m_key = NULL; m_key_len = 0; m_value = NULL; m_value_len = 0; m_expiry = 0; } void data_object::set_key(const char* key, unsigned int key_len) { m_key = key; m_key_len = key_len; } const char* data_object::get_key(unsigned int* key_len) { assert(key_len != NULL); *key_len = m_key_len; return m_key; } void data_object::set_value(const char* value, unsigned int value_len) { m_value = value; m_value_len = value_len; } const char* data_object::get_value(unsigned int *value_len) { assert(value_len != NULL); *value_len = m_value_len; return m_value; } void data_object::set_expiry(unsigned int expiry) { m_expiry = expiry; } unsigned int data_object::get_expiry(void) { return m_expiry; } /////////////////////////////////////////////////////////////////////////// imported_keylist::imported_keylist(const char *filename) : m_filename(filename) { } imported_keylist::~imported_keylist() { while (!m_keys.empty()) { free(m_keys.front()); m_keys.erase(m_keys.begin()); } } bool imported_keylist::read_keys(void) { file_reader f(m_filename); if (!f.open_file()) return false; while (!f.is_eof()) { memcache_item *i = f.read_item(); if (i != NULL) { key* k = (key*) malloc(i->get_nkey() + sizeof(key) + 1); assert(k != NULL); k->key_len = i->get_nkey(); memcpy(k->key_data, i->get_key(), i->get_nkey()); delete i; m_keys.push_back(k); } } return true; } unsigned int imported_keylist::size(void) { return m_keys.size(); } const char* imported_keylist::get(unsigned int pos, unsigned int *len) { if (pos >= m_keys.size()) return NULL; key* k = m_keys[pos]; if (len != NULL) *len = k->key_len; return k->key_data; } /////////////////////////////////////////////////////////////////////////// import_object_generator::import_object_generator(const char *filename, imported_keylist *keys, bool no_expiry) : m_keys(keys), m_reader(filename), m_cur_item(NULL), m_reader_opened(false), m_no_expiry(no_expiry) { if (m_keys != NULL) { m_key_max = m_keys->size(); m_key_min = 1; } } import_object_generator::~import_object_generator() { if (m_cur_item != NULL) delete m_cur_item; } import_object_generator::import_object_generator(const import_object_generator& from) : object_generator(from), m_keys(from.m_keys), m_reader(from.m_reader), m_cur_item(NULL), m_no_expiry(from.m_no_expiry) { if (m_keys != NULL) { m_key_max = m_keys->size(); m_key_min = 1; } if (from.m_reader_opened) { bool r = m_reader.open_file(); assert(r == true); } } bool import_object_generator::open_file(void) { m_reader_opened = true; return m_reader.open_file(); } import_object_generator* import_object_generator::clone(void) { return new import_object_generator(*this); } const char* import_object_generator::get_key(int iter, unsigned int *len) { if (m_keys == NULL) { return object_generator::get_key(iter, len); } else { unsigned int k = get_key_index(iter) - 1; return m_keys->get(k, len); } } data_object* import_object_generator::get_object(int iter) { memcache_item *i = m_reader.read_item(); if (i == NULL && m_reader.is_eof()) { m_reader.open_file(); i = m_reader.read_item(); } assert(i != NULL); if (m_cur_item != NULL) { delete m_cur_item; } m_cur_item = i; m_object.set_value(m_cur_item->get_data(), m_cur_item->get_nbytes() - 2); if (m_keys != NULL) { m_object.set_key(m_cur_item->get_key(), m_cur_item->get_nkey()); } else { unsigned int tmplen; const char *tmpkey = object_generator::get_key(iter, &tmplen); m_object.set_key(tmpkey, tmplen); } // compute expiry int expiry = 0; if (!m_no_expiry) { if (m_expiry_max > 0) { expiry = random_range(m_expiry_min, m_expiry_max); } else { expiry = m_cur_item->get_exptime(); } m_object.set_expiry(expiry); } return &m_object; }