From ae1194ebb09b2b5c5d8da5bdb659b2bf9201c108 Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Mon, 1 Jun 2026 16:48:48 -0500 Subject: [PATCH] perf: fix PacketBatcher O(n) length/1 to O(1) counter Ultraworked with Sisyphus Co-authored-by: Sisyphus --- .../live/map_live/packet_batcher.ex | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/lib/aprsme_web/live/map_live/packet_batcher.ex b/lib/aprsme_web/live/map_live/packet_batcher.ex index 7ee45fa..e99a200 100644 --- a/lib/aprsme_web/live/map_live/packet_batcher.ex +++ b/lib/aprsme_web/live/map_live/packet_batcher.ex @@ -10,11 +10,12 @@ defmodule AprsmeWeb.MapLive.PacketBatcher do # milliseconds @batch_timeout 100 - defstruct [:parent_pid, :buffer, :timer_ref] + defstruct [:parent_pid, :buffer, :timer_ref, :buffer_size] @type t :: %__MODULE__{ parent_pid: pid(), buffer: [map()], + buffer_size: non_neg_integer(), timer_ref: reference() | nil } @@ -50,27 +51,28 @@ defmodule AprsmeWeb.MapLive.PacketBatcher do %__MODULE__{ parent_pid: parent_pid, buffer: [], + buffer_size: 0, timer_ref: nil }} end @impl true def handle_cast({:add_packet, packet}, state) do - # Add packet to buffer new_buffer = [packet | state.buffer] + new_size = state.buffer_size + 1 # Cancel existing timer if any state = cancel_timer(state) # Check if we should process immediately or wait - if length(new_buffer) >= @batch_size do + if new_size >= @batch_size do # Process immediately process_batch(new_buffer, state.parent_pid) - {:noreply, %{state | buffer: [], timer_ref: nil}} + {:noreply, %{state | buffer: [], buffer_size: 0, timer_ref: nil}} else # Set timer for batch timeout timer_ref = Process.send_after(self(), :batch_timeout, @batch_timeout) - {:noreply, %{state | buffer: new_buffer, timer_ref: timer_ref}} + {:noreply, %{state | buffer: new_buffer, buffer_size: new_size, timer_ref: timer_ref}} end end @@ -78,20 +80,20 @@ defmodule AprsmeWeb.MapLive.PacketBatcher do def handle_cast(:flush, state) do state = cancel_timer(state) - if state.buffer != [] do + if state.buffer_size > 0 do process_batch(state.buffer, state.parent_pid) end - {:noreply, %{state | buffer: [], timer_ref: nil}} + {:noreply, %{state | buffer: [], buffer_size: 0, timer_ref: nil}} end @impl true def handle_info(:batch_timeout, state) do - if state.buffer != [] do + if state.buffer_size > 0 do process_batch(state.buffer, state.parent_pid) end - {:noreply, %{state | buffer: [], timer_ref: nil}} + {:noreply, %{state | buffer: [], buffer_size: 0, timer_ref: nil}} end @impl true