optimize and simplify object pools

This commit is contained in:
Andrei Alexeyev 2019-04-12 11:36:40 +03:00
parent 43ed2dcde2
commit 36c5d6974b
No known key found for this signature in database
GPG key ID: 363707CD4C7FE8A4
17 changed files with 89 additions and 172 deletions

View file

@ -12,7 +12,6 @@
#include "list.h"
#include "global.h"
#include "stageobjects.h"
#include "objectpool_util.h"
void aniplayer_create(AniPlayer *plr, Animation *ani, const char *startsequence) {
memset(plr,0,sizeof(AniPlayer));

View file

@ -63,9 +63,9 @@ Enemy *create_enemy_p(EnemyList *enemies, complex pos, float hp, EnemyVisualRule
log_fatal("Tried to spawn an enemy while in drawing code");
}
// XXX: some code relies on the insertion logic
Enemy *e = (Enemy*)alist_insert(enemies, enemies->first, objpool_acquire(stage_object_pools.enemies));
// Enemy *e = (Enemy*)alist_append(enemies, objpool_acquire(stage_object_pools.enemies));
// FIXME: some code relies on the insertion logic (which?)
Enemy *e = alist_insert(enemies, enemies->first, (Enemy*)objpool_acquire(stage_object_pools.enemies));
// Enemy *e = alist_append(enemies, (Enemy*)objpool_acquire(stage_object_pools.enemies));
e->moving = false;
e->dir = 0;
@ -129,7 +129,7 @@ static void* _delete_enemy(ListAnchor *enemies, List* enemy, void *arg) {
e->logic_rule(e, EVENT_DEATH);
ent_unregister(&e->ent);
objpool_release(stage_object_pools.enemies, (ObjectInterface*)alist_unlink(enemies, enemy));
objpool_release(stage_object_pools.enemies, alist_unlink(enemies, enemy));
return NULL;
}

View file

@ -85,7 +85,7 @@ typedef void (*EntityDrawHookCallback)(EntityInterface *ent, void *arg);
typedef void (*EntityAreaDamageCallback)(EntityInterface *ent, complex ent_origin, void *arg);
#define ENTITY_INTERFACE_BASE(typename) struct { \
OBJECT_INTERFACE(typename); \
LIST_INTERFACE(typename); \
EntityType type; \
EntityDrawFunc draw_func; \
EntityDamageFunc damage_func; \
@ -100,7 +100,7 @@ typedef void (*EntityAreaDamageCallback)(EntityInterface *ent, complex ent_origi
}
#define ENTITY_INTERFACE_NAMED(typename, name) union { \
OBJECT_INTERFACE(typename); \
LIST_INTERFACE(typename); \
EntityInterface entity_interface; \
EntityInterface name; \
}

View file

