LoginSignup
10
2

More than 1 year has passed since last update.

Elixir 高性能な數の數え方(counters)

Last updated at Posted at 2023-03-05

Elixir Patterns book & Livebooksという本を読んでElixirで效率よく數を數える方法について學んだので練習しながらメモします。

ちなみにElixirとあわせて舊字體の漢字の練習もしているので、萬一見にくい場合は以下のウエブアプリで適宜お好みの漢字に變換してみてください。

Erlangのcountersモジュール

  • 效率よく數値の配列への加算および減算する目的專用に最適化されたモジュール
  • 不可分操作で實行できるため、多數の同時アクセスが發生した場合でもデータの不整合が發生しない
  • ElixirではLogger.Backendsで使用されている

詳しくは原典をご参照ください。

カウンター數を決定する

  • カウンターの數を:countersインスタンス生成時に指定することが必要
  • インデックスは1から始まる數字
# how many numbers to manage
counter_size = 2

# one-based numbering 
autorace_index = 1
toukon_index = 2

:countersインスタンスを生成

  • options
    • :atomics (default)
    • write_concurrency
my_counter = :counters.new(counter_size, [:write_concurrency])

get_state = fn ->
  %{
    autorace: :counters.get(my_counter, autorace_index),
    toukon: :counters.get(my_counter, toukon_index)
  }
end

get_state.()
# %{autorace: 0, toukon: 0}

加算

:counters.add/3

:counters.add(my_counter, autorace_index, 1)
:counters.add(my_counter, toukon_index, 123)
get_state.(:counters.sub)
# %{autorace: 1, toukon: 123}

減算

:counters.sub/3

:counters.sub(my_counter, autorace_index, 124)
:counters.sub(my_counter, toukon_index, 24)
get_state.()
# %{autorace: -123, toukon: 99}

上書き

:counters.put/3

:counters.put(my_counter, autorace_index, 0)
:counters.put(my_counter, toukon_index, 0)
get_state.()
# %{autorace: 0, toukon: 0}

大量の數を數える

試しに10萬個のデータを受け取って數を數えてみます。

do_count = fn _ ->
  case Enum.random([:toukon, :autorace]) do
    :toukon -> :counters.add(my_counter, toukon_index, 1)
    :autorace -> :counters.add(my_counter, autorace_index, 1)
  end
end

1..100_000
|> Task.async_stream(&do_count.(&1), max_concurrency: 500)
|> Stream.run()

get_state.()
# %{autorace: 50099, toukon: 49901}

:tada: :tada: :tada:

何も計測していませんが、ドキュメントを信じているのでなんとなく高性能にみえます。:sweat_smile:

高性能のカラクリ

Elixirでは通常データは更新の度にコピーされるのですが、性能を重視した關數プログラミングっぽくない幾つかの逃げ道も用意されているようです。Erlangのcountersモジュールはその逃げ道の一つです。

他にもいくつか擧げられます。例えば、ラベル付き有向グラフを実装したdigraphMix.Deps.ConvergerMix.Tasks.Xrefで活用されています。

讀み込み速度が最適化されたpersistent_termは書き込みの頻度が少ない設定關係で利用されています。高速で値を取得できます。Mix.Stateではpersistent_termと汎用インメモリデータベースetsがうまく使い分けられています。

Elixirコミュニティ

本記事は以下のモクモク會での成果です。みなさんから刺激と元氣をいただき、ありがとうございました。

もしご興味のある方はお氣輕にご參加ください。

生產者の皆樣いつも美味しい食材をありがとうございます。おかげで健康に元氣にもくもく取り組むことができます。

10
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
2