#include <cassert>
#include <cerrno>
#include <cinttypes>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fcntl.h>
#include <unistd.h>
#include "benchmark.hpp"
#include "libpmemobj.h"
#include "os.h"
#include "valgrind_internal.h"
#ifdef __cplusplus
extern "C" {
#endif
#include "memops.h"
#include "pmalloc.h"
#include "redo.h"
#ifdef __cplusplus
}
#endif
#define FACTOR 1.2f
#define ALLOC_MIN_SIZE 64
#define OOB_HEADER_SIZE 64
struct prog_args {
size_t minsize;
bool use_random_size;
unsigned seed;
};
POBJ_LAYOUT_BEGIN(pmalloc_layout);
POBJ_LAYOUT_ROOT(pmalloc_layout, struct my_root);
POBJ_LAYOUT_TOID(pmalloc_layout, uint64_t);
POBJ_LAYOUT_END(pmalloc_layout);
struct my_root {
TOID(uint64_t) offs;
};
struct obj_bench {
PMEMobjpool *pop;
struct prog_args *pa;
size_t *sizes;
TOID(struct my_root) root;
uint64_t *offs;
};
static int
obj_init(struct benchmark *bench, struct benchmark_args *args)
{
assert(bench != NULL);
assert(args != NULL);
assert(args->opts != NULL);
if (((struct prog_args *)(args->opts))->minsize >= args->dsize) {
fprintf(stderr, "Wrong params - allocation size\n");
return -1;
}
struct obj_bench *ob =
(struct obj_bench *)malloc(sizeof(struct obj_bench));
if (ob == NULL) {
perror("malloc");
return -1;
}
pmembench_set_priv(bench, ob);
ob->pa = (struct prog_args *)args->opts;
size_t n_ops_total = args->n_ops_per_thread * args->n_threads;
assert(n_ops_total != 0);
size_t alloc_size = args->dsize;
if (alloc_size < ALLOC_MIN_SIZE)
alloc_size = ALLOC_MIN_SIZE;
size_t poolsize = PMEMOBJ_MIN_POOL +
(n_ops_total * (alloc_size + OOB_HEADER_SIZE))
+ n_ops_total * sizeof(uint64_t);
poolsize = (size_t)(poolsize * FACTOR);
if (args->is_poolset) {
if (args->fsize < poolsize) {
fprintf(stderr, "insufficient size of poolset\n");
goto free_ob;
}
poolsize = 0;
} else {
if (poolsize < PMEMOBJ_MIN_POOL)
poolsize = PMEMOBJ_MIN_POOL;
}
ob->pop = pmemobj_create(args->fname, POBJ_LAYOUT_NAME(pmalloc_layout),
poolsize, args->fmode);
if (ob->pop == NULL) {
fprintf(stderr, "%s\n", pmemobj_errormsg());
goto free_ob;
}
ob->root = POBJ_ROOT(ob->pop, struct my_root);
if (TOID_IS_NULL(ob->root)) {
fprintf(stderr, "POBJ_ROOT: %s\n", pmemobj_errormsg());
goto free_pop;
}
POBJ_ZALLOC(ob->pop, &D_RW(ob->root)->offs, uint64_t,
n_ops_total * sizeof(PMEMoid));
if (TOID_IS_NULL(D_RW(ob->root)->offs)) {
fprintf(stderr, "POBJ_ZALLOC off_vect: %s\n",
pmemobj_errormsg());
goto free_pop;
}
ob->offs = D_RW(D_RW(ob->root)->offs);
ob->sizes = (size_t *)malloc(n_ops_total * sizeof(size_t));
if (ob->sizes == NULL) {
fprintf(stderr, "malloc rand size vect err\n");
goto free_pop;
}
if (ob->pa->use_random_size) {
size_t width = args->dsize - ob->pa->minsize;
for (size_t i = 0; i < n_ops_total; i++) {
uint32_t hr = (uint32_t)os_rand_r(&ob->pa->seed);
uint32_t lr = (uint32_t)os_rand_r(&ob->pa->seed);
uint64_t r64 = (uint64_t)hr << 32 | lr;
ob->sizes[i] = r64 % width + ob->pa->minsize;
}
} else {
for (size_t i = 0; i < n_ops_total; i++)
ob->sizes[i] = args->dsize;
}
return 0;
free_pop:
pmemobj_close(ob->pop);
free_ob:
free(ob);
return -1;
}
static int
obj_exit(struct benchmark *bench, struct benchmark_args *args)
{
struct obj_bench *ob = (struct obj_bench *)pmembench_get_priv(bench);
free(ob->sizes);
POBJ_FREE(&D_RW(ob->root)->offs);
pmemobj_close(ob->pop);
return 0;
}
static int
pmalloc_init(struct benchmark *bench, struct benchmark_args *args)
{
return obj_init(bench, args);
}
static int
pmalloc_op(struct benchmark *bench, struct operation_info *info)
{
struct obj_bench *ob = (struct obj_bench *)pmembench_get_priv(bench);
uint64_t i = info->index +
info->worker->index * info->args->n_ops_per_thread;
int ret = pmalloc(ob->pop, &ob->offs[i], ob->sizes[i], 0, 0);
if (ret) {
fprintf(stderr, "pmalloc ret: %d\n", ret);
return ret;
}
return 0;
}
struct pmix_worker {
size_t nobjects;
size_t shuffle_start;
unsigned seed;
};
static int
pmix_worker_init(struct benchmark *bench, struct benchmark_args *args,
struct worker_info *worker)
{
struct obj_bench *ob = (struct obj_bench *)pmembench_get_priv(bench);
struct pmix_worker *w = (struct pmix_worker *)calloc(1, sizeof(*w));
if (w == NULL)
return -1;
w->seed = ob->pa->seed;
worker->priv = w;
return 0;
}
static void
pmix_worker_fini(struct benchmark *bench, struct benchmark_args *args,
struct worker_info *worker)
{
struct pmix_worker *w = (struct pmix_worker *)worker->priv;
free(w);
}
static void
shuffle_objects(uint64_t *objects, size_t start, size_t nobjects,
unsigned *seed)
{
uint64_t tmp;
size_t dest;
for (size_t n = start; n < nobjects; ++n) {
dest = RRAND_R(seed, nobjects - 1, 0);
tmp = objects[n];
objects[n] = objects[dest];
objects[dest] = tmp;
}
}
#define FREE_PCT 10
#define FREE_OPS 10
static int
pmix_op(struct benchmark *bench, struct operation_info *info)
{
struct obj_bench *ob = (struct obj_bench *)pmembench_get_priv(bench);
struct pmix_worker *w = (struct pmix_worker *)info->worker->priv;
uint64_t idx = info->worker->index * info->args->n_ops_per_thread;
uint64_t *objects = &ob->offs[idx];
if (w->nobjects > FREE_OPS && FREE_PCT > RRAND_R(&w->seed, 100, 0)) {
shuffle_objects(objects, w->shuffle_start, w->nobjects,
&w->seed);
for (int i = 0; i < FREE_OPS; ++i) {
uint64_t off = objects[--w->nobjects];
pfree(ob->pop, &off);
}
w->shuffle_start = w->nobjects;
} else {
int ret = pmalloc(ob->pop, &objects[w->nobjects++],
ob->sizes[idx + info->index], 0, 0);
if (ret) {
fprintf(stderr, "pmalloc ret: %d\n", ret);
return ret;
}
}
return 0;
}
static int
pmalloc_exit(struct benchmark *bench, struct benchmark_args *args)
{
struct obj_bench *ob = (struct obj_bench *)pmembench_get_priv(bench);
for (size_t i = 0; i < args->n_ops_per_thread * args->n_threads; i++) {
if (ob->offs[i])
pfree(ob->pop, &ob->offs[i]);
}
return obj_exit(bench, args);
}
static int
pfree_init(struct benchmark *bench, struct benchmark_args *args)
{
int ret = obj_init(bench, args);
if (ret)
return ret;
struct obj_bench *ob = (struct obj_bench *)pmembench_get_priv(bench);
for (size_t i = 0; i < args->n_ops_per_thread * args->n_threads; i++) {
ret = pmalloc(ob->pop, &ob->offs[i], ob->sizes[i], 0, 0);
if (ret) {
fprintf(stderr, "pmalloc at idx %" PRIu64 " ret: %s\n",
i, pmemobj_errormsg());
while (i != 0) {
pfree(ob->pop, &ob->offs[i - 1]);
i--;
}
obj_exit(bench, args);
return ret;
}
}
return 0;
}
static int
pfree_op(struct benchmark *bench, struct operation_info *info)
{
struct obj_bench *ob = (struct obj_bench *)pmembench_get_priv(bench);
uint64_t i = info->index +
info->worker->index * info->args->n_ops_per_thread;
pfree(ob->pop, &ob->offs[i]);
return 0;
}
static struct benchmark_clo pmalloc_clo[3];
static struct benchmark_info pmalloc_info;
static struct benchmark_info pfree_info;
static struct benchmark_info pmix_info;
CONSTRUCTOR(obj_pmalloc_costructor)
void
obj_pmalloc_costructor(void)
{
pmalloc_clo[0].opt_short = 'r';
pmalloc_clo[0].opt_long = "random";
pmalloc_clo[0].descr = "Use random size allocations - "
"from min-size to data-size";
pmalloc_clo[0].off =
clo_field_offset(struct prog_args, use_random_size);
pmalloc_clo[0].type = CLO_TYPE_FLAG;
pmalloc_clo[1].opt_short = 'm';
pmalloc_clo[1].opt_long = "min-size";
pmalloc_clo[1].descr = "Minimum size of allocation for "
"random mode";
pmalloc_clo[1].type = CLO_TYPE_UINT;
pmalloc_clo[1].off = clo_field_offset(struct prog_args, minsize);
pmalloc_clo[1].def = "1";
pmalloc_clo[1].type_uint.size =
clo_field_size(struct prog_args, minsize);
pmalloc_clo[1].type_uint.base = CLO_INT_BASE_DEC;
pmalloc_clo[1].type_uint.min = 1;
pmalloc_clo[1].type_uint.max = UINT64_MAX;
pmalloc_clo[2].opt_short = 'S';
pmalloc_clo[2].opt_long = "seed";
pmalloc_clo[2].descr = "Random mode seed value";
pmalloc_clo[2].off = clo_field_offset(struct prog_args, seed);
pmalloc_clo[2].def = "1";
pmalloc_clo[2].type = CLO_TYPE_UINT;
pmalloc_clo[2].type_uint.size = clo_field_size(struct prog_args, seed);
pmalloc_clo[2].type_uint.base = CLO_INT_BASE_DEC;
pmalloc_clo[2].type_uint.min = 1;
pmalloc_clo[2].type_uint.max = UINT_MAX;
pmalloc_info.name = "pmalloc",
pmalloc_info.brief = "Benchmark for internal pmalloc() "
"operation";
pmalloc_info.init = pmalloc_init;
pmalloc_info.exit = pmalloc_exit;
pmalloc_info.multithread = true;
pmalloc_info.multiops = true;
pmalloc_info.operation = pmalloc_op;
pmalloc_info.measure_time = true;
pmalloc_info.clos = pmalloc_clo;
pmalloc_info.nclos = ARRAY_SIZE(pmalloc_clo);
pmalloc_info.opts_size = sizeof(struct prog_args);
pmalloc_info.rm_file = true;
pmalloc_info.allow_poolset = true;
REGISTER_BENCHMARK(pmalloc_info);
pfree_info.name = "pfree";
pfree_info.brief = "Benchmark for internal pfree() "
"operation";
pfree_info.init = pfree_init;
pfree_info.exit = pmalloc_exit;
pfree_info.multithread = true;
pfree_info.multiops = true;
pfree_info.operation = pfree_op;
pfree_info.measure_time = true;
pfree_info.clos = pmalloc_clo;
pfree_info.nclos = ARRAY_SIZE(pmalloc_clo);
pfree_info.opts_size = sizeof(struct prog_args);
pfree_info.rm_file = true;
pfree_info.allow_poolset = true;
REGISTER_BENCHMARK(pfree_info);
pmix_info.name = "pmix";
pmix_info.brief = "Benchmark for mixed alloc/free workload";
pmix_info.init = pmalloc_init;
pmix_info.exit = pmalloc_exit;
pmix_info.multithread = true;
pmix_info.multiops = true;
pmix_info.operation = pmix_op;
pmix_info.init_worker = pmix_worker_init;
pmix_info.free_worker = pmix_worker_fini;
pmix_info.measure_time = true;
pmix_info.clos = pmalloc_clo;
pmix_info.nclos = ARRAY_SIZE(pmalloc_clo);
pmix_info.opts_size = sizeof(struct prog_args);
pmix_info.rm_file = true;
pmix_info.allow_poolset = true;
REGISTER_BENCHMARK(pmix_info);
};