Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add memory pool allocator #686

Merged
merged 9 commits into from
Mar 6, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions debug/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ messages of the lower ones.

* 103: Show unsubscripted dictionary words and subscripted ones which share
the same base word.
* 104: Memory pool statistics.

### 2) -debug=LOCATIONS (-de=LOCATIONS)
Show only messages from these LOCATIONS. The LOCATIONS string is a
Expand Down
2 changes: 2 additions & 0 deletions link-grammar/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ liblink_grammar_la_SOURCES = \
linkage/lisjuncts.c \
linkage/sane.c \
linkage/score.c \
memory-pool.c \
parse/count.c \
parse/extract-links.c \
parse/fast-match.c \
Expand Down Expand Up @@ -154,6 +155,7 @@ liblink_grammar_la_SOURCES = \
linkage/lisjuncts.h \
linkage/sane.h \
linkage/score.h \
memory-pool.h \
parse/count.h \
parse/extract-links.h \
parse/fast-match.h \
Expand Down
3 changes: 3 additions & 0 deletions link-grammar/api-structures.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@

#include "api-types.h"
#include "corpus/corpus.h"
#include "memory-pool.h"
//#include "error.h"
#include "utilities.h"

Expand Down Expand Up @@ -118,6 +119,8 @@ struct Sentence_s
size_t length; /* Number of words */
Word *word; /* Array of words after tokenization */
String_set * string_set; /* Used for assorted strings */
Pool_desc * fm_Match_node; /* Fast-matcher Match_node memory pool */
Pool_desc * Table_connector_pool; /* Count memoizing memory pool */

/* Wordgraph stuff. FIXME: create stand-alone struct for these. */
Gword *wordgraph; /* Tokenization wordgraph */
Expand Down
3 changes: 3 additions & 0 deletions link-grammar/api.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "dict-common/dict-utils.h" // for free_X_nodes
#include "disjunct-utils.h" // for free_disjuncts
#include "linkage/linkage.h"
#include "memory-pool.h"
#include "parse/histogram.h" // for PARSE_NUM_OVERFLOW
#include "parse/parse.h"
#include "post-process/post-process.h" // for post_process_new()
Expand Down Expand Up @@ -530,6 +531,8 @@ void sentence_delete(Sentence sent)
post_process_free(sent->constituent_pp);

global_rand_state = sent->rand_state;
pool_delete(sent->fm_Match_node);
pool_delete(sent->Table_connector_pool);
free(sent);
}

Expand Down
10 changes: 5 additions & 5 deletions link-grammar/connectors.c
Original file line number Diff line number Diff line change
Expand Up @@ -424,9 +424,7 @@ void sort_condesc_by_uc_constring(Dictionary dict)

void condesc_delete(Dictionary dict)
{
for (size_t i = 0; i < dict->contable.size; i++)
free(dict->contable.hdesc[i]);

pool_delete(dict->contable.mempool);
free(dict->contable.hdesc);
free(dict->contable.sdesc);
}
Expand Down Expand Up @@ -454,8 +452,7 @@ static void condesc_table_alloc(ConTable *ct, size_t size)
static bool condesc_insert(ConTable *ct, condesc_t **h,
const char *constring, int hash)
{
*h = (condesc_t *)malloc(sizeof(condesc_t));
memset(*h, 0, sizeof(condesc_t));
*h = pool_alloc(ct->mempool);
(*h)->str_hash = hash;
(*h)->string = constring;
ct->num_con++;
Expand Down Expand Up @@ -498,6 +495,9 @@ condesc_t *condesc_add(ConTable *ct, const char *constring)
{
condesc_table_alloc(ct, ct->num_con);
ct->num_con = 0;
ct->mempool = pool_new(__func__, "ConTable",
/*num_elements*/1024, sizeof(condesc_t),
/*zero_out*/true, /*align*/true, /*exact*/false);
}

int hash = connector_str_hash(constring);
Expand Down
2 changes: 2 additions & 0 deletions link-grammar/connectors.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

#include "api-types.h"
#include "lg_assert.h"
#include "memory-pool.h"
#include "string-set.h"

/* MAX_SENTENCE cannot be more than 254, because word MAX_SENTENCE+1 is
Expand Down Expand Up @@ -81,6 +82,7 @@ typedef struct
size_t size; /* Allocated size */
size_t num_con; /* Number of connector types */
size_t num_uc; /* Number of connector types with different UC part */
Pool_desc *mempool;
length_limit_def_t *length_limit_def;
length_limit_def_t **length_limit_def_next;
} ConTable;
Expand Down
272 changes: 272 additions & 0 deletions link-grammar/memory-pool.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,272 @@
/*************************************************************************/
/* Copyright (c) 2018 Amir Plivatsky */
/* All rights reserved */
/* */
/* Use of the link grammar parsing system is subject to the terms of the */
/* license set forth in the LICENSE file included with this software. */
/* This license allows free redistribution and use in source and binary */
/* forms, with or without modification, subject to certain conditions. */
/* */
/*************************************************************************/

