From a1fc8075f3e86ec2242eedd2b1bbbd15758515e7 Mon Sep 17 00:00:00 2001 From: Nick Vatamaniuc Date: Tue, 7 Jun 2022 10:30:03 -0400 Subject: [PATCH] Optimize couch_util:reorder_results/2,3 This function is used in the hot path of _revs_diff and _bulk_docs API calls. Those could always use a bit more optimization: * In `_revs_diff` it's used when fetching all the FDIs to see which docs are missing in `couch_btree:lookup/2`. * In `_bulk_docs` it's used in the `fabric_doc_update` when finalizing the response. Using erlperf in #4051 noticed an at most 5x speedup from using a map instead of a dict. Since a map already falls back to a proplist for small sizes, skip the length guard. Some erlperf examples from #4051: 500 Keys ``` > f(Keys), f(Res), {Keys, Res} = Gen(500), ok. > erlperf:run(#{runner => {couch_util, reorder_results2, [Keys, Res, 100, dict]}}). 2407 > erlperf:run(#{runner => {couch_util, reorder_results2, [Keys, Res, 100, map]}}). 11639 ``` Using a map without the guard, which is the change in this this PR: ``` > f(Keys), f(Res), {Keys, Res} = Gen(500), ok. ok > erlperf:run(#{runner => {couch_util, reorder_results, [Keys, Res]}}). 12395 > erlperf:run(#{runner => {couch_util, reorder_results, [Keys, Res]}}). 12508 ``` As a bonus this also cleans up the code a bit, too. --- src/couch/src/couch_util.erl | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/src/couch/src/couch_util.erl b/src/couch/src/couch_util.erl index e03a50e1d50..912c6dd8a30 100644 --- a/src/couch/src/couch_util.erl +++ b/src/couch/src/couch_util.erl @@ -523,24 +523,13 @@ verify(X, Y) when is_list(X) and is_list(Y) -> verify(_X, _Y) -> false. -% linear search is faster for small lists, length() is 0.5 ms for 100k list -reorder_results(Keys, SortedResults) when length(Keys) < 100 -> - [couch_util:get_value(Key, SortedResults) || Key <- Keys]; reorder_results(Keys, SortedResults) -> - KeyDict = dict:from_list(SortedResults), - [dict:fetch(Key, KeyDict) || Key <- Keys]. + Map = maps:from_list(SortedResults), + [maps:get(Key, Map) || Key <- Keys]. -reorder_results(Keys, SortedResults, Default) when length(Keys) < 100 -> - [couch_util:get_value(Key, SortedResults, Default) || Key <- Keys]; reorder_results(Keys, SortedResults, Default) -> - KeyDict = dict:from_list(SortedResults), - DefaultFunc = fun({Key, Dict}) -> - case dict:is_key(Key, Dict) of - true -> dict:fetch(Key, Dict); - false -> Default - end - end, - [DefaultFunc({Key, KeyDict}) || Key <- Keys]. + Map = maps:from_list(SortedResults), + [maps:get(Key, Map, Default) || Key <- Keys]. url_strip_password(Url) -> re:replace(