From f10769a677bd2a4b7573ace09ba461f06bb8a173 Mon Sep 17 00:00:00 2001 From: Andrea Shepard <andrea@special-circumstanc.es> Date: Sat, 11 Jan 2020 18:12:37 +0000 Subject: [PATCH] Eliminate unnecessary hash computations in the packrat backend (roughly 2.5x speed in one benchmark) --- src/backends/packrat.c | 77 +++++++++++++++++++++++++++--------------- 1 file changed, 50 insertions(+), 27 deletions(-) diff --git a/src/backends/packrat.c b/src/backends/packrat.c index 276dfd17..91e4a092 100644 --- a/src/backends/packrat.c +++ b/src/backends/packrat.c @@ -3,6 +3,8 @@ #include "../internal.h" #include "../parsers/parser_internal.h" +static uint32_t cache_key_hash(const void* key); + // short-hand for creating lowlevel parse cache values (parse result case) static HParserCacheValue * cached_result(HParseState *state, HParseResult *result) { @@ -56,31 +58,38 @@ static inline HParseResult* perform_lowlevel_parse(HParseState *state, const HPa return tmp_res; } -HParserCacheValue* recall(HParserCacheKey *k, HParseState *state) { - HParserCacheValue *cached = h_hashtable_get(state->cache, k); +HParserCacheValue* recall(HParserCacheKey *k, HParseState *state, HHashValue keyhash) { + HParserCacheValue *cached = h_hashtable_get_precomp(state->cache, k, keyhash); HRecursionHead *head = h_hashtable_get(state->recursion_heads, &k->input_pos); - if (!head) { // No heads found + + if (!head) { + /* No heads found */ return cached; - } else { // Some heads found + } else { + /* Some heads found */ if (!cached && head->head_parser != k->parser && !h_slist_find(head->involved_set, k->parser)) { - // Nothing in the cache, and the key parser is not involved + /* Nothing in the cache, and the key parser is not involved */ cached = cached_result(state, NULL); cached->input_stream = k->input_pos; } if (h_slist_find(head->eval_set, k->parser)) { - // Something is in the cache, and the key parser is in the eval set. Remove the key parser from the eval set of the head. + /* + * Something is in the cache, and the key parser is in the eval set. + * Remove the key parser from the eval set of the head. + */ head->eval_set = h_slist_remove_all(head->eval_set, k->parser); HParseResult *tmp_res = perform_lowlevel_parse(state, k->parser); - // update the cache + /* update the cache */ if (!cached) { - cached = cached_result(state, tmp_res); - h_hashtable_put(state->cache, k, cached); + cached = cached_result(state, tmp_res); + h_hashtable_put_precomp(state->cache, k, cached, keyhash); } else { - cached->value_type = PC_RIGHT; - cached->right = tmp_res; - cached->input_stream = state->input_stream; + cached->value_type = PC_RIGHT; + cached->right = tmp_res; + cached->input_stream = state->input_stream; } } + return cached; } } @@ -180,36 +189,50 @@ HParseResult* lr_answer(HParserCacheKey *k, HParseState *state, HLeftRec *growab /* Warth's recursion. Hi Alessandro! */ HParseResult* h_do_parse(const HParser* parser, HParseState *state) { HParserCacheKey *key = a_new(HParserCacheKey, 1); + HHashValue keyhash; + HLeftRec *base = NULL; + HParserCacheValue *m = NULL, *cached = NULL; + key->input_pos = state->input_stream; key->parser = parser; - HParserCacheValue *m = NULL; + keyhash = cache_key_hash(key); + if (parser->vtable->higher) { - m = recall(key, state); + m = recall(key, state, keyhash); } - // check to see if there is already a result for this object... + + /* check to see if there is already a result for this object... */ if (!m) { - // It doesn't exist, so create a dummy result to cache - HLeftRec *base = NULL; - // But only cache it now if there's some chance it could grow; primitive parsers can't + /* + * But only cache it now if there's some chance it could grow; primitive + * parsers can't + */ if (parser->vtable->higher) { base = a_new(HLeftRec, 1); base->seed = NULL; base->rule = parser; base->head = NULL; h_slist_push(state->lr_stack, base); - // cache it - h_hashtable_put(state->cache, key, cached_lr(state, base)); - // parse the input + /* cache it */ + h_hashtable_put_precomp(state->cache, key, + cached_lr(state, base), keyhash); } + + /* parse the input */ HParseResult *tmp_res = perform_lowlevel_parse(state, parser); if (parser->vtable->higher) { - // the base variable has passed equality tests with the cache + /* the base variable has passed equality tests with the cache */ h_slist_pop(state->lr_stack); - // update the cached value to our new position - HParserCacheValue *cached = h_hashtable_get(state->cache, key); + /* update the cached value to our new position */ + cached = h_hashtable_get_precomp(state->cache, key, keyhash); assert(cached != NULL); cached->input_stream = state->input_stream; } - // setupLR, used below, mutates the LR to have a head if appropriate, so we check to see if we have one + + /* + * setupLR, used below, mutates the LR to have a head if appropriate, + * so we check to see if we have one + */ if (!base || NULL == base->head) { - h_hashtable_put(state->cache, key, cached_result(state, tmp_res)); + h_hashtable_put_precomp(state->cache, key, + cached_result(state, tmp_res), keyhash); return tmp_res; } else { base->seed = tmp_res; @@ -217,7 +240,7 @@ HParseResult* h_do_parse(const HParser* parser, HParseState *state) { return res; } } else { - // it exists! + /* it exists! */ state->input_stream = m->input_stream; if (PC_LEFT == m->value_type) { setupLR(parser, state, m->left); -- GitLab