perf: micro optimisation: no lookup for non-wildcard in trie

This commit is contained in:
Zaiming Shi 2021-05-13 16:31:13 +02:00
parent 3c03047c9f
commit 55316b3ac3
2 changed files with 20 additions and 15 deletions

View File

@ -194,6 +194,11 @@ delete_key(Key) ->
ok ok
end. 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) -> lookup_topic(Topic) when is_binary(Topic) ->
case ets:lookup(?TRIE, ?TOPIC(Topic)) of case ets:lookup(?TRIE, ?TOPIC(Topic)) of
[#?TRIE{count = C}] -> [Topic || C > 0]; [#?TRIE{count = C}] -> [Topic || C > 0];
@ -220,20 +225,20 @@ do_match(Words) ->
do_match(Words, Prefix) -> do_match(Words, Prefix) ->
case is_compact() of case is_compact() of
true -> match_compact(Words, Prefix, []); true -> match_compact(Words, Prefix, false, []);
false -> match_no_compact(Words, Prefix, []) false -> match_no_compact(Words, Prefix, false, [])
end. end.
match_no_compact([], Topic, Acc) -> match_no_compact([], Topic, IsWildcard, Acc) ->
'match_#'(Topic) ++ %% try match foo/bar/# 'match_#'(Topic) ++ %% try match foo/+/# or foo/bar/#
lookup_topic(Topic) ++ %% try match foo/bar lookup_topic(Topic, IsWildcard) ++ %% e.g. foo/+
Acc; Acc;
match_no_compact([Word | Words], Prefix, Acc0) -> match_no_compact([Word | Words], Prefix, IsWildcard, Acc0) ->
case has_prefix(Prefix) of case has_prefix(Prefix) of
true -> true ->
Acc1 = 'match_#'(Prefix) ++ Acc0, Acc1 = 'match_#'(Prefix) ++ Acc0,
Acc = match_no_compact(Words, join(Prefix, '+'), Acc1), Acc = match_no_compact(Words, join(Prefix, '+'), true, Acc1),
match_no_compact(Words, join(Prefix, Word), Acc); match_no_compact(Words, join(Prefix, Word), IsWildcard, Acc);
false -> false ->
%% non-compact paths in database %% non-compact paths in database
%% if there is no prefix matches the current topic prefix %% if there is no prefix matches the current topic prefix
@ -250,20 +255,20 @@ match_no_compact([Word | Words], Prefix, Acc0) ->
Acc0 Acc0
end. end.
match_compact([], Topic, Acc) -> match_compact([], Topic, IsWildcard, Acc) ->
'match_#'(Topic) ++ %% try match foo/bar/# 'match_#'(Topic) ++ %% try match foo/bar/#
lookup_topic(Topic) ++ %% try match foo/bar lookup_topic(Topic, IsWildcard) ++ %% try match foo/bar
Acc; Acc;
match_compact([Word | Words], Prefix, Acc0) -> match_compact([Word | Words], Prefix, IsWildcard, Acc0) ->
Acc1 = 'match_#'(Prefix) ++ 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, '+'), WildcardPrefix = join(Prefix, '+'),
%% go deeper to match current_prefix/+ only when: %% go deeper to match current_prefix/+ only when:
%% 1. current word is the last %% 1. current word is the last
%% OR %% OR
%% 2. there is a prefix = 'current_prefix/+' %% 2. there is a prefix = 'current_prefix/+'
case Words =:= [] orelse has_prefix(WildcardPrefix) of case Words =:= [] orelse has_prefix(WildcardPrefix) of
true -> match_compact(Words, WildcardPrefix, Acc); true -> match_compact(Words, WildcardPrefix, true, Acc);
false -> Acc false -> Acc
end. end.

View File

@ -102,11 +102,11 @@ t_match2(_) ->
?assertEqual([], ?TRIE:match(<<"$SYS/broker/zenmq">>)). ?assertEqual([], ?TRIE:match(<<"$SYS/broker/zenmq">>)).
t_match3(_) -> 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), trans(fun() -> [emqx_trie:insert(Topic) || Topic <- Topics] end),
Matched = mnesia:async_dirty(fun emqx_trie:match/1, [<<"a/b/c">>]), Matched = mnesia:async_dirty(fun emqx_trie:match/1, [<<"a/b/c">>]),
case length(Matched) of case length(Matched) of
4 -> ok; 3 -> ok;
_ -> error({unexpected, Matched}) _ -> error({unexpected, Matched})
end, end,
SysMatched = emqx_trie:match(<<"$SYS/a/b/c">>), SysMatched = emqx_trie:match(<<"$SYS/a/b/c">>),