-
-
Notifications
You must be signed in to change notification settings - Fork 83
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Optimise the manual loading of dependent apps and modules. #455
Conversation
I'll have to wait for @scohen for a more definitive answer, but I thought this is there for struct discovery, which happens in the compilation tracer. Or is this redundant and all that compilation happens anyways? |
I’m pretty sure that it’s there so elixir sense can see the modules. You can’t see the modules unless they’re loaded. |
but I thought this is there for struct discovery, which happens in the compilation tracer. yes, happens in the compilation tracer.
I think so. |
@scottming there's definitely a reason that this is here, I didn't just plop that code in there for fun. |
This might have been carried over from the old packaging, I believe releases are started in a mode where all modules need to be explicitly loaded at app start, while the current mode doesn't need that. |
I acknowledge that the loading time will be amortized when used. However, when we let Elixir automatically load a small number of modules during usage, users won't perceive this impact. For example, the module So, I am more inclined to do this at runtime. Manually loading iex(1)> Code.loaded?(Jason.Encode)
false
iex(2)> :timer.tc(fn -> Code.ensure_loaded(Jason.Encode) end)
{3766, {:module, Jason.Encode}}
iex(3)>
Automatically Landing iex(1)> Code.loaded?(Jason.Encode)
false
iex(2)> Jason.encode(%{})
{:ok, "{}"}
iex(3)> Code.loaded?(Jason.Encode)
true |
8c1e60a
to
f9a3152
Compare
part of the contract of the call to boot is that when it's done, the app is ready to use. Making things async breaks this contract. |
Today I tried to pinpoint the real cause of the autocompletion failure. I discovered that But do you know why? @scohen |
It's likely because it can't find the protocol modules can't be found because they're not loaded. I'm not sure why this is happening, but it worries me that it is, and I wonder what other issues we're not finding. This change seems very risky to me. |
My 2 cents: It's better to amortize load time during usage instead of up-front. Everyone will have their own preference, but I think that, as a general rule, time-to-things-working should be kept as low as possible. I don't love the idea of special-casing |
I mostly agree with your point. However, loading all the modules starting with I have been using this branch for two days now, and I haven't encountered any other issues so far. I speculate that manually loading all the modules starting with |
I explored this further, and it may be a problem with {
"label":"print_hello(s)",
"kind":3,
"sortText":"094_print_hello(s)",
"textEdit":{
"range":{
"start":{
"line":6,
"character":10
},
"end":{
"line":6,
"character":10
}
},
"newText":"print_hello(${1:s})"
},
"insertTextFormat":"snippet" # this line
} |
Zach was suggesting loading modules starting with |
The problem must be elsewhere, this seems to work correctly:
|
@scottming I think this points to a bigger problem with this PR. Protocols are another thing that i believe need to be loaded before we have a functional app. ...this PR continues to worry me. |
Your guess is correct. Thank you for this guidance. I think I have identified the root cause of the problem. In these lines, when the module is not loaded, it falls back to the previous value. A module will be automatically loaded when used, but these lines lexical/apps/proto/lib/lexical/proto/field.ex Lines 277 to 280 in ba21277
prevent the module from being used, resulting in this strange issue.
|
a9d0979
to
4dd9f6c
Compare
I'm R- on this PR. It's too dangerous. I looked for |
@scohen I understand your concern. If we really must use Since |
The app can start, but the modules need to be loaded before it's initialized |
You have no idea if this is true or not. It might be true today (and i'm not sure it is), but it's possible that it might not be true tomorrow. This is why this change is so risky. |
4dd9f6c
to
630a3d0
Compare
I still prefer to initialize as fast as possible. The changes in the last two commits should have very low risk. However, if you still perceive some risk, let's keep only the commit with |
630a3d0
to
6a76380
Compare
6fb50b4
to
db2865c
Compare
`Application.spec` instead of `:application.get_key` and use `Code.ensure_all_loaded!` instead of `Code.ensure_loaded!`, The `Code.ensure_all_loaded!` will be approximately 1.5 seconds faster compared to the previous approach.
and adjust the execution of `load_all_modules` Considering that our initialization depends on few modules, I believe it is safe to adjust the order of `load_all_modules` when already in the field module `ensure_loaded_module`.
db2865c
to
2b4649f
Compare
…ed/2`" This reverts commit 2b4649f.
My suggestion is to switch back to the erlang functions and use ensure_modules_loaded and see if that's faster. It might be, since each call to Code in the previous each function requires a trip in and out of the code server. It might also be faster to collect all the modules that need to be loaded from each application and load them all once |
The test results indicate that individually loading all modules for each app is faster compared to loading all modules of all apps at once. each app loading Application.ensure_loaded(app_name)
modules = Application.spec(app_name, :modules)
Code.ensure_all_loaded!(modules) results: ~/Code/lexical @3a2c5e68 !1 ❯ ./_build/dev/package/lexical/bin/start_lexical.sh 5s 25.3.2.7 1.15.7-otp-25
Detected Elixir through asdf: /Users/scottming/.asdf/installs/elixir/1.15.7-otp-25/bin/elixir
[(server 0.3.0) lib/lexical/server/boot.ex:26: Lexical.Server.Boot.start/0]
time #=> 613491
^C
BREAK: (a)bort (A)bort with dump (c)ontinue (p)roc info (i)nfo
(l)oaded (v)ersion (k)ill (D)b-tables (d)istribution
^C%
~/Code/lexical @3a2c5e68 !1 ❯ ./_build/dev/package/lexical/bin/start_lexical.sh 25.3.2.7 1.15.7-otp-25
Detected Elixir through asdf: /Users/scottming/.asdf/installs/elixir/1.15.7-otp-25/bin/elixir
[(server 0.3.0) lib/lexical/server/boot.ex:26: Lexical.Server.Boot.start/0]
time #=> 630133
^C
BREAK: (a)bort (A)bort with dump (c)ontinue (p)roc info (i)nfo
(l)oaded (v)ersion (k)ill (D)b-tables (d)istribution
^C%
~/Code/lexical @3a2c5e68 !1 ❯ ./_build/dev/package/lexical/bin/start_lexical.sh 25.3.2.7 1.15.7-otp-25
Detected Elixir through asdf: /Users/scottming/.asdf/installs/elixir/1.15.7-otp-25/bin/elixir
[(server 0.3.0) lib/lexical/server/boot.ex:26: Lexical.Server.Boot.start/0]
time #=> 613521 load all at once modules =
Enum.flat_map(app_names, fn app_name ->
Application.ensure_loaded(app_name)
Application.spec(app_name, :modules)
end)
Code.ensure_all_loaded!(modules) results ~/Code/lexical cancel-manually-load-deps ⇡1 !1 ❯ ./_build/dev/package/lexical/bin/start_lexical.sh
Detected Elixir through asdf: /Users/scottming/.asdf/installs/elixir/1.15.7-otp-25/bin/elixir
[(server 0.3.0) lib/lexical/server/boot.ex:26: Lexical.Server.Boot.start/0]
time #=> 658426
^C
BREAK: (a)bort (A)bort with dump (c)ontinue (p)roc info (i)nfo
(l)oaded (v)ersion (k)ill (D)b-tables (d)istribution
^C%
~/Code/lexical cancel-manually-load-deps ⇡1 !1 ❯ ./_build/dev/package/lexical/bin/start_lexical.sh 25.3.2.7 1.15.7-otp-25
Detected Elixir through asdf: /Users/scottming/.asdf/installs/elixir/1.15.7-otp-25/bin/elixir
[(server 0.3.0) lib/lexical/server/boot.ex:26: Lexical.Server.Boot.start/0]
time #=> 655624
^C
BREAK: (a)bort (A)bort with dump (c)ontinue (p)roc info (i)nfo
(l)oaded (v)ersion (k)ill (D)b-tables (d)istribution
^C%
~/Code/lexical cancel-manually-load-deps ⇡1 !1 ❯ ./_build/dev/package/lexical/bin/start_lexical.sh 25.3.2.7 1.15.7-otp-25
Detected Elixir through asdf: /Users/scottming/.asdf/installs/elixir/1.15.7-otp-25/bin/elixir
[(server 0.3.0) lib/lexical/server/boot.ex:26: Lexical.Server.Boot.start/0]
time #=> 656506
^C
BREAK: (a)bort (A)bort with dump (c)ontinue (p)roc info (i)nfo
(l)oaded (v)ersion (k)ill (D)b-tables (d)istribution
^C% |
I have always felt that the startup of 'lexical' is very slow. After some investigation, I found that this is mainly because we manually load all dependent modules, which takes about
2.4
seconds.After removing this line:
Enum.each(modules, &Code.ensure_loaded!/1)
, I found that 'lexical' works perfectly fine. Additionally, when we start the:server
app,it will automatically load and start the dependent apps, so we don't need to manually load the dependent apps either.before
CleanShot.2023-10-23.at.22.52.56.mp4
After
CleanShot.2023-10-28.at.10.33.35.mp4