Avoid traversing untouched tails in Enum.reverse_slice/3 - #15938
Merged
Merged
Conversation
Walk the prefix and selected slice directly, returning the input for zero- and one-element reversals. Reuse the untouched suffix and remove whole-list reversal and counting. Include boundary and single-enumeration tests. Assisted-by: Codex:GPT-6
Member
|
Can you please do the benchmarks also using ranges as enumerables? It may be that we need to provide a special path for lists but keep the current implementation for others. |
Contributor
Author
|
Bench ranges: # Run with: ./bin/elixir bench_ranges.exs
Mix.install([:benchee])
path = "lib/elixir/lib/enum.ex"
for {ref, module} <- [{"HEAD^", OldEnum}, {"HEAD", NewEnum}] do
{source, 0} = System.cmd("git", ["show", "#{ref}:#{path}"], cd: __DIR__)
[_, enum_code] = String.split(source, "defmodule Enum do\n", parts: 2)
[enum_code, _] = String.split(enum_code, "\nend\n\ndefimpl Enumerable", parts: 2)
code = "defmodule #{inspect(module)} do\n" <> enum_code <> "\nend\n"
Code.compile_string(code, path)
end
range_20 = 1..20
range_10k = 1..10_000
inputs = %{
"small (len 20): count=0" => {range_20, 5, 0},
"small (len 20): head slice (start 0, count 5)" => {range_20, 0, 5},
"small (len 20): middle slice (start 5, count 10)" => {range_20, 5, 10},
"small (len 20): tail slice (start 15, count 5)" => {range_20, 15, 5},
"small (len 20): full range (start 0, count 20)" => {range_20, 0, 20},
"small (len 20): start out of bounds (start 50, count 5)" => {range_20, 50, 5},
"large (len 10k): count=0" => {range_10k, 500, 0},
"large (len 10k): head small slice (start 0, count 10)" => {range_10k, 0, 10},
"large (len 10k): middle small slice (start 1000, count 50)" => {range_10k, 1000, 50},
"large (len 10k): middle large slice (start 2000, count 6000)" => {range_10k, 2000, 6000},
"large (len 10k): tail slice (start 9500, count 500)" => {range_10k, 9500, 500},
"large (len 10k): full range (start 0, count 10000)" => {range_10k, 0, 10_000},
"large (len 10k): start out of bounds (start 15000, count 100)" => {range_10k, 15_000, 100}
}
Benchee.run(
%{
"HEAD^" => fn {enum, start, count} -> OldEnum.reverse_slice(enum, start, count) end,
"HEAD" => fn {enum, start, count} -> NewEnum.reverse_slice(enum, start, count) end
},
inputs: inputs,
pre_check: :all_same,
warmup: 1,
time: 2,
memory_time: 1
)Results:
|
Member
|
💚 💙 💜 💛 ❤️ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Assisted-by: Codex:GPT-6
Assisted-by: Antigravity:Gemini 3.8 Flash
In most cases it is faster but has tradeoffs. It uses less memory.
Bench:
Results:
HEAD^) IPS / MemoryHEAD) IPS / Memory