Skip to content

Calculate IEx table widths with Enum.reduce_while - #15942

Merged
josevalim merged 1 commit into
elixir-lang:mainfrom
preciz:perf/iex-table-reduce-while
Sep 25, 2026
Merged

josevalim merged 1 commit into
elixir-lang:mainfrom
preciz:perf/iex-table-reduce-while

Conversation

@preciz

@preciz preciz commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

Assisted-by: Codex:GPT-6
Assisted-by: Antigravity:Gemini 3.8 Flash

Calculate the maximum length directly in print_table/2 using
Enum.reduce_while/3, eliminating the intermediate lengths list and the min() call.

Bench:

# Run with: ./bin/elixir bench.exs
Mix.install([:benchee])

defmodule Baseline do
  def print_table_offset(list) do
    lengths = Enum.map(list, &String.length(&1))
    max_length = max_length(lengths)
    min(max_length, 30) + 4
  end

  defp max_length(list) do
    Enum.reduce(list, 0, &max(&1, &2))
  end
end

defmodule Proposed do
  def print_table_offset(list) do
    max_length =
      Enum.reduce_while(list, 0, fn item, acc ->
        case String.length(item) do
          len when len >= 30 -> {:halt, 30}
          len -> {:cont, max(acc, len)}
        end
      end)

    max_length + 4
  end
end

inputs = %{
  "50 items with long first item" => ["a_very_long_file_name_that_exceeds_thirty_chars.ex" | for(i <- 1..49, do: "file_#{i}.ex")],
  "50 short items (< 30 chars)" => for(i <- 1..50, do: "file_#{i}.ex")
}

Benchee.run(
  %{
    "main" => &Baseline.print_table_offset/1,
    "HEAD" => &Proposed.print_table_offset/1
  },
  inputs: inputs,
  time: 2,
  warmup: 1,
  memory_time: 1
)

Results:

Operating System: Linux
CPU Information: AMD Ryzen 7 8845HS w
Number of Available Cores: 16
Available memory: 54.72 GB
Elixir 1.21.0-dev
Erlang 29.0.5
JIT enabled: true

##### With input 50 items with long first item #####
Name           ips        average  deviation         median         99th %
HEAD        6.98 M       0.143 μs  ±1549.47%       0.130 μs        0.26 μs
main        0.22 M        4.62 μs    ±45.21%        4.42 μs        7.13 μs

Comparison: 
HEAD        6.98 M
main        0.22 M - 32.24x slower +4.47 μs

Memory usage statistics:

Name    Memory usage
HEAD         0.42 KB
main        17.62 KB - 41.76x memory usage +17.20 KB

**All measurements for memory usage were the same**

##### With input 50 short items (< 30 chars) #####
Name           ips        average  deviation         median         99th %
HEAD      222.30 K        4.50 μs   ±140.43%        4.31 μs        6.61 μs
main      217.90 K        4.59 μs   ±152.96%        4.43 μs        7.00 μs

Comparison: 
HEAD      222.30 K
main      217.90 K - 1.02x slower +0.0908 μs

Memory usage statistics:

Name    Memory usage
HEAD        18.02 KB
main        17.58 KB - 0.98x memory usage -0.43750 KB

**All measurements for memory usage were the same**

Calculate the maximum grapheme length directly in print_table/2 using Enum.reduce_while/3, eliminating the intermediate lengths list and the single-use max_length/1 helper while halting traversal early as soon as a length reaches the 30-character column clamp.

Assisted-by: Codex:GPT-6
Assisted-by: Antigravity:Gemini 3.8 Flash
@josevalim
josevalim merged commit 787dad4 into elixir-lang:main Sep 25, 2026
12 of 15 checks passed
@josevalim

Copy link
Copy Markdown
Member

💚 💙 💜 💛 ❤️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants