Skip to content

Commit

Permalink
Merge pull request #1985 from pbalcer/obj-alloc-class-ctl-xalloc
Browse files Browse the repository at this point in the history
allocation class interface + xalloc
  • Loading branch information
krzycz authored Jul 25, 2017
2 parents 1f91998 + 7be1ecf commit 852860d
Show file tree
Hide file tree
Showing 42 changed files with 1,473 additions and 92 deletions.
130 changes: 130 additions & 0 deletions doc/libpmemobj.3.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,8 @@ typedef int (*pmemobj_constr)(PMEMobjpool *pop, void *ptr, void *arg);
int pmemobj_alloc(PMEMobjpool *pop, PMEMoid *oidp, size_t size, uint64_t type_num,
pmemobj_constr constructor, void *arg);
int pmemobj_zalloc(PMEMobjpool *pop, PMEMoid *oidp, size_t size, uint64_t type_num);
int pmemobj_xalloc(PMEMobjpool *pop, PMEMoid *oidp, size_t size, uint64_t type_num,
uint64_t flags, pmemobj_constr constructor, void *arg);
int pmemobj_realloc(PMEMobjpool *pop, PMEMoid *oidp, size_t size, uint64_t type_num);
int pmemobj_zrealloc(PMEMobjpool *pop, PMEMoid *oidp, size_t size, uint64_t type_num);
int pmemobj_strdup(PMEMobjpool *pop, PMEMoid *oidp, const char *s, uint64_t type_num);
Expand Down Expand Up @@ -1247,6 +1249,21 @@ will differ from the requested one by at least 64 bytes. For this reason, making
discouraged. If *size* equals 0, then **pmemobj_zalloc**() returns non-zero value, sets the *errno* and leaves the *oidp* untouched. The allocated object is
added to the internal container associated with given *type_num*.

```c
int pmemobj_xalloc(PMEMobjpool *pop, PMEMoid *oidp,
size_t size, uint64_t type_num, uint64_t flags,
pmemobj_constr constructor , void *arg);
```

The **pmemobj_xalloc**() function allocates a new object from the persistent
memory heap associated with memory pool *pop*. Equivalent to **pmemobj_alloc**()
but with the addition of allocation modifiers.

The *flags* argument is a bitmask of the following values:

+ **POBJ_XALLOC_ZERO** - zero the object (equivalent of **pmemobj_zalloc**())
+ **POBJ_CLASS_ID(class_id)** - allocate the object from the allocation class with id equal to *class_id*

```c
void pmemobj_free(PMEMoid *oidp);
```
Expand Down Expand Up @@ -1856,6 +1873,7 @@ The **pmemobj_tx_xalloc**() function transactionally allocates a new object of g

+ **POBJ_XALLOC_ZERO** - zero the object (equivalent of pmemobj_tx_zalloc)
+ **POBJ_XALLOC_NO_FLUSH** - skip flush on commit (when application deals with flushing or uses pmemobj_memcpy_persist)
+ **POBJ_CLASS_ID(class_id)** - allocate the object from the allocation class with id equal to *class_id*

If successful, returns a handle to the newly allocated object. Otherwise, stage changes to **TX_STAGE_ONABORT**, **OID_NULL** is returned, and *errno* is set appropriately.
If *size* equals 0, **OID_NULL** is returned and *errno* is set appropriately. This function must be called during **TX_STAGE_WORK**.
Expand Down Expand Up @@ -2476,6 +2494,118 @@ executed.

Always returns 0.

heap.alloc_class.[class_id].desc | rw | - | `struct pobj_alloc_class_desc` | `struct pobj_alloc_class_desc` | integer, integer, string

A description of an allocation class. Allows one to create or view the internal
data structures of the allocator.

Creating custom allocation classes can be beneficial for both raw allocation
throughput, scalability and, most importantly, fragmentation.
By carefully constructing allocation classes that match the application workload,
one can entirely eliminate external and internal fragmentation. For example,
it is possible to easily construct a slab-like allocation mechanism for any
data structure.

