我围绕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
Run Code Online (Sandbox Code Playgroud)
还有一个具有已知密钥的条目:
:ets.insert(:table, {"test_string", 200})
Run Code Online (Sandbox Code Playgroud)
现在有一个简单的愚蠢的基准测试函数,它试图test_string从ets表中多次选择并测量每次选择的时间:
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
Run Code Online (Sandbox Code Playgroud)
现在,如果我看看最大时间,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
Run Code Online (Sandbox Code Playgroud)
在这里我们可以看到最大值几乎是平均值的10倍。
这里发生了什么事?它与GC,内存分配时间或诸如此类有关吗?您是否有任何想法,为什么从ets表中进行选择有时会导致这种变慢,或者如何进行分析。
UPD。
我一直在学习如何使用ets,但有一件困扰我的事情是,偶尔*ets:match会抛出一个bad argument......而且,从它们开始,所有后续调用(甚至以前工作的调用)也会抛出bad argument:
> ets:match(Tid, { [$r | '$1'] }, 1).
% this match works...
% Then, at some point, this comes up:
** exception error: bad argument
in function ets:match/3
called as ets:match(24589,{[114|'$1']},1)
% And from then on, matches stop working:
> ets:match(Tid, { [$r | '$1'] }, 1).
** exception error: bad argument
in function ets:match/3
called as ets:match(24589,{[114|'$1']},1)
有没有办法"重置" ets系统,以便我可以再次查询它(即从shell)?
*:我无法重现这个问题......但是当我试图做"其他事情"时,它经常发生.
简短版本:ets:foldl当一个人正在迭代它们时,是否可以安全地删除每个ETS记录?
假设一个ETS表正在累积信息,现在是时候处理它了.从表中读取记录,以某种方式使用,然后删除.(另外,假设表是private,所以没有并发问题.)
在另一种语言中,使用类似的数据结构,您可以使用for ... each循环,处理每个记录,然后从hash/dict/map /中删除它.但是,该ets模块没有foreach例如lists.
但这可能有效:
1> ets:new(ex, [named_table]).
ex
2> ets:insert(ex, {alice, "high"}).
true
3> ets:insert(ex, {bob, "medium"}).
true
4> ets:insert(ex, {charlie, "low"}).
true
5> ets:foldl(fun({Name, Adjective}, DontCare) ->
io:format("~p has a ~p opinion of you~n", [Name, Adjective]),
ets:delete(ex, Name),
DontCare
end, notused, ex).
bob has a "medium" opinion of you
alice has a "high" opinion of you
charlie has a "low" opinion of you
notused
6> ets:info(ex). …Run Code Online (Sandbox Code Playgroud) 当我调试时,我通常会查看大约5000个进程,每个进程可能是大约100个gen_servers,fsms等之一.如果我想知道erlang进程是什么,我可以这样做:
process_info(pid(0,1,0), initial_call).
Run Code Online (Sandbox Code Playgroud)
并获得如下结果:
{initial_call,{proc_lib,init_p,5}}
Run Code Online (Sandbox Code Playgroud)
......这几乎没用.
最近,我想到了一个想法(支持你自己)注册每个过程的名称告诉我世卫组织所代表的过程.例如,player_1150是代表玩家1150的玩家进程.是的,我在一周的运行过程中最终制造了几百万个原子.(我希望听到关于在我的系统运行时使用大约8GB未使用的实际内存时限制为10,000,000个原子的缺点的评论,如果有的话.)这样做意味着我可以在实时系统的控制台上运行,查询所有进程的消息队列有多长,查找最高级别的违规者,然后检查这些进程是否已注册并打印出他们注册的原子.
我遇到了这个问题:我正在将进程从一个节点移动到另一个节点.现在玩家进程可以有3个不同的名字; player_1158,player_1158_deprecating,player_1158_replacement.我必须绝对确保我使用精确计时注册和注销这些名称,以确保始终命名一个进程并且始终存在相应的名称,并且我不会尝试注册某个死亡进程已经存在的名称.有一些slop空间,因为这只用于实时系统的控制台调试尽管如此,我开始感觉这个机制的那一刻影响了我如何开发系统(移动进程的那个)我觉得是时候到了做点别的.
我现在面前有两个想法.将流程ID与其描述相关联的ets表:
ets:insert(self(), {player, 1158}).
Run Code Online (Sandbox Code Playgroud)
我真的不喜欢那个,因为我必须手动保持桌子清洁.当玩家退出(或崩溃)时,有人负责确保他的数据从ets表中删除.
第二种方法是使用进程字典,存储类似的信息.当我对实时系统的探索让我想知道一个进程是谁时,我可以使用process_info查看他的进程字典.
我意识到这些解决方案都没有在功能上干净,但考虑到系统本身永远不会,这些数据的消费者,我不会太担心它.我需要某些调试工具才能快速轻松地工作,因此所描述的行为不容争论.是否有任何令人信服的论据可以采用这种或那种方式(除了学术界"不使用_,它是邪恶的"罐装垃圾?)我很乐意听到其他建议及其理由.
我使用预测包中的ets()和auto.arima()函数来预测R中的未来值.应该使用哪个标准来选择这两者之间的最佳模型?
以下是ets(data.ets)和auto.arima(data.ar)的准确度输出.
> accuracy(data.ets)
ME RMSE MAE MPE MAPE MASE
0.6995941 4.1325246 3.2634246 0.5402465 2.7777897 0.5573740
> accuracy(data.ar)
ME RMSE MAE MPE MAPE MASE
-0.8215465 4.3640818 3.1070931 -0.7404200 2.5783128 0.5306735
Run Code Online (Sandbox Code Playgroud)
每个型号的AIC如下
> ETSfit$aic
[1] 613.8103
> ARIMAfit$aic
[1] 422.5597
Run Code Online (Sandbox Code Playgroud)
以下是ets和auto.arima的拟合模型
> ETSfit
ETS(A,N,A)
Call:
ets(y = data.ts)
Smoothing parameters:
alpha = 0.5449
gamma = 1e-04
Initial states:
l = 95.8994
s=6.3817 -3.1792 6.8525 3.218 -3.4445 -1.2408
-4.5852 0.4434 1.7133 0.8123 -1.28 -5.6914
sigma: 4.1325
AIC AICc BIC
613.8103 620.1740 647.3326 …Run Code Online (Sandbox Code Playgroud) 我有很多分析数据,我希望每隔一段时间(例如一分钟)进行汇总。数据被发送到将其存储在ETS表中的过程中,并且每隔一段时间,计时器就会向其发送一条消息处理表并删除旧数据。
问题在于传入的数据量千差万别,我基本上需要对它做两件事:
如果保持这两个条件,我基本上可以假设该表的大小恒定,并且其中的所有内容都比X更新。
问题是我还没有找到一种有效的方法来将这两件事合在一起。我知道我可以使用匹配规范来删除所有早于X的整数,如果索引是时间戳记,这应该相当快。尽管我不确定这是否是定期整理桌子的最佳方法。
第二个问题是将表的总大小保持在一定限制内,我不太确定该怎么做。我想到的一种解决方案是使用一个自动增量字段,该字段将在每次插入时使用,并且在修剪表时,查看第一个和最后一个索引,计算差异,然后再次使用匹配规范删除阈值以下的所有内容。
说了这么多之后,感觉到我可能正在将ETS表用于并非旨在做的事情。有没有更好的方法来存储这样的数据,还是我正确地解决了这个问题?
我正在编写一个 Elixir 应用程序,它需要一个注册表来存储哪个 pid 属于哪个用户的映射。我将在应用程序中为每个用户提供一个 GenServer 来进行监督。我有一个使用 ETS 处理一个节点的基本示例,但对于 2 个以上节点,我无法使用 ETS,因为它不支持集群/复制。拥有分布式缓存还有哪些其他选项?通过一些研究,我的选择是使用 Redis 等数据库或使用 Amensia。
我使用 erlangs ets 功能缓存了一堆邮政编码和经纬度值。
如下图...
iex()> :ets.new(:postcode_cache, [:named_table])
:postcode_cache
iex()> :ets.insert(:postcode_cache, [{"OX495NU", "latlongvalue"},{"M320JG", "latlongvalue"}])
true
Run Code Online (Sandbox Code Playgroud)
这类似于我在应用程序中创建的 ets 表。我想创建一个函数,仅从缓存中选择条目,其中邮政编码包含用户输入的子字符串。有什么办法可以做到这一点,如果可以的话,我将如何实现这个功能?
(将来我可能只想使用经纬度值选择一定距离内的值,但这超出了这个问题的范围)。
为了清楚起见,该表类似于以下灵丹妙药列表......
iex()> postcode_list = [{"OX495NU","latlong"}, {"M320JG", "latlong"}]
Run Code Online (Sandbox Code Playgroud)
我想用 ets 复制的功能是这样的......
iex()> Enum.filter(list, fn({postcode, _}) -> if String.contains?(postcode, "OX49") end)
[{"OX495NU", "latlong"}]
Run Code Online (Sandbox Code Playgroud) 我使用ets通过elixir作为一个简单的内存持久层来存储和检索键,也用于偶尔的foldl,它涉及减少许多具有不同值的重复键.我正在使用行李选项.
是否有一种简单的O(1)方法来检索当前密钥的列表而无需进行更多涉及的表遍历或匹配或折叠?
Erlang或Elixir语法响应欢迎.
:ets.new(:cache, [:bag, :named_table, :protected])
Run Code Online (Sandbox Code Playgroud)
我有一个原子键的静态映射,由我用来帮助插入的整数索引.但并非所有的钥匙都被使用..
chunk_key_map = %{2 => :chunk_key_2, ..... 28 => :chunk_key_28}
Run Code Online (Sandbox Code Playgroud)
如果没有快速的方法,我知道我可以做一个ets:查找我的每个静态原子键值并测试!= []并生成我自己的列表,但想看看ets是否支持这样的功能.
谢谢
我想在ets中创建一个表,如果它不存在的话.如何检查此命名是否存在?
ets ×10
erlang ×6
elixir ×5
caching ×2
debugging ×1
foreach ×1
forecasting ×1
performance ×1
process ×1
r ×1