From c878586fd494ba1ca0e9ef8cb3cd9cf4023339b6 Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Tue, 30 Jan 2024 17:02:53 -0500 Subject: [PATCH 1/2] Use code_server path cache in code:where_is_file/1 `application:load/1` slows down as the number of directories in the code path increases because the call to `code:where_is_file/1` for the '.app' file must scan each directory for the app. The code_server maintains a cache of the contents of directories in the path depending on their `code:cache()` setting. Re-using and updating that cache when searching for '.app' files in `application:load/1` can improve its runtime, especially when loading multiple applications. For a reasonably large project (RabbitMQ) with a high number of code path dirs (111 at time of writing), loading an application might take upwards of 20ms. With the code_server's cache, loading each application takes at most around half of a millisecond instead. `application:load/1` is used for loading plugins in Rabbit's CLI for example, and this change can save as much as 750ms for a run of `rabbitmqctl --help` (30% of the total run time). It also improves the boot time for the broker since `application:load/1` is used by `application:start/2`. --- lib/kernel/src/code.erl | 3 +-- lib/kernel/src/code_server.erl | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/lib/kernel/src/code.erl b/lib/kernel/src/code.erl index 54082f21b608..f6b04ed19004 100644 --- a/lib/kernel/src/code.erl +++ b/lib/kernel/src/code.erl @@ -1773,8 +1773,7 @@ locate application resource files. Filename :: file:filename(), Absname :: file:filename(). where_is_file(File) when is_list(File) -> - Path = get_path(), - where_is_file(Path, File). + call({where_is_file, File}). %% To avoid unnecessary work when looking at many modules, this also %% accepts pairs of directories and pre-fetched contents in the path diff --git a/lib/kernel/src/code_server.erl b/lib/kernel/src/code_server.erl index 7689acf12468..34ff7037d15c 100644 --- a/lib/kernel/src/code_server.erl +++ b/lib/kernel/src/code_server.erl @@ -302,6 +302,11 @@ handle_call({replace_path,Name,Dir,Control}, _From, handle_call(get_path, _From, S) -> {reply,[P || {P, _Cache} <- S#state.path],S}; +handle_call({where_is_file,File}, _From, + #state{path=Path,path_cache=Cache0}=S) -> + {Resp,Cache} = where_is_file(Path, File, Cache0), + {reply,Resp,S#state{path_cache=Cache}}; + handle_call(clear_cache, _From, S) -> {reply,ok,S#state{path_cache=#{}}}; @@ -1485,3 +1490,22 @@ archive_extension() -> to_list(X) when is_list(X) -> X; to_list(X) when is_atom(X) -> atom_to_list(X). + +where_is_file([], _File, Cache) -> + {non_existing, Cache}; +where_is_file([{Dir, nocache} | Tail], File, Cache) -> + Full = filename:append(Dir, File), + case erl_prim_loader:read_file_info(Full) of + {ok,_} -> + {Full, Cache}; + _Error -> + where_is_file(Tail, File, Cache) + end; +where_is_file([{Dir, CacheKey} | Tail], File, Cache) when is_integer(CacheKey) -> + case with_cache(CacheKey, Dir, File, Cache) of + {true, Cache1} -> + Full = filename:append(Dir, File), + {Full, Cache1}; + {false, Cache1} -> + where_is_file(Tail, File, Cache1) + end. From c8687e34ab64dcbb4198dc35b20cc5e9cdcb8bcb Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Thu, 8 Aug 2024 16:14:57 -0400 Subject: [PATCH 2/2] code: Refactor which/1 in terms of where_is_file/1 Co-authored-by: Kiko Fernandez-Reyes --- lib/kernel/src/code.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/kernel/src/code.erl b/lib/kernel/src/code.erl index f6b04ed19004..dc8fc44a6a36 100644 --- a/lib/kernel/src/code.erl +++ b/lib/kernel/src/code.erl @@ -1751,7 +1751,8 @@ file containing object code for `Module` and returns the absolute filename. which(Module) when is_atom(Module) -> case is_loaded(Module) of false -> - which(Module, get_path()); + File = atom_to_list(Module) ++ objfile_extension(), + where_is_file(File); {file, File} -> File end.