#include <errno.h> // errno
#include <string.h> // strerror_r

#include "error.h"
#include "memory-pool.h"
#include "utilities.h" // MIN, MAX, aligned alloc

/* TODO: Add valgrind descriptions. See:
* http://valgrind.org/docs/manual/mc-manual.html#mc-manual.mempools */

/**
* Align given size to the nearest upper power of 2
* for size<MAX_ALIGNMENT, else to MIN_ALIGNMENT.
*/
static size_t align_size(size_t element_size)
{
if (element_size < MAX_ALIGNMENT)
{
size_t s = next_power_of_two_up(element_size);
if (s != element_size)
element_size = ALIGN(element_size, s);
}
else
{
element_size = ALIGN(element_size, MIN_ALIGNMENT);
}

return element_size;
}

/* The address of the next allocated block is at the end of the block. */

/**
* Create a memory pool descriptor.
* 1. If required, set the allocation size to a power of 2 of the element size.
* 2. Save the given parameters in the pool descriptor, to be used by
* pool_alloc();
* 3. Chain the pool descriptor to the given pool_list, so it can be
* automatically freed.
*/
Pool_desc *pool_new(const char *func, const char *name,
size_t num_elements, size_t element_size,
bool zero_out, bool align, bool exact)
{
Pool_desc *mp = malloc(sizeof(Pool_desc));

mp->func = func;
mp->name = name;

if (align)
{
mp->element_size = align_size(element_size);
mp->alignment = MAX(MIN_ALIGNMENT, mp->element_size);
mp->alignment = MIN(MAX_ALIGNMENT, mp->alignment);
mp->data_size = num_elements * mp->element_size;
mp->block_size = ALIGN(mp->data_size + FLDSIZE_NEXT, mp->alignment);
}
else
{
mp->element_size = element_size;
mp->alignment = MIN_ALIGNMENT;
mp->data_size = num_elements * mp->element_size;
mp->block_size = mp->data_size + FLDSIZE_NEXT;
}

mp->zero_out = zero_out;
mp->exact = exact;
mp->alloc_next = NULL;
mp->chain = NULL;
mp->ring = NULL;
mp->free_list = NULL;
mp->curr_elements = 0;
mp->num_elements = num_elements;

lgdebug(+D_MEMPOOL, "%sElement size %zu, alignment %zu (pool '%s' created in %s())\n",
POOL_ALLOCATOR?"":"(Fake pool allocator) ",
mp->element_size, mp->alignment, mp->name, mp->func);
return mp;
}

/**
* Delete the given memory pool.
*/
void pool_delete(Pool_desc *mp)
{
if (NULL == mp) return;
lgdebug(+D_MEMPOOL, "Used %zu elements (pool '%s' created in %s())\n",
mp->curr_elements, mp->name, mp->func);

/* Free its chained memory blocks. */
char *c_next;
size_t alloc_size;
#if POOL_ALLOCATOR
alloc_size = mp->data_size;
#else
alloc_size = mp->element_size;
#endif
for (char *c = mp->chain; c != NULL; c = c_next)
{
c_next = POOL_NEXT_BLOCK(c, alloc_size);
#if POOL_ALLOCATOR
aligned_free(c);
#else
free(c);
#endif
}
free(mp);
}