The `[class_id]` is an index field. Only values between 0-254 are valid.
If setting an allocation class, but the `class_id` is already taken, the function
will return -1.
The values between 0-127 are reserved for the default allocation classes of the
library and can be used only for reading.

If one wants to retrieve information about all allocation classes, the
recommended method is to simply call this entry point for all class ids between
0 and 254 and discard those results for which the function returned an error.

This entry point takes a complex argument.

```
struct pobj_alloc_class_desc {
size_t unit_size;
unsigned units_per_block;
enum pobj_header_type header_type;
unsigned class_id;
};
```

The first field `unit_size`, is an 8-byte unsigned integer that defines the
allocation class size. While theoretically limited only by **PMEMOBJ_MAX_ALLOC_SIZE**,
this value should be between 8 bytes and a couple of megabytes for most of the
workloads.

The field `units_per_block` defines how many units does a single block of memory
contains. This value will be rounded up
to match internal size of the block (256 kilobytes or a multiple thereof).
For example, given a class with `unit_size` of 512 bytes and `units_per_block`
equal 1000, a single block of memory for that class will have 512 kilobytes.
This is relevant because the bigger the block size, the blocks need to be
fetched less frequently which leads to a lower contention on global state of the
heap.
Keep in mind that the information whether an object is allocated or not is
stored in a bitmap with limited number of entries, this makes it inefficient to
create allocation classes smaller than 128 bytes.

The field `header_type` defines the header of objects from the allocation class.
There are three types:

