Skip to content

Better performance timer in elixir

License

Notifications You must be signed in to change notification settings

xlgames-navi/ex_timer

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

34 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ex_timer

Hex.pm Version

Better-performance timer in elixir

not using extra gen_server and not using any other processes for timer

Overview

In general, It used a timer to schedule any works in the future.
Process.send_after/4 provides its function in elixir.
But, If you register a lot of timers, its mailbox getting larger.
In elixir as the number of queues increases, cause performance issues.
Maybe the most important point to note that you should be keep a small queue called as mailbox.

Specification

  • To insert and delete new timer is reasonably fast.
    • insertion, deletion : O(n)
  • Don't inspect all registered a lot of timers to check if time has expired.
    • be lightweight what it check time-out : O(1)

Installation

If available in Hex, add in deps of mix.exs

def deps do
  [
    {:ex_timer, "~> 1.5"}
  ]
end

then run as

$ mix deps.get

Usage

defmodule Scheduler do
  use GenServer
  require ExTimer

  defmodule State do
    defstruct [:timer, calls: 0]
  end

  def start_link(_) do
    GenServer.start_link(__MODULE__, [])
  end

  def init(_) do
    Process.send_after(self(), :tick, :rand.uniform(1000))

    state = %State{timer: ExTimer.new()}
    state = put_in(state.timer, ExTimer.add(state.timer, {:timeout1, 1, 9}, 2000))
    state = put_in(state.timer, ExTimer.add(state.timer, {:timeout2, 3, 9}, 9000))
    state = ExtraTimer.start_timer(state)
    {:ok, state}
  end

  def handle_info(:tick, state) do
    {state, timer} = ExTimer.update(state, state.timer)
    state = put_in(state.timer, timer)
    Process.send_after(self(), :tick, :rand.uniform(1000))
    {:noreply, state}
  end
  
  def handle_timer({:timeout1, arg0, arg1}, timer, state) do
    IO.puts("#{inspect(__ENV__.function)} (#{arg0}, #{arg1}) called")
    {state, timer}
  end
  
  def handle_timer({:timeout_type1, module, function}, timer, state) do
    apply(module, function, [state, timer])
  end

  def handle_timer({:timeout_type2, function_ref}, timer, state) do
    function_ref.(state, timer)
  end
end

defmodule ExtraTimer do
  def start_timer(state) do
    state = %{state | timer: 0, calls: 0}
    state = put_in(state.timer, ExTimer.new())
    state = put_in(state.timer, ExTimer.add(state.timer, {:timeout_type1, ExtraTimer, :handle_timeout_1}, 100))
    state = put_in(state.timer, ExTimer.add(state.timer, {:timeout_type2, &handle_timeout_2/2}, 100))
    state
  end
  
  def handle_timeout_1(state, timer) do
    state = put_in(state.calls, state.calls + 1)
    {state, timer}
  end

  def handle_timeout_2(state, timer) do
    state = put_in(state.calls, state.calls + 1)
    {state, timer}
  end
end

About

Better performance timer in elixir

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Elixir 100.0%