From 214eb801b6be94f3b3313d490d78fea4202bc0b2 Mon Sep 17 00:00:00 2001 From: Wojtek Mach Date: Fri, 23 Oct 2026 11:30:03 +0200 Subject: [PATCH 1/3] Find the script directory without subprocesses in bin/elixir MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OTP master: $ hyperfine -N -w 10 -r 200 \ -n "main" "/usr/bin/env PATH=$HOME/src/elixir-ex40-base/bin:$HOME/src/otp-ex40-master/bin:$PATH elixir -e 'IO.puts :hello'" \ -n "main + wm-ex40-script" "/usr/bin/env PATH=$HOME/src/elixir-ex40-script/bin:$HOME/src/otp-ex40-master/bin:$PATH elixir -e 'IO.puts :hello'" Benchmark 1: main (200 runs) mean ± σ min … max Wall Time 81.4 ms ± 1.5 ms 78.3 ms … 85.6 ms Memory 79.8 MiB ± 0.4 MiB 79.0 MiB … 81.0 MiB Benchmark 2: main + wm-ex40-script (200 runs) mean ± σ min … max Wall Time 76.6 ms ± 1.5 ms 74.0 ms … 81.3 ms -5.9% Memory 79.9 MiB ± 0.4 MiB 79.0 MiB … 80.9 MiB +0.1% OTP master + https://github.com/erlang/otp/pull/11814: $ hyperfine -N -w 10 -r 200 \ -n "main" "/usr/bin/env PATH=$HOME/src/elixir-ex40-base/bin:$HOME/src/otp-ex40-base/bin:$PATH elixir -e 'IO.puts :hello'" \ -n "main + wm-ex40-script" "/usr/bin/env PATH=$HOME/src/elixir-ex40-script/bin:$HOME/src/otp-ex40-base/bin:$PATH elixir -e 'IO.puts :hello'" Benchmark 1: main (200 runs) mean ± σ min … max Wall Time 69.6 ms ± 1.6 ms 66.6 ms … 77.9 ms Memory 79.8 MiB ± 0.4 MiB 78.8 MiB … 81.2 MiB Benchmark 2: main + wm-ex40-script (200 runs) mean ± σ min … max Wall Time 64.9 ms ± 1.4 ms 62.0 ms … 68.7 ms -6.6% Memory 79.9 MiB ± 0.4 MiB 79.0 MiB … 80.9 MiB 0.0% --- bin/elixir | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/bin/elixir b/bin/elixir index 86a30bb4295..ad3557bfb29 100755 --- a/bin/elixir +++ b/bin/elixir @@ -215,8 +215,13 @@ while [ $I -ge 0 ]; do I=$((I - 1)) done -SELF=$(readlink_f "$0") -SCRIPT_PATH=$(dirname "$SELF") +# Use $0 directly when it is an absolute path that is not a symbolic link +if [ "${0#/}" != "$0" ] && [ ! -h "$0" ]; then + SELF="$0" +else + SELF=$(readlink_f "$0") +fi +SCRIPT_PATH="${SELF%/*}" if [ "$OSTYPE" = "cygwin" ]; then SCRIPT_PATH=$(cygpath -m "$SCRIPT_PATH"); fi if [ "$MODE" != "iex" ]; then ERL="-s elixir start_cli $ERL"; fi From e86159c38577beaf65bb7f9ccd6ed8d2906659ca Mon Sep 17 00:00:00 2001 From: Wojtek Mach Date: Fri, 23 Oct 2026 11:30:03 +0200 Subject: [PATCH 2/3] Load the CLI modules in parallel at start MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OTP master: $ hyperfine -N -w 10 -r 200 \ -n "main" "/usr/bin/env PATH=$HOME/src/elixir-ex40-base/bin:$HOME/src/otp-ex40-master/bin:$PATH elixir -e 'IO.puts :hello'" \ -n "main + wm-ex40-preload" "/usr/bin/env PATH=$HOME/src/elixir-ex40-preload/bin:$HOME/src/otp-ex40-master/bin:$PATH elixir -e 'IO.puts :hello'" Benchmark 1: main (200 runs) mean ± σ min … max Wall Time 81.3 ms ± 1.6 ms 78.0 ms … 87.6 ms Memory 79.8 MiB ± 0.4 MiB 78.9 MiB … 81.0 MiB Benchmark 2: main + wm-ex40-preload (200 runs) mean ± σ min … max Wall Time 77.5 ms ± 3.0 ms 70.4 ms … 98.8 ms -4.7% Memory 81.3 MiB ± 0.4 MiB 80.5 MiB … 82.5 MiB +1.9% OTP master + https://github.com/erlang/otp/pull/11814: $ hyperfine -N -w 10 -r 200 \ -n "main" "/usr/bin/env PATH=$HOME/src/elixir-ex40-base/bin:$HOME/src/otp-ex40-base/bin:$PATH elixir -e 'IO.puts :hello'" \ -n "main + wm-ex40-preload" "/usr/bin/env PATH=$HOME/src/elixir-ex40-preload/bin:$HOME/src/otp-ex40-base/bin:$PATH elixir -e 'IO.puts :hello'" Benchmark 1: main (200 runs) mean ± σ min … max Wall Time 69.3 ms ± 1.7 ms 66.4 ms … 74.3 ms Memory 79.8 MiB ± 0.4 MiB 78.9 MiB … 81.2 MiB Benchmark 2: main + wm-ex40-preload (200 runs) mean ± σ min … max Wall Time 66.5 ms ± 3.4 ms 60.2 ms … 91.0 ms -4.0% Memory 81.2 MiB ± 0.4 MiB 80.0 MiB … 82.5 MiB +1.8% --- lib/elixir/src/elixir.erl | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lib/elixir/src/elixir.erl b/lib/elixir/src/elixir.erl index 8e79148c14f..331258a4007 100644 --- a/lib/elixir/src/elixir.erl +++ b/lib/elixir/src/elixir.erl @@ -16,6 +16,20 @@ -include("elixir.hrl"). -define(system, 'Elixir.System'). -define(elixir_eval_env, {elixir, eval_env}). +-define(cli_modules, [ + 'Elixir.Access', 'Elixir.Application', 'Elixir.Code', 'Elixir.Enum', 'Elixir.IO', + 'Elixir.Kernel', 'Elixir.Kernel.CLI', 'Elixir.Keyword', 'Elixir.List.Chars', + 'Elixir.List.Chars.List', 'Elixir.Macro.Env', 'Elixir.Map', 'Elixir.Module.ParallelChecker', + 'Elixir.Regex', 'Elixir.String.Chars', 'Elixir.String.Tokenizer', 'Elixir.Supervisor', + 'Elixir.Supervisor.Default', 'Elixir.System', elixir_aliases, elixir_bitstring, + elixir_code_server, elixir_config, elixir_dispatch, elixir_env, elixir_erl, elixir_erl_pass, + elixir_erl_var, elixir_expand, elixir_interpolation, elixir_module, elixir_parser, + elixir_quote, elixir_rewrite, elixir_sup, elixir_tokenizer, elixir_utils, + erl_bits, eval_bits, logger_backend, re +]). +-define(cli_logger_modules, [ + 'Elixir.Logger', 'Elixir.Logger.App', 'Elixir.Logger.Formatter', 'Elixir.Logger.Utils' +]). %% Top level types %% TODO: Remove char_list type on v2.0 @@ -180,7 +194,9 @@ start() -> user_drv:start(#{initial_shell => iex:shell()}). start_cli() -> + _ = code:ensure_modules_loaded(?cli_modules), {ok, _} = application:ensure_all_started(elixir), + _ = code:ensure_modules_loaded(?cli_logger_modules), %% We start the Logger so tools that depend on Elixir %% always have the Logger directly accessible. However From e30723937686b700d09ebfa5e58e8100fee0bd4b Mon Sep 17 00:00:00 2001 From: Wojtek Mach Date: Fri, 23 Oct 2026 11:48:41 +0200 Subject: [PATCH 3/3] Sort the modules of a remote call in the type checker The previous commit loads the CLI modules before the elixir application starts. This changes the atom order. Then Module.Types.ExprTest fails, because it expects List before String. --- lib/elixir/lib/module/types/of.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/elixir/lib/module/types/of.ex b/lib/elixir/lib/module/types/of.ex index 44599256cf0..e5ad220e7af 100644 --- a/lib/elixir/lib/module/types/of.ex +++ b/lib/elixir/lib/module/types/of.ex @@ -734,7 +734,7 @@ defmodule Module.Types.Of do def modules(type, fun, arity, hints \\ [], expr, meta, stack, context) do case atom_fetch(type) do {:finite, mods} -> - {mods, context} + {:lists.sort(mods), context} {:infinite, _} -> {[], context}