ETS Select 性能下降

6

我进行了一些关于从ETS表中选择性能的测试,并注意到了奇怪的行为。例如,我们有一个简单的ETS表(没有特定选项),它存储键/值 - 一个随机字符串和一个数字:

:ets.new(:table, [:named_table])

for _i <- 1..2000 do
    :ets.insert(:table, {:crypto.strong_rand_bytes(10) 
    |> Base.url_encode64 
    |> binary_part(0, 10), 100})
end

还有一条已知键的条目:

:ets.insert(:table, {"test_string", 200})

现在有一个简单的愚蠢基准测试功能,它尝试从ets表中多次选择test_string,并测量每个选择的时间:
test_fn = fn() ->
  Enum.map(Enum.to_list(1..10_000), fn(x) ->
    :timer.tc(fn() ->
      :ets.select(:table, [{{:'$1', :'$2'}, 
                           [{:'==', :'$1', "test_string"}], 
                           [:'$_']}])
    end)
  end) |> Enum.unzip
end

现在如果我使用Enum.max(timings)来查看最大时间,它会返回一个比几乎所有其他选择都大约多10倍的值。例如:

iex(1)> {timings, _result} = test_fn.()
....
....
....
iex(2)> Enum.max(timings)
896
iex(3)> Enum.sum(timings) / length(timings)
96.8845

我们可以看到最大值几乎比平均值高出10倍。
这里发生了什么?它与GC、内存分配时间或类似的问题有关吗?您有没有想法,为什么从ets表中进行选择有时会导致这样的放慢,或者如何对其进行性能分析。
更新:这里是时间分布图:enter image description here

1
有趣的是,哪一个是最大值?也许你可以绘制结果,它会显示一些趋势。 - Hauleth
2
添加了时间值分布图表。 - 0xAX
1个回答

4

select/2的第二个参数match_spec会导致其变慢。

根据这个问题的回答
Erlang: ets select and match performance

In trivial non-specific use-cases, select is just a lot of work around match.  
In non-trivial more common use-cases, select will give you what you really want a lot quicker.

此外,如果您使用的是 setordered_set 类型的表格,在根据键获取值时,请使用 lookup/2,因为它速度更快。
在我的电脑上,以下代码。
  def lookup() do
    {timings, _} = Enum.map(Enum.to_list(1..10_000), fn(_x) ->
      :timer.tc(fn() ->
        :ets.lookup(:table, "test_string")
      end)
    end) |> Enum.unzip
    IO.puts Enum.max(timings)
    IO.puts Enum.sum(timings) / length(timings)
  end

打印的

0 
0.0

当您打印时

16000
157.9

如果您感兴趣,这里可以找到ETS:select的NIF C代码。
https://github.com/erlang/otp/blob/9d1b3bb0db87cf95cb821af01189f6d6be072f79/erts/emulator/beam/erl_db.c

谢谢你的回答!我的实际查询比我这里写的要复杂一些,这就是我使用 :ets.select/2 而不是 :ets.lookup/2 的原因。是的,我同意,:ets.select/2 做了很多事情,特别是当第二个参数给出时,它应该比 lookup 慢。但同时我期望每个 select 都需要大约相同的时间,不会像你在图表上看到的那样有这样的峰值。 - 0xAX
我无法确定确切的原因,但可能是其中之一。
  1. NIF神话(http://erlang.org/doc/efficiency_guide/myths.html)
  2. GC(由于在调用ets:select时在堆上进行内存分配)。
- HelloWorld

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接