/*
* Copyright 2015-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.
*/
/*
* benchmark.hpp -- This file contains interface for creating benchmarks to the
* pmembench framework. The _most_ important data structure is
* struct benchmark_info which should be properly filled and registered by the
* benchmark. Some fields should be filled by meta-data and information about
* the benchmark like: name, brief description, supported operation modes etc.
* The other group of fields are function callbacks which may be implemented by
* the benchmark. Some callbacks are required, others are optional. This is
* indicated in the structure description.
*
* To register a benchmark you can use the special macro
* REGISTER_BENCHMARK() which takes static benchmark_info data structure as an
* argument. You can also use the pmembench_register() function. Please note
* that registering a benchmark should be done at initialization time. You can
* achieve this by specifying pmembench_init macro in function attributes:
*
* static void pmembench_init my_benchmark_init()
* {
* pmembench_register(&my_benchmark);
* }
*
* However using the REGISTER_BENCHMARK() macro is recommended.
*/
#ifndef _BENCHMARK_H
#define _BENCHMARK_H
#include <climits>
#include <cstdbool>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <util.h>
#include "benchmark_time.hpp"
#include "os.h"
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
#endif
#define RRAND(max, min) (rand() % ((max) - (min)) + (min))
#define RRAND_R(seed, max, min) (os_rand_r(seed) % ((max) - (min)) + (min))
struct benchmark;
/*
* benchmark_args - Arguments for benchmark.
*
* It contains set of common arguments and pointer to benchmark's specific
* arguments which are automatically processed by framework according to
* clos, nclos and opt_size in benchmark_info structure.
*/
struct benchmark_args {
const char *fname; /* path to test file */
size_t fsize; /* size of test file */
bool is_poolset; /* test file is a poolset */
bool is_dynamic_poolset; /* test file is directory in which
benchmark creates reusable files */
mode_t fmode; /* test file's permissions */
unsigned n_threads; /* number of working threads */
size_t n_ops_per_thread; /* number of operations per thread */
bool thread_affinity; /* set worker threads CPU affinity mask */
ssize_t main_affinity; /* main thread affinity */
char *affinity_list; /* set CPU affinity order */
size_t dsize; /* data size */
unsigned seed; /* PRNG seed */
unsigned repeats; /* number of repeats of one scenario */
unsigned min_exe_time; /* minimal execution time */
bool help; /* print help for benchmark */
void *opts; /* benchmark specific arguments */
};
/*
* benchmark_results - Benchmark's execution results.
*/
struct benchmark_results {
uint64_t nbytes; /* number of bytes processed */
uint64_t nops; /* number of operations executed */
benchmark_time_t time; /* total execution time */
};
/*
* struct results -- statistics for total measurements
*/
struct results {
double min;
double max;
double avg;
double std_dev;
double med;
};
/*
* struct latency -- statistics for latency measurements
*/
struct latency {
uint64_t max;
uint64_t min;
uint64_t avg;
double std_dev;
uint64_t pctl50_0p;
uint64_t pctl99_0p;
uint64_t pctl99_9p;
};
/*
* struct thread_results -- results of a single thread
*/
struct thread_results {
benchmark_time_t beg;
benchmark_time_t end;
benchmark_time_t end_op[];
};
/*
* struct bench_results -- results of the whole benchmark
*/
struct bench_results {
struct thread_results **thres;
};
/*
* struct total_results -- results and statistics of the whole benchmark
*/
struct total_results {
size_t nrepeats;
size_t nthreads;
size_t nops;
double nopsps;
struct results total;
struct latency latency;
struct bench_results *res;
};
/*
* Command Line Option integer value base.
*/
#define CLO_INT_BASE_NONE 0x0
#define CLO_INT_BASE_DEC 0x1
#define CLO_INT_BASE_HEX 0x2
#define CLO_INT_BASE_OCT 0x4
/*
* Command Line Option type.
*/
enum clo_type {
CLO_TYPE_FLAG,
CLO_TYPE_STR,
CLO_TYPE_INT,
CLO_TYPE_UINT,
CLO_TYPE_MAX,
};
/*
* Description of command line option.
*
* This structure is used to declare command line options by the benchmark
* which will be automatically parsed by the framework.
*
* opt_short : Short option char. If there is no short option write 0.
* opt_long : Long option string.
* descr : Description of command line option.
* off : Offset in data structure in which the value should be stored.
* type : Type of command line option.
* def : Default value. If set to NULL, this options is required.
* ignore_in_res: Do not print in results.
* check : Optional callback for checking the command line option value.
* type_int : Parameters for signed integer.
* type_uint : Parameters for unsigned integer.
* type_str : Parameters for string.
*
* size : Size of integer value. Valid values: 1, 2, 4, 8.
* base : Integer base system from which the parsing should be
* performed. This field may be used as bit mask by logically
* adding different base types.
* limit_min : Indicates whether value should be limited by the minimum
* value.
* limit_max : Indicates whether value should be limited by the maximum
* value.
* min : Minimum value when limit_min is set.
* max : Maximum value when limit_min is set.
*
* alloc : If set to true the framework should allocate memory for the
* value. The memory will be freed by the framework at the end of
* execution. Otherwise benchmark must provide valid pointer in
* opt_var and max_size parameter must be set properly.
* max_size : Maximum size of string.
*/
struct benchmark_clo {
int opt_short;
const char *opt_long;
enum clo_type type;
const char *descr;
size_t off;
const char *def;
bool ignore_in_res;
struct {
size_t size;
int base;
int64_t min;
int64_t max;
} type_int;
struct {
size_t size;
int base;
uint64_t min;
uint64_t max;
} type_uint;
int used;
};
#define clo_field_offset(s, f) ((size_t) & ((s *)0)->f)
#define clo_field_size(s, f) (sizeof(((s *)0)->f))
/*
* worker_info - Worker thread's information structure.
*/
struct worker_info {
size_t index; /* index of worker thread */
struct operation_info *opinfo; /* operation info structure */
size_t nops; /* number of operations */
void *priv; /* worker's private data */
benchmark_time_t beg; /* start time */
benchmark_time_t end; /* end time */
};
/*
* operation_info - Information about operation.
*/
struct operation_info {
struct worker_info *worker; /* worker's info */
struct benchmark_args *args; /* benchmark arguments */
size_t index; /* operation's index */
benchmark_time_t end; /* operation's end time */
};
/*
* struct benchmark_info -- benchmark descriptor
* name : Name of benchmark.
* brief : Brief description of benchmark.
* clos : Command line options which will be automatically parsed by
* framework.
* nclos : Number of command line options.
* opts_size : Size of data structure where the parsed values should be
* stored in.
* print_help : Callback for printing help message.
* pre_init : Function for initialization of the benchmark before parsing
* command line arguments.
* init : Function for initialization of the benchmark after parsing
* command line arguments.
* exit : Function for de-initialization of the benchmark.
* multithread : Indicates whether the benchmark operation function may be
* run in many threads.
* multiops : Indicates whether the benchmark operation function may be
* run many time in a loop.
* measure_time : Indicates whether the benchmark framework should measure the
* execution time of operation function. If set to false, the
* benchmark must report the execution time by itself.
* init_worker : Callback for initialization thread specific data. Invoked in
* a single thread for every thread worker.
* operation : Callback function which does the main job of benchmark.
* rm_file : Indicates whether the test file should be removed by
* framework before the init function will be called.
* allow_poolset: Indicates whether benchmark may use poolset files.
* If set to false and fname points to a poolset, an error
* will be returned.
* According to multithread and single_operation flags it may be
* invoked in different ways:
* +-------------+----------+-------------------------------------+
* | multithread | multiops | description |
* +-------------+----------+-------------------------------------+
* | false | false | invoked once, in one thread |
* +-------------+----------+-------------------------------------+
* | false | true | invoked many times, in one thread |
* +-------------+----------+-------------------------------------+
* | true | false | invoked once, in many threads |
* +-------------+----------+-------------------------------------+
* | true | true | invoked many times, in many threads |
* +-------------+----------+-------------------------------------+
*
*/
struct benchmark_info {
const char *name;
const char *brief;
struct benchmark_clo *clos;
size_t nclos;
size_t opts_size;
void (*print_help)(struct benchmark *bench);
int (*pre_init)(struct benchmark *bench);
int (*init)(struct benchmark *bench, struct benchmark_args *args);
int (*exit)(struct benchmark *bench, struct benchmark_args *args);
int (*init_worker)(struct benchmark *bench, struct benchmark_args *args,
struct worker_info *worker);
void (*free_worker)(struct benchmark *bench,
struct benchmark_args *args,
struct worker_info *worker);
int (*operation)(struct benchmark *bench, struct operation_info *info);
void (*print_extra_headers)();
void (*print_extra_values)(struct benchmark *bench,
struct benchmark_args *args,
struct total_results *res);
bool multithread;
bool multiops;
bool measure_time;
bool rm_file;
bool allow_poolset;
bool print_bandwidth;
};
void *pmembench_get_priv(struct benchmark *bench);
void pmembench_set_priv(struct benchmark *bench, void *priv);
struct benchmark_info *pmembench_get_info(struct benchmark *bench);
int pmembench_register(struct benchmark_info *bench_info);
#define REGISTER_BENCHMARK(bench) \
if (pmembench_register(&(bench))) { \
fprintf(stderr, "Unable to register benchmark '%s'\n", \
(bench).name); \
}
#endif /* _BENCHMARK_H */