Store ranges as tuples in Sequence :queue

Found a memory savings for `Indexer.Sequence`: store Ranges as tuples in
the `:queue` as the tuples take up less space than than the `%Range{}`
struct, but only after garbage-collection.  The incoming and outgoing
format is still Ranges, so this is a non-breaking implementation detail
only.

```elixir
  test "memory usage" do
    {:ok, pid} = Sequence.start_link(ranges: [max_block_number..0],
step: -10)

    Process.info(pid, :memory) |> IO.inspect(label: "BEFORE GC")

    :erlang.garbage_collect(pid)

    Process.info(pid, :memory) |> IO.inspect(label: "AFTER GC")
  end
```

| max_block_number | Garbage Collection | Ranges (bytes) | Tuples (bytes) | Tuples / Ranges (%) |
|------------------|--------------------|----------------|----------------|---------------------|
| 5,000,000        | Before             | 41,050,964     | 22,445,660     | 54.68               |
| 5,000,000        | After              | 34,386,756     | 23,879,996     | 69.45               |
| 10,000,000       | Before             | 80,889,772     | 47,928,116     | 59.24               |
| 10,000,000       | After              | 71,303,316     | 49,516,492     | 69.44               |
pull/917/head
Luke Imhoff 6 years ago
parent 9570295f5c
commit 753319de4a
  1. 10
      apps/indexer/lib/indexer/sequence.ex

@ -182,8 +182,8 @@ defmodule Indexer.Sequence do
def handle_call(:pop, _from, %__MODULE__{queue: queue, current: current, step: step} = state) do def handle_call(:pop, _from, %__MODULE__{queue: queue, current: current, step: step} = state) do
{reply, new_state} = {reply, new_state} =
case {current, :queue.out(queue)} do case {current, :queue.out(queue)} do
{_, {{:value, range}, new_queue}} -> {_, {{:value, {first, last}}, new_queue}} ->
{range, %__MODULE__{state | queue: new_queue}} {first..last, %__MODULE__{state | queue: new_queue}}
{nil, {:empty, new_queue}} -> {nil, {:empty, new_queue}} ->
{:halt, %__MODULE__{state | queue: new_queue}} {:halt, %__MODULE__{state | queue: new_queue}}
@ -251,8 +251,8 @@ defmodule Indexer.Sequence do
{:error, "Range (#{inspect(range)}) direction is opposite step (#{step}) direction"} {:error, "Range (#{inspect(range)}) direction is opposite step (#{step}) direction"}
end end
defp reduce_chunked_range(_.._ = range, count, step, initial, reducer) when count <= abs(step) do defp reduce_chunked_range(first..last, count, step, initial, reducer) when count <= abs(step) do
{:ok, reducer.(range, initial)} {:ok, reducer.({first, last}, initial)}
end end
defp reduce_chunked_range(first..last, _, step, initial, reducer) do defp reduce_chunked_range(first..last, _, step, initial, reducer) do
@ -277,7 +277,7 @@ defmodule Indexer.Sequence do
{:cont, full_chunk_last} {:cont, full_chunk_last}
end end
{action, reducer.(chunk_first..chunk_last, acc)} {action, reducer.({chunk_first, chunk_last}, acc)}
end) end)
{:ok, final} {:ok, final}

Loading…
Cancel
Save