((Пожалуйста, прости меня, что я задаю более одного вопроса в одной теме. Я думаю, что они связаны.))
Здравствуйте, я хотел знать, какие лучшие практики существуют в Erlang в отношении предварительно скомпилированных данных для каждого модуля.
Пример: У меня есть модуль , который интенсивно работает на априорном, очень сложном регулярных выражениях. re: compile / 2 документация гласит: «Компиляция один раз и многократное выполнение гораздо эффективнее, чем компиляция каждый раз, когда нужно найти соответствие». Поскольку тип данных re (mp ()) никоим образом не указан, и поэтому его нельзя поместить во время компиляции, если вам нужен независимый от цели луч, необходимо скомпилировать RegEx во время выполнения. (( Примечание: re: compile / 2 - только пример. Любая сложная функция для запоминания подойдет моему вопросу.))
Модуль Эрланга (может) имеет атрибут -on_load(F/A)
, обозначающий метод, который должен выполняться один раз при загрузке модуля . Таким образом, я мог бы разместить свои регулярные выражения для компиляции в этом методе и сохранить результат в новой ets таблице с именем ?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
.