#include <stdlib.h>
#include "unittest.h"
#define LAYOUT_NAME "obj_fragmentation"
#define MEGABYTE (1UL << 20)
#define GIGABYTE (1UL << 30)
#define RRAND(seed, max, min)\
((min) == (max) ? (min) : (os_rand_r(&seed) % ((max) - (min)) + (min)))
uint64_t *objects;
size_t nobjects;
size_t allocated_current;
#define MAX_OBJECTS (200ULL * 1000000)
#define ALLOC_TOTAL (5000 * MEGABYTE)
#define ALLOC_CURR (1000 * MEGABYTE)
#define FREES_P 200
#define DEFAULT_FILE_SIZE (3 * GIGABYTE)
static unsigned seed;
static void
shuffle_objects(size_t start, size_t end)
{
uint64_t tmp;
size_t dest;
for (size_t n = start; n < end; ++n) {
dest = RRAND(seed, nobjects - 1, 0);
tmp = objects[n];
objects[n] = objects[dest];
objects[dest] = tmp;
}
}
static uint64_t
remove_last()
{
UT_ASSERT(nobjects > 0);
uint64_t obj = objects[--nobjects];
return obj;
}
static void
allocate_objects(PMEMobjpool *pop, size_t size_min, size_t size_max)
{
size_t allocated_total = 0;
size_t sstart = 0;
PMEMoid oid = pmemobj_root(pop, 1);
uint64_t uuid_lo = oid.pool_uuid_lo;
while (allocated_total < ALLOC_TOTAL) {
size_t s = RRAND(seed, size_max, size_min);
pmemobj_alloc(pop, &oid, s, 0, NULL, NULL);
s = pmemobj_alloc_usable_size(oid);
UT_ASSERTeq(OID_IS_NULL(oid), 0);
objects[nobjects++] = oid.off;
UT_ASSERT(nobjects < MAX_OBJECTS);
allocated_total += s;
allocated_current += s;
if (allocated_current > ALLOC_CURR) {
shuffle_objects(sstart, nobjects);
for (int i = 0; i < FREES_P; ++i) {
oid.pool_uuid_lo = uuid_lo;
oid.off = remove_last();
allocated_current -=
pmemobj_alloc_usable_size(oid);
pmemobj_free(&oid);
}
sstart = nobjects;
}
}
}
static void
delete_objects(PMEMobjpool *pop, float pct)
{
size_t nfree = (float)nobjects * pct;
PMEMoid oid = pmemobj_root(pop, 1);
uint64_t uuid_lo = oid.pool_uuid_lo;
shuffle_objects(0, nobjects);
while (nfree--) {
oid.off = remove_last();
oid.pool_uuid_lo = uuid_lo;
allocated_current -= pmemobj_alloc_usable_size(oid);
pmemobj_free(&oid);
}
}
typedef void workload(PMEMobjpool *pop);
static void w1(PMEMobjpool *pop) {
allocate_objects(pop, 100, 100);
}
static void w2(PMEMobjpool *pop) {
allocate_objects(pop, 100, 100);
allocate_objects(pop, 130, 130);
}
static void w3(PMEMobjpool *pop) {
allocate_objects(pop, 100, 100);
delete_objects(pop, 0.9);
allocate_objects(pop, 130, 130);
}
static void w4(PMEMobjpool *pop) {
allocate_objects(pop, 100, 150);
allocate_objects(pop, 200, 250);
}
static void w5(PMEMobjpool *pop) {
allocate_objects(pop, 100, 150);
delete_objects(pop, 0.9);
allocate_objects(pop, 200, 250);
}
static void w6(PMEMobjpool *pop) {
allocate_objects(pop, 100, 200);
delete_objects(pop, 0.5);
allocate_objects(pop, 1000, 2000);
}
static void w7(PMEMobjpool *pop) {
allocate_objects(pop, 1000, 2000);
delete_objects(pop, 0.9);
allocate_objects(pop, 1500, 2500);
}
static void w8(PMEMobjpool *pop) {
allocate_objects(pop, 50, 150);
delete_objects(pop, 0.9);
allocate_objects(pop, 5000, 15000);
}
static workload *workloads[] = {
w1, w2, w3, w4, w5, w6, w7, w8
};
static float workloads_target[] = {
0.01f, 0.01f, 0.01f, 0.9f, 0.8f, 0.7f, 0.2f, 0.7f
};
int
main(int argc, char *argv[])
{
START(argc, argv, "obj_fragmentation2");
if (argc < 3)
UT_FATAL("usage: %s filename workload [seed]", argv[0]);
const char *path = argv[1];
PMEMobjpool *pop = pmemobj_create(path, LAYOUT_NAME, DEFAULT_FILE_SIZE,
S_IWUSR | S_IRUSR);
if (pop == NULL)
UT_FATAL("!pmemobj_create: %s", path);
int w = atoi(argv[2]);
if (argc > 3)
seed = (unsigned)atoi(argv[3]);
else
seed = time(NULL);
objects = ZALLOC(sizeof(uint64_t) * MAX_OBJECTS);
UT_ASSERTne(objects, NULL);
workloads[w - 1](pop);
PMEMoid oid;
size_t remaining = 0;
size_t chunk = 100;
while (pmemobj_alloc(pop, &oid, chunk, 0, NULL, NULL) == 0)
remaining += pmemobj_alloc_usable_size(oid) + 16;
size_t allocated_sum = 0;
oid = pmemobj_root(pop, 1);
for (size_t n = 0; n < nobjects; ++n) {
if (objects[n] == 0)
continue;
oid.off = objects[n];
allocated_sum += pmemobj_alloc_usable_size(oid) + 16;
}
size_t used = DEFAULT_FILE_SIZE - remaining;
float frag = ((float)used / allocated_sum) - 1.f;
UT_ASSERT(frag <= workloads_target[w - 1]);
pmemobj_close(pop);
FREE(objects);
DONE(NULL);
}