Skip to content

vm: test int64 results against small-integer bounds first - #431

Open
shinyobjectz wants to merge 1 commit into
tv-labs:mainfrom
OpenRelationship:perf/dispatcher-smallint
Open

shinyobjectz wants to merge 1 commit into
tv-labs:mainfrom
OpenRelationship:perf/dispatcher-smallint

Conversation

@shinyobjectz

Copy link
Copy Markdown

The dispatcher's integer fast paths for +, - and * (@op_add / @op_subtract / @op_multiply and their _k forms) check every result against @min_int / @max_int before writing it, so that they can wrap to int64. Both bounds are bignums on the BEAM, so every integer add pays a bignum comparison even when the result is small.

Change

  • These six fast paths first test the result against the BEAM small-integer bounds, 2^59 - 1 and -2^59. A result inside those bounds is already in int64 range and is written as is. Any other result takes the existing check and Numeric.to_signed_int64 unchanged, so wraparound behaves exactly as before.

Tests

  • test/lua/vm/int64_wrap_engines_test.exs runs under both engines. It passes operands as function parameters so that nothing is constant-folded. It covers a + b, a - b and a * b, plus a + 1, a - 1 and a * 2, which the peephole fuses to the _k opcodes.
  • The cases cover max + 1, min - 1, max * max, min * -1, results of exactly 2^63, and both sides of 2^59. Mixed int/float operands must stay floats, checked with ===.
  • One test also asserts that all six opcodes appear in the compiled bytecode.
  • The new tests pass on main as well; they pin the behaviour this change must keep. As a check, raising the small bound above int64 made the compiled-engine cases fail.

The full suite passes: mix test gives 2875 passed, and mix test --include lua53 --include differential --include slow gives 2896 passed. One test, BootstrapTest "reset/0 ...", fails intermittently on this branch and on main alike, in about one run in five.

Benchmark

The machine is an Apple M4 with OTP 29 and Elixir 1.20.3, MIX_ENV=prod. Each number is the median of 6 interleaved before/after rounds, in ms. Each round is itself the median of 15 :timer.tc runs after 3 warmup runs. "budget" means Lua.new(max_instructions: 1_000_000_000).

program before after before (budget) after (budget)
fib(25) 30.0 26.6 38.6 35.6
1e6-iteration sum loop inside a function 61.3 55.5 81.3 74.8
the same loop at chunk top level 80.3 81.4 79.3 82.3

The top-level loop showed no measurable change.

-- fib(25)
local function fib(n) if n < 2 then return n end return fib(n - 1) + fib(n - 2) end
return fib(25)

-- loop inside a function
local function main()
  local s = 0
  for i = 1, 1000000 do s = s + i end
  return s
end
return main()

The integer fast paths of +, - and * checked every result against the
int64 bounds, which are bignums on the BEAM. They now test the BEAM
small-integer bounds (2^59 - 1, -2^59) first; a result inside them is
already in int64 range, and anything else takes the unchanged full check.

Adds int64_wrap_engines_test.exs: the register and constant forms at the
int64 and small-integer bounds, with mixed int/float, under both engines.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant