From 55316b3ac36fd0d1a03b8a2839585520e90f3ba6 Mon Sep 17 00:00:00 2001 From: Zaiming Shi Date: Thu, 13 May 2021 16:31:13 +0200 Subject: [PATCH] perf: micro optimisation: no lookup for non-wildcard in trie --- src/emqx_trie.erl | 31 ++++++++++++++++++------------- test/emqx_trie_SUITE.erl | 4 ++-- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/emqx_trie.erl b/src/emqx_trie.erl index 901da2556..7146feb74 100644 --- a/src/emqx_trie.erl +++ b/src/emqx_trie.erl @@ -194,6 +194,11 @@ delete_key(Key) -> ok end. +%% micro-optimization: no need to lookup when topic is not wildcard +%% because we only insert wildcards to emqx_trie +lookup_topic(_Topic, false) -> []; +lookup_topic(Topic, true) -> lookup_topic(Topic). + lookup_topic(Topic) when is_binary(Topic) -> case ets:lookup(?TRIE, ?TOPIC(Topic)) of [#?TRIE{count = C}] -> [Topic || C > 0]; @@ -220,20 +225,20 @@ do_match(Words) -> do_match(Words, Prefix) -> case is_compact() of - true -> match_compact(Words, Prefix, []); - false -> match_no_compact(Words, Prefix, []) + true -> match_compact(Words, Prefix, false, []); + false -> match_no_compact(Words, Prefix, false, []) end. -match_no_compact([], Topic, Acc) -> - 'match_#'(Topic) ++ %% try match foo/bar/# - lookup_topic(Topic) ++ %% try match foo/bar +match_no_compact([], Topic, IsWildcard, Acc) -> + 'match_#'(Topic) ++ %% try match foo/+/# or foo/bar/# + lookup_topic(Topic, IsWildcard) ++ %% e.g. foo/+ Acc; -match_no_compact([Word | Words], Prefix, Acc0) -> +match_no_compact([Word | Words], Prefix, IsWildcard, Acc0) -> case has_prefix(Prefix) of true -> Acc1 = 'match_#'(Prefix) ++ Acc0, - Acc = match_no_compact(Words, join(Prefix, '+'), Acc1), - match_no_compact(Words, join(Prefix, Word), Acc); + Acc = match_no_compact(Words, join(Prefix, '+'), true, Acc1), + match_no_compact(Words, join(Prefix, Word), IsWildcard, Acc); false -> %% non-compact paths in database %% if there is no prefix matches the current topic prefix @@ -250,20 +255,20 @@ match_no_compact([Word | Words], Prefix, Acc0) -> Acc0 end. -match_compact([], Topic, Acc) -> +match_compact([], Topic, IsWildcard, Acc) -> 'match_#'(Topic) ++ %% try match foo/bar/# - lookup_topic(Topic) ++ %% try match foo/bar + lookup_topic(Topic, IsWildcard) ++ %% try match foo/bar Acc; -match_compact([Word | Words], Prefix, Acc0) -> +match_compact([Word | Words], Prefix, IsWildcard, Acc0) -> Acc1 = 'match_#'(Prefix) ++ Acc0, - Acc = match_compact(Words, join(Prefix, Word), Acc1), + Acc = match_compact(Words, join(Prefix, Word), IsWildcard, Acc1), WildcardPrefix = join(Prefix, '+'), %% go deeper to match current_prefix/+ only when: %% 1. current word is the last %% OR %% 2. there is a prefix = 'current_prefix/+' case Words =:= [] orelse has_prefix(WildcardPrefix) of - true -> match_compact(Words, WildcardPrefix, Acc); + true -> match_compact(Words, WildcardPrefix, true, Acc); false -> Acc end. diff --git a/test/emqx_trie_SUITE.erl b/test/emqx_trie_SUITE.erl index 23cd1009f..7ae23b4c6 100644 --- a/test/emqx_trie_SUITE.erl +++ b/test/emqx_trie_SUITE.erl @@ -102,11 +102,11 @@ t_match2(_) -> ?assertEqual([], ?TRIE:match(<<"$SYS/broker/zenmq">>)). t_match3(_) -> - Topics = [<<"d/#">>, <<"a/b/c">>, <<"a/b/+">>, <<"a/#">>, <<"#">>, <<"$SYS/#">>], + Topics = [<<"d/#">>, <<"a/b/+">>, <<"a/#">>, <<"#">>, <<"$SYS/#">>], trans(fun() -> [emqx_trie:insert(Topic) || Topic <- Topics] end), Matched = mnesia:async_dirty(fun emqx_trie:match/1, [<<"a/b/c">>]), case length(Matched) of - 4 -> ok; + 3 -> ok; _ -> error({unexpected, Matched}) end, SysMatched = emqx_trie:match(<<"$SYS/a/b/c">>),