- **POBJ_HEADER_LEGACY**, string value: `legacy`. Used for allocation classes
prior to 1.3 version of the library. Not recommended for use.
Incurs 64 byte metadata overhead for every object.
Fully supports all features.
- **POBJ_HEADER_COMPACT**, string value: `compact`. Used as default for all
predefined allocation classes.
Incurs 16 bytes metadata overhead for every object.
Fully supports all features.
- **POBJ_HEADER_NONE**, string value: `none`. Header type that doesn't
incur any metadata overhead beyond a single bitmap entry. Can be used
for very small allocation classes or when objects must be adjacent to
each other.
This header type does not support type numbers (it's always 0) and
allocations that span more than one unit.

The field `class_id` is optional, runtime only (can't be set from config file),
variable that allows the user to retrieve the identifier of the class. This will
be equivalent to the provided `[class_id]`.

The allocation classes are a runtime state of the library and must be created
after every open. It's highly recommended to use the configuration file to store
the classes.

This structure is declared in the `libpmemobj/ctl.h` header file, please read it
for an in-depth explanation of the allocation classes and relevant algorithms.

Allocation classes constructed in this way can be leveraged by explicitly
specifying the class using **POBJ_CLASS_ID(id)** flag in **pmemobj_tx_xalloc**()/**pmemobj_xalloc**()
functions.

Example of a valid alloc class query string:
```
heap.alloc_class.128.desc=500,1000,compact
```
This query, if executed, will create an allocation class with an id of 128 that
has a unit size of 500 bytes, has at least 1000 units per block and uses
a compact header.

For reading, function returns 0 if successful, if the allocation class does
not exist it sets the errno to **ENOENT** and returns -1;

For writing, function returns 0 if the allocation class has been successfully
created, -1 otherwise.

heap.alloc_class.new.desc | wo | - | - | `struct pobj_alloc_class_desc` | integer, integer, string

Same as `heap.alloc_class.[class_id].desc`, but instead of requiring the user
to provide the class_id, it automatically creates the allocation class with the
first available identifier.

This should be used when it's impossible to guarantee unique allocation class
naming in the application (e.g. when writing a library that uses libpmemobj).

The required class identifier will be stored in the `class_id` field of the
`struct pobj_alloc_class_desc`.

This function returns 0 if the allocation class has been successfully created,
-1 otherwise.

# CTL external configuration #

In addition to direct function call, each write entry point can also be set
Expand Down
4 changes: 2 additions & 2 deletions src/examples/libpmemobj/Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#
# Copyright 2014-2016, Intel Corporation
# Copyright 2014-2017, Intel Corporation
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
Expand Down Expand Up @@ -38,7 +38,7 @@ include ../../common.inc
PROGS = manpage btree pi lists setjmp
DIRS = pminvaders pmemlog pmemblk string_store string_store_tx\
string_store_tx_type hashmap tree_map pmemobjfs map libart array\
linkedlist list_map
linkedlist list_map slab_allocator

ifeq ($(call cxx_ok), y)
DIRS += cpp pman cpp_map panaconda doc_snippets
Expand Down
3 changes: 3 additions & 0 deletions src/examples/libpmemobj/slab_allocator/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
main
libslab_allocator.so

48 changes: 48 additions & 0 deletions src/examples/libpmemobj/slab_allocator/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#
# Copyright 2017, 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.
#

#
# examples/libpmemobj/slab_allocator/Makefile -- build the slab_allocator example
#
TOP := $(dir $(lastword $(MAKEFILE_LIST)))../../../../
include $(TOP)/src/common.inc

PROGS = main
LIBRARIES = slab_allocator

LIBS = -lpmemobj

include ../../Makefile.inc

libslab_allocator.o: slab_allocator.o

main: main.o slab_allocator.o
121 changes: 121 additions & 0 deletions src/examples/libpmemobj/slab_allocator/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/*
* Copyright 2017, 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.
*/

/*
* main.c -- example usage of a slab-like mechanism implemented in libpmemobj
*
* This application does nothing besides demonstrating the example slab
* allocator mechanism.
*
* By using the CTL alloc class API we can instrument libpmemobj to optimally
* manage memory for the pool.
*/

#include <ex_common.h>
#include <assert.h>
#include <stdio.h>
#include "slab_allocator.h"

POBJ_LAYOUT_BEGIN(slab_allocator);
POBJ_LAYOUT_ROOT(slab_allocator, struct foo);
POBJ_LAYOUT_TOID(slab_allocator, struct bar);
POBJ_LAYOUT_TOID(slab_allocator, struct root);
POBJ_LAYOUT_END(slab_allocator);

struct foo {
char data[100];
};

struct bar {
char data[500];
};

struct root {
TOID(struct foo) foop;
TOID(struct bar) barp;
};

int
main(int argc, char *argv[])
{
if (argc < 2) {
printf("usage: %s file-name\n", argv[0]);
return 1;
}

const char *path = argv[1];

PMEMobjpool *pop;

if (file_exists(path) != 0) {
if ((pop = pmemobj_create(path, POBJ_LAYOUT_NAME(btree),
PMEMOBJ_MIN_POOL, 0666)) == NULL) {
perror("failed to create pool\n");
return 1;
}
} else {
if ((pop = pmemobj_open(path,
POBJ_LAYOUT_NAME(btree))) == NULL) {
perror("failed to open pool\n");
return 1;
}
}

struct slab_allocator *foo_producer = slab_new(pop, sizeof(struct foo));
assert(foo_producer != NULL);
struct slab_allocator *bar_producer = slab_new(pop, sizeof(struct bar));
assert(bar_producer != NULL);

TOID(struct root) root = POBJ_ROOT(pop, struct root);

if (TOID_IS_NULL(D_RO(root)->foop)) {
TX_BEGIN(pop) {
TX_SET(root, foop.oid, slab_tx_alloc(foo_producer));
} TX_END
}

if (TOID_IS_NULL(D_RO(root)->barp)) {
slab_alloc(bar_producer, &D_RW(root)->barp.oid, NULL, NULL);
}

assert(pmemobj_alloc_usable_size(D_RO(root)->foop.oid) ==
sizeof(struct foo));

assert(pmemobj_alloc_usable_size(D_RO(root)->barp.oid) ==
sizeof(struct bar));

slab_delete(foo_producer);
slab_delete(bar_producer);
pmemobj_close(pop);

return 0;
}
Loading

0 comments on commit 852860d

Please sign in to comment.