/*
* Copyright 2016-2018, Intel Corporation
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* pmemobj_persist.cpp -- pmemobj persist benchmarks definition
*/
#include <cassert>
#include <cerrno>
#include <cstddef>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fcntl.h>
#include <sys/file.h>
#include <sys/mman.h>
#include <unistd.h>
#include "benchmark.hpp"
#include "file.h"
#include "libpmemobj.h"
#include "util.h"
/*
* The factor used for PMEM pool size calculation, accounts for metadata,
* fragmentation and etc.
*/
#define FACTOR 3
/* The minimum allocation size that pmalloc can perform */
#define ALLOC_MIN_SIZE 64
/* OOB and allocation header size */
#define OOB_HEADER_SIZE 64
#define CONST_B 0xFF
/*
* prog_args -- benchmark specific command line options
*/
struct prog_args {
size_t minsize; /* minimum size for random allocation size */
bool use_random_size; /* if set, use random size allocations */
bool no_warmup; /* do not do warmup */
unsigned seed; /* seed for random numbers */
};
/*
* obj_bench -- benchmark context
*/
struct obj_bench {
PMEMobjpool *pop; /* persistent pool handle */
struct prog_args *pa; /* prog_args structure */
PMEMoid *oids; /* vector of allocated objects */
void **ptrs; /* pointers to allocated objects */
uint64_t nobjs; /* number of allocated objects */
size_t obj_size; /* size of each allocated objects */
int const_b; /* memset() value */
};
/*
* init_objects -- allocate persistent objects and obtain direct pointers
*/
static int
init_objects(struct obj_bench *ob)
{
assert(ob->nobjs != 0);
ob->oids = (PMEMoid *)malloc(ob->nobjs * sizeof(*ob->oids));
if (!ob->oids) {
perror("malloc");
return -1;
}
ob->ptrs = (void **)malloc(ob->nobjs * sizeof(*ob->ptrs));
if (!ob->ptrs) {
perror("malloc");
goto err_malloc;
}
for (uint64_t i = 0; i < ob->nobjs; i++) {
PMEMoid oid;
void *ptr;
if (pmemobj_alloc(ob->pop, &oid, ob->obj_size, 0, nullptr,
nullptr)) {
perror("pmemobj_alloc");
goto err_palloc;
}
ptr = pmemobj_direct(oid);
if (!ptr) {
perror("pmemobj_direct");
goto err_palloc;
}
ob->oids[i] = oid;
ob->ptrs[i] = ptr;
}
return 0;
err_palloc:
free(ob->ptrs);
err_malloc:
free(ob->oids);
return -1;
}
/*
* do_warmup -- does the warmup by writing the whole pool area
*/
static void
do_warmup(struct obj_bench *ob)
{
for (uint64_t i = 0; i < ob->nobjs; ++i) {
memset(ob->ptrs[i], 0, ob->obj_size);
pmemobj_persist(ob->pop, ob->ptrs[i], ob->obj_size);
}
}
/*
* obj_persist_op -- actual benchmark operation
*/
static int
obj_persist_op(struct benchmark *bench, struct operation_info *info)
{
auto *ob = (struct obj_bench *)pmembench_get_priv(bench);
uint64_t idx = info->worker->index * info->args->n_ops_per_thread +
info->index;
assert(idx < ob->nobjs);
void *ptr = ob->ptrs[idx];
memset(ptr, ob->const_b, ob->obj_size);
pmemobj_persist(ob->pop, ptr, ob->obj_size);
return 0;
}
/*
* obj_persist_init -- initialization function
*/
static int
obj_persist_init(struct benchmark *bench, struct benchmark_args *args)
{
assert(bench != nullptr);
assert(args != nullptr);
assert(args->opts != nullptr);
enum file_type type = util_file_get_type(args->fname);
if (type == OTHER_ERROR) {
fprintf(stderr, "could not check type of file %s\n",
args->fname);
return -1;
}
auto *pa = (struct prog_args *)args->opts;
size_t poolsize;
if (pa->minsize >= args->dsize) {
fprintf(stderr, "Wrong params - allocation size\n");
return -1;
}
auto *ob = (struct obj_bench *)malloc(sizeof(struct obj_bench));
if (ob == nullptr) {
perror("malloc");
return -1;
}
pmembench_set_priv(bench, ob);
ob->pa = pa;
/* initialize memset() value */
ob->const_b = CONST_B;
ob->nobjs = args->n_ops_per_thread * args->n_threads;
/* Create pmemobj pool. */
ob->obj_size = args->dsize;
if (ob->obj_size < ALLOC_MIN_SIZE)
ob->obj_size = ALLOC_MIN_SIZE;
/* For data objects */
poolsize = ob->nobjs * (ob->obj_size + OOB_HEADER_SIZE);
/* multiply by FACTOR for metadata, fragmentation, etc. */
poolsize = poolsize * FACTOR;
if (args->is_poolset || type == TYPE_DEVDAX) {
if (args->fsize < poolsize) {
fprintf(stderr, "file size too large\n");
goto free_ob;
}
poolsize = 0;
} else if (poolsize < PMEMOBJ_MIN_POOL) {
poolsize = PMEMOBJ_MIN_POOL;
}
poolsize = PAGE_ALIGNED_UP_SIZE(poolsize);
ob->pop = pmemobj_create(args->fname, nullptr, poolsize, args->fmode);
if (ob->pop == nullptr) {
fprintf(stderr, "%s\n", pmemobj_errormsg());
goto free_ob;
}
if (init_objects(ob)) {
goto free_pop;
}
if (!ob->pa->no_warmup) {
do_warmup(ob);
}
return 0;
free_pop:
pmemobj_close(ob->pop);
free_ob:
free(ob);
return -1;
}
/*
* obj_persist_exit -- benchmark cleanup function
*/
static int
obj_persist_exit(struct benchmark *bench, struct benchmark_args *args)
{
auto *ob = (struct obj_bench *)pmembench_get_priv(bench);
for (uint64_t i = 0; i < ob->nobjs; ++i) {
pmemobj_free(&ob->oids[i]);
}
pmemobj_close(ob->pop);
free(ob->oids);
free(ob->ptrs);
free(ob);
return 0;
}
static struct benchmark_clo obj_persist_clo[1];
/* Stores information about benchmark. */
static struct benchmark_info obj_persist_info;
CONSTRUCTOR(pmemobj_persist_constructor)
void
pmemobj_persist_constructor(void)
{
obj_persist_clo[0].opt_short = 'w';
obj_persist_clo[0].opt_long = "no-warmup";
obj_persist_clo[0].descr = "Don't do warmup";
obj_persist_clo[0].def = "false";
obj_persist_clo[0].type = CLO_TYPE_FLAG;
obj_persist_clo[0].off = clo_field_offset(struct prog_args, no_warmup);
obj_persist_info.name = "pmemobj_persist";
obj_persist_info.brief = "Benchmark for pmemobj_persist() "
"operation";
obj_persist_info.init = obj_persist_init;
obj_persist_info.exit = obj_persist_exit;
obj_persist_info.multithread = true;
obj_persist_info.multiops = true;
obj_persist_info.operation = obj_persist_op;
obj_persist_info.measure_time = true;
obj_persist_info.clos = obj_persist_clo;
obj_persist_info.nclos = ARRAY_SIZE(obj_persist_clo);
obj_persist_info.opts_size = sizeof(struct prog_args);
obj_persist_info.rm_file = true;
obj_persist_info.allow_poolset = true;
REGISTER_BENCHMARK(obj_persist_info);
};