XPerf
LanguagesElixir

BEAM performance guide (v0) — Elixir & Erlang

Less quadratic list growth, fewer binary rebuilds, quieter hot loops.

Scope: Elixir 1.14+ and Erlang/OTP 25+ on BEAM. One kit covers both languages. Speedup claims cite a local bench, an upstream source, or are marked provisional. Corpus residual counts are lint hit counts, not wall-time savings.

Profile first (:timer.tc, fprof, eprof, Benchee). Extensions scanned: .ex, .exs, .erl.

Contents

1. Lists

list ++ [x] (Elixir) or List ++ [X] (Erlang) in a loop is quadratic. Prefer [x | acc] then Enum.reverse/1 / lists:reverse/1, or use proper accumulators.

Map.merge/2 or Keyword.merge/2 on every element of a reduce was measured slower than Map.put/3 (see evidence-policy.md). Use Map.put/3, or merge outside only when the other map stays the same. Chained Enum.map/2 then Enum.filter/2 builds an intermediate list. On the measured chain of about 200000 integers that was about 4.5×; a short list is a different cost.

Lint: be-list-concat-reduce, be-map-merge-hot, be-enum-map-filter-chain (all default hint).

2. Binaries / strings and hot I/O

a <> b (Elixir) or repeated binary append in a loop rebuilds binaries. Prefer iolists / IO.iodata_to_binary/1. No lint rule covers this smell (its former rule, BE002, was retired). Keep IO.puts / :io.format / io:format out of tight loops.

Lint: be-io-puts-hot (default off).

Lint map

IDSmellDefault
be-list-concat-reducelist ++ in loophint
be-map-merge-hotMap/Keyword.merge in loophint
be-enum-map-filter-chainchained Enum.map/filterhint
be-io-puts-hotIO.puts / io:format in loopoff

Escalate only with evidence/be-*.jsonl — see evidence-policy.md.

References