Skip to content

Optimize integer division and remainder operations - #15981

Closed
preciz wants to merge 1 commit into
elixir-lang:mainfrom
preciz:perf/rem
Closed

preciz wants to merge 1 commit into
elixir-lang:mainfrom
preciz:perf/rem

Conversation

@preciz

@preciz preciz commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

Assisted-by: Antigravity:Gemini 3.8 Flash

If after div/2 we call rem/2 then the compiler fuses it to one operation.

https://github.com/erlang/otp/blob/master/erts/emulator/beam/jit/x86/ops.tab#L1190-L1213

These changes have been evaluated locally to produce fewer assembly lines and that they run faster.

Bench:

Mix.install([:benchee])

defmodule Bench do
  def old(n, m) do
    div = div(n, m)
    n - div * m
  end

  def new(n, m) do
    _ = div(n, m)
    rem(n, m)
  end
end

Benchee.run(
  %{
    "old" => fn -> Bench.old(10, 3) end,
    "new" => fn -> Bench.new(10, 3) end
  },
  warmup: 1,
  time: 3
)

Evaluate division and remainder operations together to let the runtime fuse them into a single instruction. Replace manual remainder calculations with rem/2 to remove extra multiplication and subtraction instructions.

Assisted-by: Antigravity:Gemini 3.8 Flash
@preciz

preciz commented Oct 11, 2026

Copy link
Copy Markdown
Contributor Author

Assembly lines:

Module & Function Before (HEAD~1) After (HEAD) Net Change
Calendar.ISO.div_rem/2 166 lines
div=1, rem=0
mul=1, sub=2
110 lines
div_rem=1
mul=0, sub=1
-56 lines
Calendar.ISO.offset_to_iodata/4 139 lines
div=2, rem=1
125 lines
div_rem=1, div=1
-14 lines
NaiveDateTime.div_rem/2 166 lines
div=1, rem=0
mul=1, sub=2
110 lines
div_rem=1
mul=0, sub=1
-56 lines
Enumerable.File.Stream.count/1 552 lines
div=1, rem=1
542 lines
div_rem=1, div=0
-10 lines
Float.bignum_to_float/4 360 lines
div=1, rem=1
351 lines
div_rem=1, div=0
-9 lines
Integer.floor_div/2 131 lines
div=2, rem=1
112 lines
div_rem=1, div=0
-19 lines
Integer.ceil_div/2 133 lines
div=2, rem=1
114 lines
div_rem=1, div=0
-19 lines
Integer.extended_gcd/6 299 lines
div=1, rem=0
mul=3, sub=3
261 lines
div_rem=1
mul=2, sub=2
-38 lines
String.build_filler/5 355 lines
div=1, rem=1
340 lines
div_rem=1, div=0
-15 lines

@josevalim

Copy link
Copy Markdown
Member

Thanks but chasing this level of optimization is unlikely to be worth our time investment in most cases!

@josevalim josevalim closed this Oct 11, 2026
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