#if POOL_ALLOCATOR
/**
* Allocate an element from the requested pool.
* This function uses the feature that pointers to void and char are
* interchangeable.
* 1. If no current block of current block exhausted - obtain another one
* and chain it the block chain. Else reuse an LRU unused block;
* The element pointer is aligned to the required alignment.
* 2. zero the block if required;
* 3. Return element pointer.
*/
void *pool_alloc(Pool_desc *mp)
{
#ifdef POOL_FREE
if (NULL != mp->free_list)
{
void *alloc_next = mp->free_list;
mp->free_list = *(char **)mp->free_list;
if (mp->zero_out) memset(alloc_next, 0, mp->element_size);
return alloc_next;
}
#endif // POOL_FREE

mp->curr_elements++; /* For stats. */

if ((NULL == mp->alloc_next) || (mp->alloc_next == mp->ring + mp->data_size))
{
assert(!mp->exact || (NULL == mp->alloc_next),
"Too many elements %zu>%zu (pool '%s' created in %s())",
mp->curr_elements, mp->num_elements, mp->name, mp->func);

/* No current block or current block exhausted - obtain another one. */
char *prev = mp->ring;
if (NULL != mp->ring)
mp->ring = POOL_NEXT_BLOCK(mp->ring, mp->data_size);

if (NULL == mp->ring)
{
/* Allocate a new block and chain it. */
mp->ring = aligned_alloc(mp->alignment, mp->block_size);
if (NULL == mp->ring)
{
/* aligned_alloc() has strict requirements. */
char errbuf[64];
strerror_r(errno, errbuf, sizeof(errbuf));
assert(NULL == mp->ring, "Block/element sizes %zu/%zu: %s",
mp->block_size, mp->element_size, errbuf);
}
if (NULL == mp->alloc_next)
mp->chain = mp->ring;
else
POOL_NEXT_BLOCK(prev, mp->data_size) = mp->ring;
POOL_NEXT_BLOCK(mp->ring, mp->data_size) = NULL;
//printf("New ring %p next %p\n", mp->ring,
//POOL_NEXT_BLOCK(mp->ring, mp->data_size));
} /* Else reuse existing block. */

if (mp->zero_out) memset(mp->ring, 0, mp->data_size);
mp->alloc_next = mp->ring;
}

/* Grab a new element. */
void *alloc_next = mp->alloc_next;
mp->alloc_next += mp->element_size;
return alloc_next;
}

/**
* Reuse the given memory pool.
* Reset the pool pointers without freeing its memory.
* pool_alloc() will then reuse the existing pool blocks before allocating
* new blocks.
*/
void pool_reuse(Pool_desc *mp)
{
lgdebug(+D_MEMPOOL, "Used %zu elements (pool '%s' created in %s())\n",
mp->curr_elements, mp->name, mp->func);
mp->ring = mp->chain;
mp->alloc_next = NULL;
}

#ifdef POOL_FREE
/**
* Free elements. They are added to a free list that is used by
* pool_alloc() before it allocates from memory blocks.
* XXX Unchecked.
*/
void pool_free(Pool_desc *mp, void *e)
{
assert(mp->element_size >= FLDSIZE_NEXT);
if (NULL == e) return;

char *next = mp->free_list;
mp->free_list = e;
*(char **)e = next;
}
#endif // POOL_FREE

#else // !POOL_ALLOCATOR

/* A dummy pool allocator - for debugging (see the comment in memory-pool.h).
* Note: No Doxygen headers because these function replace functions with
* the same name defined above. */

/*
* Allocate an element by using malloc() directly.
*/
void *pool_alloc(Pool_desc *mp)
{
mp->curr_elements++;
assert(!mp->exact || mp->curr_elements <= mp->num_elements,
"Too many elements (%zu>%zu) (pool '%s' created in %s())",
mp->curr_elements, mp->num_elements, mp->name, mp->func);

/* Allocate a new element and chain it. */
char *next = mp->chain;
mp->chain = malloc(mp->element_size + FLDSIZE_NEXT);
POOL_NEXT_BLOCK(mp->chain, mp->element_size) = next;

void *alloc_next = mp->chain;
if (mp->zero_out) memset(alloc_next, 0, mp->element_size);

return alloc_next;
}

/*
* Reuse the given fake memory pool by freeing its memory.
*/
void pool_reuse(Pool_desc *mp)
{
if (NULL == mp) return;
lgdebug(+D_MEMPOOL, "Used %zu elements (pool '%s' created in %s())\n",
mp->curr_elements, mp->name, mp->func);

/* Free its chained memory blocks. */
char *c_next;
for (char *c = mp->chain; c != NULL; c = c_next)
{
c_next = POOL_NEXT_BLOCK(c, mp->element_size);
free(c);
}

mp->chain = NULL;
}

#ifdef POOL_FREE
void pool_free(Pool_desc *mp, void *e)
{
free(e);
}
#endif // POOL_FREE
#endif // POOL_ALLOCATOR
Loading