((请原谅我在一个线程中问了多个问题。我认为它们是相关的。))
您好,我想知道 Erlang 在每个模块的预编译数据方面存在哪些最佳实践。
示例:我有一个模块,它对一个已知的、非常复杂的正则表达式进行大量操作。re:compile/2 的文档说:“编译一次并执行多次比每次要匹配时都编译效率高得多”。由于没有指定 re 的 mp() 数据类型,因此如果您想要一个独立于目标的梁,则不能在编译时放置,因此必须在运行时编译 RegEx。((注意: re:compile/2 只是一个例子。任何要记忆的复杂函数都适合我的问题。))
Erlang 的模块 (can) 有一个-on_load(F/A)
属性,表示加载模块时应该执行一次的方法。因此,我可以将我的正则表达式放入此方法中进行编译,并将结果保存在一个名为?MODULE
.
在丹的回答后更新。
我的问题是:
- 如果我理解 ets 正确,它的数据保存在另一个进程中(与进程字典不同),并且检索 ets 表的值非常昂贵。(如果我错了,请证明我错了!) ets 中的内容是否应该复制到进程字典中以加快速度?(记住:数据永远不会被更新。)
- 将所有数据作为一条记录(而不是许多表项)放入 ets/process 字典中是否有任何(相当大的)缺点?
工作示例:
-module(memoization).
-export([is_ipv4/1, fillCacheLoop/0]).
-record(?MODULE, { re_ipv4 = re_ipv4() }).
-on_load(fillCache/0).
fillCacheLoop() ->
receive
{ replace, NewData, Callback, Ref } ->
true = ets:insert(?MODULE, [{ data, {self(), NewData} }]),
Callback ! { on_load, Ref, ok },
?MODULE:fillCacheLoop();
purge ->
ok
end
.
fillCache() ->
Callback = self(),
Ref = make_ref(),
process_flag(trap_exit, true),
Pid = spawn_link(fun() ->
case catch ets:lookup(?MODULE, data) of
[{data, {TableOwner,_} }] ->
TableOwner ! { replace, #?MODULE{}, self(), Ref },
receive
{ on_load, Ref, Result } ->
Callback ! { on_load, Ref, Result }
end,
ok;
_ ->
?MODULE = ets:new(?MODULE, [named_table, {read_concurrency,true}]),
true = ets:insert_new(?MODULE, [{ data, {self(), #?MODULE{}} }]),
Callback ! { on_load, Ref, ok },
fillCacheLoop()
end
end),
receive
{ on_load, Ref, Result } ->
unlink(Pid),
Result;
{ 'EXIT', Pid, Result } ->
Result
after 1000 ->
error
end
.
is_ipv4(Addr) ->
Data = case get(?MODULE.data) of
undefined ->
[{data, {_,Result} }] = ets:lookup(?MODULE, data),
put(?MODULE.data, Result),
Result;
SomeDatum -> SomeDatum
end,
re:run(Addr, Data#?MODULE.re_ipv4)
.
re_ipv4() ->
{ok, Result} = re:compile("^0*"
"([1-9]?\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.0*"
"([1-9]?\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.0*"
"([1-9]?\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.0*"
"([1-9]?\\d|1\\d\\d|2[0-4]\\d|25[0-5])$"),
Result
.