Elixir/Erlang:使用静态快速查找 table

Elixir/Erlang: Fast lookup with static table

在我的应用程序中,我需要将整数转换为某个项;例如:

1 → :red
2 → :green
3 → :blue

table 是静态的,它在编译时是已知的,它的索引范围是 <1, n>。大约有 60 个。

table应该用什么方式表示,这样查找速度最快? Dict、HashDict、元组(使用 kernel.elem())、ets、具有模式匹配的函数...?

如果地图是完全静态的,不会改变,可以使用生成模式匹配。这将是将该查找集成到您的应用程序中的最快方式。

一些示例代码,从外部文件读取这些映射:https://github.com/h4cc/slugger/blob/master/lib/slugger.ex#L69-72 您的源地图数据可以保存在 @attribute.

中,而不是使用外部文件

即使在运行时需要新的映射,也可以通过在 HashDict 中进行查找的包罗万象的模式匹配来处理。

如果您依赖多个进程的快速访问,那么mochiglobal may be the answer. It's tricky constant pool, which keeps keys and values as functions in module. Every time you put/2 something, module is recompiled (it's slow but doesn't matter in your case). With this approach value is not copied into process heap as it is function call. Here is better explanation

正如 Julius Beckmann 在这种情况下所建议的那样,具有模式匹配的函数应该足够了,因为根据我的测量,它们比访问地图快 5 倍以上。

您可以在下面找到您正在寻找的实现(基准代码包含在底部):

defmodule TermLookUpByInteger do
  @term_by_integer %{
    1 => :aa, 11 => :ba, 21 => :ca, 31 => :da, 41 => :ea, 51 => :fa, 61 => :ga,
    2 => :ab, 12 => :bb, 22 => :cb, 32 => :db, 42 => :eb, 52 => :fb, 62 => :gb,
    3 => :ac, 13 => :bc, 23 => :cc, 33 => :dc, 43 => :ec, 53 => :fc, 63 => :gc,
    4 => :ad, 14 => :bd, 24 => :cd, 34 => :dd, 44 => :ed, 54 => :fd, 64 => :gd,
    5 => :ae, 15 => :be, 25 => :ce, 35 => :de, 45 => :ee, 55 => :fe, 65 => :ge,
    6 => :af, 16 => :bf, 26 => :cf, 36 => :df, 46 => :ef, 56 => :ff, 66 => :gf,
    7 => :ag, 17 => :bg, 27 => :cg, 37 => :dg, 47 => :eg, 57 => :fg, 67 => :gg,
    8 => :ah, 18 => :bh, 28 => :ch, 38 => :dh, 48 => :eh, 58 => :fh, 68 => :gh,
    9 => :ai, 19 => :bi, 29 => :ci, 39 => :di, 49 => :ei, 59 => :fi, 69 => :gi,
    0 => :aj, 10 => :bj, 20 => :cj, 30 => :dj, 40 => :ej, 50 => :fj, 60 => :gj,
  }

  @doc """
    iex> TermLookUpByInteger.lookup_pmf(2)
    :ab
  """
  def lookup_pmf(int), do: do_lookup(int)

  for {int, term} <- @term_by_integer do
    defp do_lookup(unquote(int)), do: unquote(term)
  end

  @doc """
    iex> TermLookUpByInteger.lookup_m(3)
    :ac
  """
  def lookup_m(int), do: @term_by_integer[int]
end

# Benchmark:

n = 1_000_000
range = 1..(n)
measure = fn fun -> :timer.tc(fn -> for _ <- range, do: fun.() end) end
{time_pmf, _result} = measure.(fn -> TermLookUpByInteger.lookup_pmf(:random.uniform(60)) end)
{time_m, _result}   = measure.(fn -> TermLookUpByInteger.lookup_m(:random.uniform(60)) end)

IO.puts "                      Sample size: #{n}"
IO.puts "Pattern matching functions lookup: #{time_pmf/1000} ms"
IO.puts "                       Map lookup: #{time_m/1000} ms"
IO.puts "              Absolute Difference: #{(time_pmf-time_m)/1000} ms"
IO.puts "              Relative Difference: #{round((time_pmf-time_m)/time_m*100)}%"
IO.puts "                           Faster: x #{Float.round(time_m/time_pmf, 2)} times"

结果:

                      Sample size: 1000000
Pattern matching functions lookup: 447.6 ms
                       Map lookup: 2423.517 ms
              Absolute Difference: -1975.917 ms
              Relative Difference: -82%
                           Faster: x 5.41 times

希望有用。