Grow resizables more than requested to avoid excessive alloocator calls
This commit is contained in:
parent
e5c4f32126
commit
8ecbba5217
4 changed files with 108 additions and 36 deletions
16
src/alloc.c
16
src/alloc.c
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "apfl.h"
|
||||
#include "alloc.h"
|
||||
#include "intrinsics.h"
|
||||
|
||||
#define ALLOC_DEBUG 1
|
||||
|
||||
|
|
@ -83,17 +84,6 @@ apfl_allocator_default(void)
|
|||
: libc_allocator;
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
# define MULTIPLY_CHECK_OVERFLOW(res, a, b) __builtin_mul_overflow((a), (b), &(res))
|
||||
#else
|
||||
# define MULTIPLY_CHECK_OVERFLOW(res, a, b) \
|
||||
( \
|
||||
(b) != 0 && (a) > SIZE_MAX / (b) \
|
||||
? true \
|
||||
: ((res = (a) * (b)), false) \
|
||||
)
|
||||
#endif
|
||||
|
||||
void *
|
||||
apfl_alloc_realloc_array(
|
||||
struct apfl_allocator allocator,
|
||||
|
|
@ -105,10 +95,10 @@ apfl_alloc_realloc_array(
|
|||
size_t oldsize;
|
||||
size_t newsize;
|
||||
|
||||
if (MULTIPLY_CHECK_OVERFLOW(oldsize, elem_size, oldlen)) {
|
||||
if (MULTIPLY_CHECK_OVERFLOW_SIZE(oldsize, elem_size, oldlen)) {
|
||||
return NULL;
|
||||
}
|
||||
if (MULTIPLY_CHECK_OVERFLOW(newsize, elem_size, newlen)) {
|
||||
if (MULTIPLY_CHECK_OVERFLOW_SIZE(newsize, elem_size, newlen)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
|||
29
src/intrinsics.h
Normal file
29
src/intrinsics.h
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
#ifndef APFL_INTRINSICS_H
|
||||
#define APFL_INTRINSICS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#if defined(__GNUC__) && !defined(APFL_NO_INTRINSICS)
|
||||
#define APFL_USE_INTRINSICS
|
||||
#endif
|
||||
|
||||
#ifdef APFL_USE_INTRINSICS
|
||||
# define MULTIPLY_CHECK_OVERFLOW_SIZE(res, a, b) __builtin_mul_overflow((a), (b), &(res))
|
||||
#else
|
||||
# define MULTIPLY_CHECK_OVERFLOW_SIZE(res, a, b) \
|
||||
( \
|
||||
(b) != 0 && (a) > SIZE_MAX / (b) \
|
||||
? true \
|
||||
: ((res = (a) * (b)), false) \
|
||||
)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -172,23 +172,28 @@ enum listbuilder_cmd {
|
|||
|
||||
#define LISTBUILDER_NAME(n) listbuilder_##n
|
||||
#define LISTBUILDER_ITEM_NAME(n) listbuilder_item_##n
|
||||
#define LISTBUILDER_FILL_LIST(pt, itemtype, items, list_items, list_len, cap) \
|
||||
apfl_resizable_init((void **)&list_items, &list_len, &cap); \
|
||||
\
|
||||
for (items++; items->has_item; items++) { \
|
||||
if (!apfl_resizable_append( \
|
||||
pt->allocator, \
|
||||
sizeof(itemtype), \
|
||||
(void **)&list_items, \
|
||||
&list_len, \
|
||||
&cap, \
|
||||
&items->item, \
|
||||
1 \
|
||||
)) { \
|
||||
test_fatalf(pt->t, "Failed appending"); \
|
||||
assert(false); \
|
||||
} \
|
||||
} \
|
||||
#define LISTBUILDER_FILL_LIST(pt, itemtype, items, list_items, list_len, cap) \
|
||||
apfl_resizable_init((void **)&list_items, &list_len, &cap); \
|
||||
\
|
||||
size_t len = 0; \
|
||||
for (items++; items->has_item; items++) { \
|
||||
len++; \
|
||||
} \
|
||||
\
|
||||
if (len > 0) { \
|
||||
if ((list_items = ALLOC_LIST(pt->allocator, itemtype, len)) == NULL) { \
|
||||
test_fatalf(pt->t, "Could not allocate memory"); \
|
||||
assert(false); \
|
||||
} \
|
||||
\
|
||||
cap = len; \
|
||||
list_len = len; \
|
||||
for (; len-- > 0; ) { \
|
||||
items--; \
|
||||
list_items[len] = items->item; \
|
||||
} \
|
||||
} \
|
||||
|
||||
|
||||
#define MKLISTBUILDER(name, listtype, itemtype, items_memb) \
|
||||
struct LISTBUILDER_ITEM_NAME(name) { \
|
||||
|
|
|
|||
|
|
@ -5,8 +5,13 @@
|
|||
#include <string.h>
|
||||
|
||||
#include "alloc.h"
|
||||
#include "intrinsics.h"
|
||||
#include "resizable.h"
|
||||
|
||||
#define DOUBLE_THRESHOLD 100
|
||||
#define LINEAR_STEP 10
|
||||
#define SHRINK_TOLERANCE 10
|
||||
|
||||
void
|
||||
apfl_resizable_init(void **mem, size_t *len, size_t *cap)
|
||||
{
|
||||
|
|
@ -15,6 +20,28 @@ apfl_resizable_init(void **mem, size_t *len, size_t *cap)
|
|||
*cap = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
shrink_cap(
|
||||
struct apfl_allocator allocator,
|
||||
size_t elem_size,
|
||||
void **mem,
|
||||
size_t *cap,
|
||||
size_t new_min_cap
|
||||
) {
|
||||
assert(new_min_cap <= *cap);
|
||||
|
||||
if (*cap - new_min_cap < SHRINK_TOLERANCE) {
|
||||
return;
|
||||
}
|
||||
|
||||
void *new_mem = REALLOC_UNTYPED_ARRAY(allocator, *mem, elem_size, *cap, new_min_cap);
|
||||
// TODO: Check other shrinking reallocs. Some wrongly assume this will never fail.
|
||||
if (new_mem != NULL || new_min_cap == 0) {
|
||||
*mem = new_mem;
|
||||
*cap = new_min_cap;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
apfl_resizable_resize(
|
||||
struct apfl_allocator allocator,
|
||||
|
|
@ -24,9 +51,9 @@ apfl_resizable_resize(
|
|||
size_t *cap,
|
||||
size_t newlen
|
||||
) {
|
||||
// TODO: We're wasteful here by never actually shrinking the memory.
|
||||
if (newlen <= *len || newlen < *cap) {
|
||||
*len = newlen;
|
||||
shrink_cap(allocator, elem_size, mem, cap, newlen);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -52,16 +79,37 @@ apfl_resizable_ensure_cap(
|
|||
return true;
|
||||
}
|
||||
|
||||
// TODO: We currently simply grow the memory to have space for exactly
|
||||
// want_cap elements. It would probably be smarter to grow the memory
|
||||
// a bit larger to reduce calls to realloc.
|
||||
void *newmem = REALLOC_UNTYPED_ARRAY(allocator, *mem, elem_size, *cap, want_cap);
|
||||
size_t new_cap = *cap || 1;
|
||||
while (
|
||||
new_cap < DOUBLE_THRESHOLD
|
||||
&& new_cap < want_cap
|
||||
) {
|
||||
size_t tmp;
|
||||
if (MULTIPLY_CHECK_OVERFLOW_SIZE(tmp, new_cap, 2)) {
|
||||
break;
|
||||
}
|
||||
new_cap = tmp;
|
||||
}
|
||||
|
||||
if (new_cap < want_cap) {
|
||||
if (SIZE_MAX - LINEAR_STEP > want_cap) {
|
||||
new_cap = want_cap + LINEAR_STEP;
|
||||
} else {
|
||||
// New size would overflow. Last chance is to use the want_cap
|
||||
// without additional LINEAR_STEP
|
||||
new_cap = want_cap;
|
||||
}
|
||||
}
|
||||
|
||||
assert(new_cap >= want_cap);
|
||||
|
||||
void *newmem = REALLOC_UNTYPED_ARRAY(allocator, *mem, elem_size, *cap, new_cap);
|
||||
if (newmem == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*mem = newmem;
|
||||
*cap = want_cap;
|
||||
*cap = new_cap;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue