From fd0986071c082170fa154ce6d7695805ad513515 Mon Sep 17 00:00:00 2001 From: Andrew Mayorov Date: Fri, 4 Aug 2023 19:27:43 +0400 Subject: [PATCH] perf(topicidx): implement fast-forwarding prefixes This should give less `ets:next/2` calls in general and much less when index has relatively small number of long non-wildcard topics. --- apps/emqx/src/emqx_topic_index.erl | 20 ++++++++++++++++---- apps/emqx/test/emqx_topic_index_SUITE.erl | 10 ++++++++++ 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/apps/emqx/src/emqx_topic_index.erl b/apps/emqx/src/emqx_topic_index.erl index 29d8694af..44e88e659 100644 --- a/apps/emqx/src/emqx_topic_index.erl +++ b/apps/emqx/src/emqx_topic_index.erl @@ -73,7 +73,13 @@ match(K, Prefix, Words, RPrefix, Tab) -> match_rest(Matched, Words, RPrefix, Tab) end. -match_rest(false, [W | Rest], RPrefix, Tab) -> +match_rest([W1 | [W2 | _] = SLast], [W1 | [W2 | _] = Rest], RPrefix, Tab) -> + % NOTE + % Fast-forward through identical words in the topic and the last key suffixes. + % This should save us a few redundant `ets:next` calls at the cost of slightly + % more complex match patterns. + match_rest(SLast, Rest, [W1 | RPrefix], Tab); +match_rest(SLast, [W | Rest], RPrefix, Tab) when is_list(SLast) -> match(Rest, [W | RPrefix], Tab); match_rest(plus, [W | Rest], RPrefix, Tab) -> case match(Rest, ['+' | RPrefix], Tab) of @@ -115,7 +121,13 @@ matches(K, Prefix, Words, RPrefix, Acc, Tab) -> matches_rest(Matched, Words, RPrefix, Acc, Tab) end. -matches_rest(false, [W | Rest], RPrefix, Acc, Tab) -> +matches_rest([W1 | [W2 | _] = SLast], [W1 | [W2 | _] = Rest], RPrefix, Acc, Tab) -> + % NOTE + % Fast-forward through identical words in the topic and the last key suffixes. + % This should save us a few redundant `ets:next` calls at the cost of slightly + % more complex match patterns. + matches_rest(SLast, Rest, [W1 | RPrefix], Acc, Tab); +matches_rest(SLast, [W | Rest], RPrefix, Acc, Tab) when is_list(SLast) -> matches(Rest, [W | RPrefix], Acc, Tab); matches_rest(plus, [W | Rest], RPrefix, Acc, Tab) -> NAcc = matches(Rest, ['+' | RPrefix], Acc, Tab), @@ -143,8 +155,8 @@ match_filter([], ['#'], _Suffix) -> true; match_filter([], ['+' | _], _Suffix) -> plus; -match_filter([], [_H | _], _Suffix) -> - false; +match_filter([], [_H | _] = Rest, _Suffix) -> + Rest; match_filter([H | T1], [H | T2], Suffix) -> match_filter(T1, T2, Suffix); match_filter([H1 | _], [H2 | _], _Suffix) when H2 > H1 -> diff --git a/apps/emqx/test/emqx_topic_index_SUITE.erl b/apps/emqx/test/emqx_topic_index_SUITE.erl index f2b263a5a..80ca536b4 100644 --- a/apps/emqx/test/emqx_topic_index_SUITE.erl +++ b/apps/emqx/test/emqx_topic_index_SUITE.erl @@ -133,6 +133,16 @@ t_match7(_) -> emqx_topic_index:insert(W, t_match7, <<>>, Tab), ?assertEqual(W, topic(match(T, Tab))). +t_match_fast_forward(_) -> + Tab = emqx_topic_index:new(), + emqx_topic_index:insert(<<"a/b/1/2/3/4/5/6/7/8/9/#">>, id1, <<>>, Tab), + emqx_topic_index:insert(<<"z/y/x/+/+">>, id2, <<>>, Tab), + emqx_topic_index:insert(<<"a/b/c/+">>, id3, <<>>, Tab), + % dbg:tracer(), + % dbg:p(all, c), + % dbg:tpl({ets, next, '_'}, x), + ?assertEqual([id1], [id(M) || M <- matches(<<"a/b/1/2/3/4/5/6/7/8/9/0">>, Tab)]). + t_match_unique(_) -> Tab = emqx_topic_index:new(), emqx_topic_index:insert(<<"a/b/c">>, t_match_id1, <<>>, Tab),