Как выбрать случайный элемент из набора ets в Erlang/Elixir?

У меня есть большое количество процессов, которые мне нужно отслеживать в наборе ets, а затем случайным образом выбирать отдельные процессы. Итак, я создал такой набор:

:ets.new(:pid_lookup, [:set, :protected, :named_table])

тогда ради аргумента давайте просто вставим self() 1000 раз:

Enum.map 1..1000, fn x -> :ets.insert(:pid_lookup, {x, self()}) end

Теперь мне нужно выбрать один наугад. Я знаю, что могу просто выбрать случайный с помощью :ets.lookup(:pid_lookup, :rand.uniform(1000)), но что, если я заранее не знаю размер набора (в приведенном выше случае 1000)?

Как узнать размер набора ets? И/или есть ли лучший способ выбрать случайный pid из структуры данных ets?


person Thomas Browne    schedule 10.12.2018    source источник
comment
Вы можете использовать :ets.info(tab, :size), чтобы получить размер таблицы. Я действительно не знаю лучшего варианта захвата случайного элемента из него. Каков вариант использования случайного pid из набора?   -  person Justin Wood    schedule 10.12.2018
comment
@JustinWood Я просто сравниваю генерирующие серверы. Я создаю 1000 из них, затем я хочу, чтобы каждый из них отправлял сообщения другому случайному несколько раз и смотрел, сколько времени это занимает с сообщениями разного размера. Но когда я создаю Genservers, они запускаются, поэтому другие Genservers могут быть еще не запущены, когда они начинают отправлять. Поэтому мне нужно знать, сколько было создано на начальных этапах теста.   -  person Thomas Browne    schedule 10.12.2018
comment
Ну, 1000, конечно, условно. Я проверю производительность на основе различных чисел. По сути, я хочу получить мысленную карту того, насколько быстро проходит передача сообщений.   -  person Thomas Browne    schedule 10.12.2018
comment
Если вы просто хотите протестировать передачу сообщений, почему бы не запустить все серверы генерации и не начать отправлять сообщения?   -  person Justin Wood    schedule 10.12.2018
comment
Ну, я действительно хочу знать, как выбирать элементы случайным образом в таблице ets для кучи статистики, которую я все равно делаю. Но, чтобы ответить на ваш вопрос напрямую, по мере того, как я раскручиваю 1000 Genservers, первые начнут отправлять до того, как раскрутятся последние. Поэтому я не могу просто выбрать случайное число из 1000. Мне нужно знать, сколько их было раскручено, а для этого мне нужно знать, каков размер таблицы ETS.   -  person Thomas Browne    schedule 10.12.2018
comment
@JustinWood, пожалуйста, превратите ets.info(<table name>, :size) в ответ, и я приму его.   -  person Thomas Browne    schedule 12.12.2018


Ответы (2)


  • Если ключи порядковые номера
tab = :ets.new(:tab, [])
Enum.each(1..1000, & :ets.insert(tab, {&1, :value}))
size = :ets.info(tab,  :size) 
# size = 1000
value_picked_randomly = :ets.lookup(tab, Enum.random(1..1000)) 

:ets.info(tab, :size) возвращает размер таблицы; который представляет собой количество записей, вставленных в данную таблицу.

  • Если вы не знаете, что ключи
first = :ets.first(tab)
:ets.lookup(tab, first)
func = fn key->
    if function_that_may_return_true() do
        key = case :ets.next(tab, key) do
         :'$end_of_table' -> throw :reached_end_of_table
         key -> func.(key)
        end
    else 
        :ets.lookup(tab, key)
    end
end
func.()

func перебирает таблицу ets и возвращает случайное значение. Это займет много времени, поэтому не будет идеальным решением для таблиц с большим количеством записей.

person HelloWorld    schedule 07.01.2020

Как я понял из комментариев, это проблема XY.

По сути, вам нужно отследить изменяющийся список и случайным образом выбрать один из его элементов. ETS в целом и :ets.set в частности ни в коем случае не предназначены для запроса размера. Они служат разным целям.

Создайте Agent в своем дереве наблюдения, содержащем список PID уже запущенных серверов и используйте Kernel.length/1 для запроса его размера или даже используйте Enum.random/1, если список не на самом деле огромен (последний обходит все перечисляемое, чтобы получить случайный элемент).

person Aleksei Matiushkin    schedule 11.12.2018
comment
это решение только в том случае, если вы не беспокоитесь о DDOS-атаке вашего агента. Я специально избегал решений, связанных с узкими местами в одном процессе. 1000 это только пример. В комментариях далее говорится, что я сравниваю. Поэтому разумно предположить, что число для отслеживания может стать на несколько порядков больше. Отсюда ЭТС. На самом деле вопрос совершенно ясен. Я не просил кого-то угадать мою архитектуру, я просто задавал конкретные вопросы о ETS, а именно, как найти размер или как произвести выборку случайным образом. - person Thomas Browne; 12.12.2018
comment
Я на 102% уверен, что в этом случае Agent подойдет, но если вы настаиваете на использовании ETS, просто следите за номером — это отдельный ETS. При вставке в PIDs ETS также увеличиваем счетчик. - person Aleksei Matiushkin; 12.12.2018