Use an object pool for stack nodes, to reduce allocations

Also, fix some leaks in the case where memory allocation failed during parsing
This commit is contained in:
Max Brunsfeld 2016-02-04 11:15:46 -08:00
parent a302ee822a
commit c96c4a08e6
8 changed files with 196 additions and 103 deletions

View file

@ -10,6 +10,7 @@
#define MAX_SUCCESSOR_COUNT 8
#define INITIAL_HEAD_CAPACITY 3
#define STARTING_TREE_CAPACITY 10
#define MAX_NODE_POOL_SIZE 50
typedef struct StackNode {
StackEntry entry;
@ -24,6 +25,7 @@ struct Stack {
int head_capacity;
Vector pop_results;
Vector pop_paths;
Vector node_pool;
void *tree_selection_payload;
TreeSelectionFunction tree_selection_function;
};
@ -50,20 +52,26 @@ Stack *ts_stack_new() {
self->head_count = 1;
self->head_capacity = INITIAL_HEAD_CAPACITY;
self->heads = NULL;
self->pop_results = vector_new(sizeof(StackPopResult));
self->pop_paths = vector_new(sizeof(PopPath));
self->node_pool = vector_new(sizeof(StackNode *));
self->tree_selection_payload = NULL;
self->tree_selection_function = ts_stack__default_tree_selection;
self->heads = ts_calloc(INITIAL_HEAD_CAPACITY, sizeof(StackNode *));
if (!self->heads)
goto error;
self->pop_results = vector_new(sizeof(StackPopResult), 4);
if (!vector_valid(&self->pop_results))
if (!vector_grow(&self->pop_results, 4))
goto error;
self->pop_paths = vector_new(sizeof(PopPath), 4);
if (!vector_valid(&self->pop_paths))
if (!vector_grow(&self->pop_paths, 4))
goto error;
if (!vector_grow(&self->node_pool, 20))
goto error;
self->tree_selection_payload = NULL;
self->tree_selection_function = ts_stack__default_tree_selection;
return self;
error:
@ -74,6 +82,8 @@ error:
vector_delete(&self->pop_results);
if (self->pop_paths.contents)
vector_delete(&self->pop_paths);
if (self->node_pool.contents)
vector_delete(&self->node_pool);
ts_free(self);
}
return NULL;
@ -127,40 +137,50 @@ static void stack_node_retain(StackNode *self) {
self->ref_count++;
}
static bool stack_node_release(StackNode *self) {
if (!self)
static bool stack_node_release(Stack *self, StackNode *node) {
if (!node)
return false;
assert(self->ref_count != 0);
self->ref_count--;
if (self->ref_count == 0) {
for (int i = 0; i < self->successor_count; i++)
stack_node_release(self->successors[i]);
ts_tree_release(self->entry.tree);
ts_free(self);
assert(node->ref_count != 0);
node->ref_count--;
if (node->ref_count == 0) {
for (int i = 0; i < node->successor_count; i++)
stack_node_release(self, node->successors[i]);
ts_tree_release(node->entry.tree);
if (self->node_pool.size >= MAX_NODE_POOL_SIZE)
ts_free(node);
else
vector_push(&self->node_pool, &node);
return true;
} else {
return false;
}
}
static StackNode *stack_node_new(StackNode *next, TSStateId state, TSTree *tree) {
static StackNode *stack_node_new(Stack *self, StackNode *next, TSStateId state, TSTree *tree) {
assert(tree->ref_count > 0);
StackNode *self = ts_malloc(sizeof(StackNode));
if (!self)
return NULL;
StackNode *node;
if (self->node_pool.size == 0) {
node = ts_malloc(sizeof(StackNode));
if (!node)
return NULL;
} else {
node = *(StackNode **)vector_pop(&self->node_pool);
}
ts_tree_retain(tree);
stack_node_retain(next);
TSLength position = ts_tree_total_size(tree);
if (next)
position = ts_length_add(next->entry.position, position);
*self = (StackNode){
*node = (StackNode){
.ref_count = 1,
.successor_count = 1,
.successors = { next, NULL, NULL },
.entry = {.state = state, .tree = tree, .position = position },
};
return self;
return node;
}
static void ts_stack__add_alternative_tree(Stack *self, StackNode *node,
@ -225,7 +245,7 @@ static int ts_stack__find_head(Stack *self, StackNode *node) {
}
void ts_stack_remove_head(Stack *self, int head_index) {
stack_node_release(self->heads[head_index]);
stack_node_release(self, self->heads[head_index]);
for (int i = head_index; i < self->head_count - 1; i++)
self->heads[i] = self->heads[i + 1];
self->head_count--;
@ -262,11 +282,11 @@ StackPushResult ts_stack_push(Stack *self, int head_index, TSStateId state,
if (ts_stack__merge_head(self, head_index, state, tree, position))
return StackPushResultMerged;
StackNode *new_head = stack_node_new(self->heads[head_index], state, tree);
StackNode *new_head = stack_node_new(self, self->heads[head_index], state, tree);
if (!new_head)
return StackPushResultFailed;
stack_node_release(self->heads[head_index]);
stack_node_release(self, self->heads[head_index]);
self->heads[head_index] = new_head;
return StackPushResultContinued;
}
@ -292,11 +312,11 @@ Vector ts_stack_pop(Stack *self, int head_index, int child_count,
PopPath initial_path = {
.goal_tree_count = child_count,
.node = previous_head,
.trees = vector_new(sizeof(TSTree *), capacity),
.trees = vector_new(sizeof(TSTree *)),
.is_shared = false,
};
if (!vector_valid(&initial_path.trees))
if (!vector_grow(&initial_path.trees, capacity))
goto error;
if (!vector_push(&self->pop_paths, &initial_path))
@ -382,11 +402,12 @@ Vector ts_stack_pop(Stack *self, int head_index, int child_count,
goto error;
}
stack_node_release(previous_head);
stack_node_release(self, previous_head);
return self->pop_results;
error:
return vector_new(0, 0);
vector_delete(&initial_path.trees);
return vector_new(0);
}
void ts_stack_shrink(Stack *self, int head_index, int count) {
@ -398,13 +419,13 @@ void ts_stack_shrink(Stack *self, int head_index, int count) {
new_head = new_head->successors[0];
}
stack_node_retain(new_head);
stack_node_release(head);
stack_node_release(self, head);
self->heads[head_index] = new_head;
}
void ts_stack_clear(Stack *self) {
for (int i = 0; i < self->head_count; i++)
stack_node_release(self->heads[i]);
stack_node_release(self, self->heads[i]);
self->head_count = 1;
self->heads[0] = NULL;
}
@ -416,9 +437,17 @@ void ts_stack_set_tree_selection_callback(Stack *self, void *payload,
}
void ts_stack_delete(Stack *self) {
vector_delete(&self->pop_results);
vector_delete(&self->pop_paths);
if (self->pop_paths.contents)
vector_delete(&self->pop_results);
if (self->pop_paths.contents)
vector_delete(&self->pop_paths);
ts_stack_clear(self);
for (size_t i = 0; i < self->node_pool.size; i++) {
StackNode **node = vector_get(&self->node_pool, i);
ts_free(*node);
}
if (self->node_pool.contents)
vector_delete(&self->node_pool);
ts_free(self->heads);
ts_free(self);
}