@ -12,7 +12,6 @@
#include "global.h"
#include "list.h"
#include "stageobjects.h"
#include "objectpool_util.h"
static const char* item_sprite_name(ItemType type) {
static const char *const map[] = {
@ -85,7 +84,7 @@ Item* create_item(complex pos, complex v, ItemType type) {
void delete_item(Item *item) {
ent_unregister(&item->ent);
objpool_release(stage_object_pools.items, &alist_unlink(&global.items, item)->object_interface);
objpool_release(stage_object_pools.items, alist_unlink(&global.items, item));
}
Item *create_clear_item(complex pos, uint clear_flags) {

View file

@ -102,7 +102,7 @@ void lasers_free(void) {
static void ent_draw_laser(EntityInterface *ent);
Laser *create_laser(complex pos, float time, float deathtime, const Color *color, LaserPosRule prule, LaserLogicRule lrule, complex a0, complex a1, complex a2, complex a3) {
Laser *l = (Laser*)alist_push(&global.lasers, objpool_acquire(stage_object_pools.lasers));
Laser *l = alist_push(&global.lasers, (Laser*)objpool_acquire(stage_object_pools.lasers));
l->birthtime = global.frames;
l->timespan = time;
@ -345,7 +345,7 @@ static void* _delete_laser(ListAnchor *lasers, List *laser, void *arg) {
l->lrule(l, EVENT_DEATH);
ent_unregister(&l->ent);
objpool_release(stage_object_pools.lasers, (ObjectInterface*)alist_unlink(lasers, laser));
objpool_release(stage_object_pools.lasers, alist_unlink(lasers, laser));
return NULL;
}

View file

@ -68,7 +68,6 @@ taisei_src = files(
'list.c',
'log.c',
'main.c',
'objectpool_util.c',
'player.c',
'plrmodes.c',
'progress.c',

View file

@ -12,42 +12,45 @@
#include "util.h"
#include "list.h"
typedef struct ObjHeader {
alignas(alignof(max_align_t)) struct ObjHeader *next;
} ObjHeader;
struct ObjectPool {
char *tag;
size_t size_of_object;
size_t max_objects;
#ifdef OBJPOOL_TRACK_STATS
size_t usage;
size_t peak_usage;
#endif
size_t num_extents;
char **extents;
ObjectInterface *free_objects;
ObjHeader *free_objects;
char objects[];
};
static inline ObjectInterface* obj_ptr(ObjectPool *pool, char *objects, size_t idx) {
return (ObjectInterface*)(void*)(objects + idx * pool->size_of_object);
inline attr_must_inline attr_returns_max_aligned
static ObjHeader *obj_ptr(ObjectPool *pool, char *objects, size_t idx) {
return CASTPTR_ASSUME_ALIGNED(objects + idx * pool->size_of_object, ObjHeader);
}
static void objpool_register_objects(ObjectPool *pool, char *objects) {
for(size_t i = 0; i < pool->max_objects; ++i) {
list_push(&pool->free_objects, obj_ptr(pool, objects, i));
ObjHeader *o = obj_ptr(pool, objects, i);
o->next = pool->free_objects;
pool->free_objects = o;
}
}
ObjectPool *objpool_alloc(size_t obj_size, size_t max_objects, const char *tag) {
// TODO: overflow handling
ObjectPool *pool = malloc(sizeof(ObjectPool) + (obj_size * max_objects));
ObjectPool *pool = calloc(1, sizeof(ObjectPool) + (obj_size * max_objects));
pool->size_of_object = obj_size;
pool->max_objects = max_objects;
pool->free_objects = NULL;
pool->usage = 0;
pool->peak_usage = 0;
pool->num_extents = 0;
pool->extents = NULL;
pool->tag = strdup(tag);
memset(pool->objects, 0, obj_size * max_objects);
objpool_register_objects(pool, pool->objects);
log_debug("[%s] Allocated pool for %zu objects, %zu bytes each",
@ -89,22 +92,20 @@ static char* objpool_fmt_size(ObjectPool *pool) {
}
}
ObjectInterface *objpool_acquire(ObjectPool *pool) {
ObjectInterface *obj = list_pop(&pool->free_objects);
void *objpool_acquire(ObjectPool *pool) {
ObjHeader *obj = pool->free_objects;
if(obj) {
acquired:
pool->free_objects = obj->next;
memset(obj, 0, pool->size_of_object);
IF_OBJPOOL_DEBUG({
obj->_object_private.used = true;
})
#ifdef OBJPOOL_TRACK_STATS
if(++pool->usage > pool->peak_usage) {
pool->peak_usage = pool->usage;
}
#endif
// log_debug("[%s] Usage: %zu", pool->tag, pool->usage);
return obj;
}
@ -116,39 +117,27 @@ acquired:
free(tmp);
objpool_add_extent(pool);
obj = list_pop(&pool->free_objects);
obj = pool->free_objects;
assert(obj != NULL);
goto acquired;
}
void objpool_release(ObjectPool *pool, ObjectInterface *object) {
void objpool_release(ObjectPool *pool, void *object) {
objpool_memtest(pool, object);
IF_OBJPOOL_DEBUG({
if(!object->_object_private.used) {
log_fatal("[%s] Attempted to release an unused object %p",
pool->tag,
(void*)object
);
}
object->_object_private.used = false;
})
list_push(&pool->free_objects, object);
ObjHeader *obj = object;
obj->next = pool->free_objects;
pool->free_objects = obj;
#ifdef OBJPOOL_TRACK_STATS
pool->usage--;
// log_debug("[%s] Usage: %zu", pool->tag, pool->usage);
#endif
}
void objpool_free(ObjectPool *pool) {
if(!pool) {
return;
}
#ifdef OBJPOOL_TRACK_STATS
if(pool->usage != 0) {
log_warn("[%s] %zu objects still in use", pool->tag, pool->usage);
}
#endif
for(size_t i = 0; i < pool->num_extents; ++i) {
free(pool->extents[i]);
@ -166,12 +155,17 @@ size_t objpool_object_size(ObjectPool *pool) {
void objpool_get_stats(ObjectPool *pool, ObjectPoolStats *stats) {
stats->tag = pool->tag;
stats->capacity = pool->max_objects * (1 + pool->num_extents);
#ifdef OBJPOOL_TRACK_STATS
stats->usage = pool->usage;
stats->peak_usage = pool->peak_usage;
#else
stats->usage = 0;
stats->peak_usage = 0;
#endif
}
attr_unused
static bool objpool_object_in_subpool(ObjectPool *pool, ObjectInterface *object, char *objects) {
static bool objpool_object_in_subpool(ObjectPool *pool, ObjHeader *object, char *objects) {
char *objofs = (char*)object;
char *minofs = objects;
char *maxofs = objects + (pool->max_objects - 1) * pool->size_of_object;
@ -194,7 +188,7 @@ static bool objpool_object_in_subpool(ObjectPool *pool, ObjectInterface *object,
}
attr_unused
static bool objpool_object_in_pool(ObjectPool *pool, ObjectInterface *object) {
static bool objpool_object_in_pool(ObjectPool *pool, ObjHeader *object) {
if(objpool_object_in_subpool(pool, object, pool->objects)) {
return true;
}
@ -208,16 +202,13 @@ static bool objpool_object_in_pool(ObjectPool *pool, ObjectInterface *object) {
return false;
}
void objpool_memtest(ObjectPool *pool, ObjectInterface *object) {
assert(pool != NULL);
assert(object != NULL);
IF_OBJPOOL_DEBUG({
if(!objpool_object_in_pool(pool, object)) {
log_fatal("[%s] Object pointer %p does not belong to this pool",
pool->tag,
(void*)object
);
}
})
#ifdef OBJPOOL_DEBUG
void objpool_memtest(ObjectPool *pool, void *object) {
if(!objpool_object_in_pool(pool, object)) {
log_fatal("[%s] Object pointer %p does not belong to this pool",
pool->tag,
object
);
}
}
#endif

View file

@ -18,13 +18,13 @@
#endif
#ifdef OBJPOOL_DEBUG
#define OBJPOOL_TRACK_STATS
#define IF_OBJPOOL_DEBUG(code) code
#else
#define IF_OBJPOOL_DEBUG(code)
#endif
typedef struct ObjectPool ObjectPool;
typedef struct ObjectInterface ObjectInterface;
typedef struct ObjectPoolStats ObjectPoolStats;
struct ObjectPoolStats {
@ -34,46 +34,20 @@ struct ObjectPoolStats {
size_t peak_usage;
};
#define OBJECT_INTERFACE_BASE(typename) struct { \
LIST_INTERFACE(typename); \
IF_OBJPOOL_DEBUG( \
struct { \
bool used; \
} _object_private; \
) \
}
#define OBJECT_INTERFACE(typename) union { \
ObjectInterface object_interface; \
OBJECT_INTERFACE_BASE(typename); \
}
struct ObjectInterface {
OBJECT_INTERFACE_BASE(ObjectInterface);
};
#ifdef USE_GNU_EXTENSIONS
// Do some compile-time checks to make sure the type is compatible with object pools
#define OBJPOOL_ALLOC(typename,max_objects) (__extension__ ({ \
static_assert(__builtin_types_compatible_p(ObjectInterface, __typeof__(((typename*)(0))->object_interface)), \
#typename " must implement ObjectInterface (use the OBJECT_INTERFACE macro)"); \
static_assert(__builtin_offsetof(typename, object_interface) == 0, \
"object_interface must be the first member in " #typename); \
objpool_alloc(sizeof(typename), max_objects, #typename); \
}))
#else
#define OBJPOOL_ALLOC(typename,max_objects) objpool_alloc(sizeof(typename), max_objects, #typename)
#endif
ObjectPool *objpool_alloc(size_t obj_size, size_t max_objects, const char *tag);
void objpool_free(ObjectPool *pool);
ObjectInterface *objpool_acquire(ObjectPool *pool);
void objpool_release(ObjectPool *pool, ObjectInterface *object);
void objpool_get_stats(ObjectPool *pool, ObjectPoolStats *stats);
void objpool_memtest(ObjectPool *pool, ObjectInterface *object);
size_t objpool_object_size(ObjectPool *pool);
#define OBJPOOL_ALLOC(typename,max_objects) objpool_alloc(sizeof(typename), max_objects, #typename)
#define OBJPOOL_ACQUIRE(pool, type) CASTPTR_ASSUME_ALIGNED(objpool_acquire(pool), type)
ObjectPool *objpool_alloc(size_t obj_size, size_t max_objects, const char *tag) attr_returns_nonnull attr_nodiscard attr_nonnull(3);
void objpool_free(ObjectPool *pool) attr_nonnull(1);
void *objpool_acquire(ObjectPool *pool) attr_returns_max_aligned attr_returns_nonnull attr_nodiscard attr_hot attr_nonnull(1);
void objpool_release(ObjectPool *pool, void *object) attr_hot attr_nonnull(1, 2);
void objpool_get_stats(ObjectPool *pool, ObjectPoolStats *stats) attr_nonnull(1, 2);
size_t objpool_object_size(ObjectPool *pool) attr_nonnull(1);
#ifdef OBJPOOL_DEBUG
void objpool_memtest(ObjectPool *pool, void *object) attr_nonnull(1, 2);
#else
#define objpool_memtest(pool, object) ((void)(pool), (void)(object))
#endif
#endif // IGUARD_objectpool_h

View file

@ -21,11 +21,11 @@ ObjectPool *objpool_alloc(size_t obj_size, size_t max_objects, const char *tag)
return pool;
}
ObjectInterface *objpool_acquire(ObjectPool *pool) {
void *objpool_acquire(ObjectPool *pool) {
return calloc(1, pool->size_of_object);
}
void objpool_release(ObjectPool *pool, ObjectInterface *object) {
void objpool_release(ObjectPool *pool, void *object) {
free(object);
}
@ -38,11 +38,11 @@ void objpool_get_stats(ObjectPool *pool, ObjectPoolStats *stats) {
stats->tag = "<N/A>";
}
void objpool_memtest(ObjectPool *pool, ObjectInterface *object) {
assert(pool != NULL);
assert(object != NULL);
}
size_t objpool_object_size(ObjectPool *pool) {
return pool->size_of_object;
}
#ifdef OBJPOOL_DEBUG
void objpool_memtest(ObjectPool *pool, void *object) {
}
#endif

View file

@ -1,33 +0,0 @@
/*
* This software is licensed under the terms of the MIT-License
* See COPYING for further information.
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@alienslab.net>.
*/
#include "taisei.h"
#include "objectpool_util.h"
#include "util.h"
bool objpool_is_full(ObjectPool *pool) {
ObjectPoolStats stats;
objpool_get_stats(pool, &stats);
return stats.capacity == stats.usage;
}
size_t objpool_object_contents_size(ObjectPool *pool) {
return objpool_object_size(pool) - sizeof(ObjectInterface);
}
void *objpool_object_contents(ObjectPool *pool, ObjectInterface *obj, size_t *out_size) {
assert(obj != NULL);
if(out_size != NULL) {
objpool_memtest(pool, obj);
*out_size = objpool_object_contents_size(pool);
}
return (char*)obj + sizeof(ObjectInterface);
}

View file

@ -1,20 +0,0 @@
/*
* This software is licensed under the terms of the MIT-License
* See COPYING for further information.
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@alienslab.net>.
*/
#ifndef IGUARD_objectpool_util_h
#define IGUARD_objectpool_util_h
#include "taisei.h"
#include "objectpool.h"
bool objpool_is_full(ObjectPool *pool);
size_t objpool_object_contents_size(ObjectPool *pool);
void *objpool_object_contents(ObjectPool *pool, ObjectInterface *obj, size_t *out_size);
#endif // IGUARD_objectpool_util_h

View file

@ -295,7 +295,7 @@ static void* _delete_projectile(ListAnchor *projlist, List *proj, void *arg) {
Projectile *p = (Projectile*)proj;
proj_call_rule(p, EVENT_DEATH);
ent_unregister(&p->ent);
objpool_release(stage_object_pools.projectiles, (ObjectInterface*)alist_unlink(projlist, proj));
objpool_release(stage_object_pools.projectiles, alist_unlink(projlist, proj));
return NULL;
}

View file

@ -16,11 +16,11 @@
#include "stagetext.h"
#include "aniplayer.h"
#define MAX_projectiles 1024
#define MAX_projectiles 2048
#define MAX_items MAX_projectiles
#define MAX_enemies 64
#define MAX_lasers 64
#define MAX_stagetext 128
#define MAX_stagetext 1024
#define OBJECT_POOLS \
OBJECT_POOL(Projectile, projectiles) \

View file

@ -51,7 +51,7 @@ StageText* stagetext_add_numeric(int n, complex pos, Alignment align, Font *font
}
static void* stagetext_delete(List **dest, List *txt, void *arg) {
objpool_release(stage_object_pools.stagetext, (ObjectInterface*)list_unlink(dest, txt));
objpool_release(stage_object_pools.stagetext, list_unlink(dest, txt));
return NULL;
}

View file

@ -26,7 +26,7 @@ typedef struct StageTextTable StageTextTable;
#define STAGETEXT_BUF_SIZE 76
struct StageText {
OBJECT_INTERFACE(StageText);
LIST_INTERFACE(StageText);
Font *font;
complex pos;

View file

@ -233,6 +233,14 @@ typedef complex max_align_t;
#define attr_must_inline \
__attribute__ ((always_inline))
// Function returns a pointer aligned to x bytes
#define attr_returns_aligned(x) \
__attribute__ ((assume_aligned(x)))
// Function returns a pointer aligned the same as max_align_t
#define attr_returns_max_aligned \
attr_returns_aligned(alignof(max_align_t))
// Structure must not be initialized with an implicit (non-designated) initializer.
#if __has_attribute(designated_init) && defined(TAISEI_BUILDCONF_USE_DESIGNATED_INIT)
#define attr_designated_init \

View file

@ -40,7 +40,7 @@ void capproach_asymptotic_p(complex *val, complex target, double rate, double ep
double psin(double) attr_const;
int sign(double) attr_const;
double swing(double x, double s) attr_const;
uint32_t topow2_u32(uint32_t x) attr_const;
uint32_t topow2_u32(uint32_t x) attr_const;
uint64_t topow2_u64(uint64_t x) attr_const;
float ftopow2(float x) attr_const;
float smooth(float x) attr_const;