Параллельные случайные числа юлия

Рассмотрим базовую итерацию для генерации N случайных чисел и сохранения их в массиве (предположим, что нас не интересует понимание массива, а также что мы не знаем вызова rand(N))

function random_numbers(N::Int)
array = zeros(N)
for i in 1:N
    array[i] = rand()
end
array
end

Меня интересует аналогичная функция, которая использует ядра моего ноутбука для создания того же массива. Я проверил этот хороший блог, где макросы @everywhere, Введены @spawn и @parallel, но там расчет ведется "на лету" и массив для сохранения данных не нужен.

У меня сложилось впечатление, что это очень просто и может быть легко выполнено с помощью, возможно, функции pmap, но я не знаком с параллельными вычислениями.

Моя цель — применить этот метод к функции, которую я создал для генерации случайных чисел, взятых из необычного распределения.


person dapias    schedule 24.11.2017    source источник
comment
Итак, давайте вернемся к фактам: [1] Что вы уже пробовали? [2] Где находятся собранные результаты? [3] Чего не хватало в собранных результатах по сравнению с некоторым набором ваших предыдущих ожиданий и что вы тестировали, пытаясь лучше сопоставить первое со вторым? Будьте достаточно четкими в публикации этих фактов и количественных показателей, прежде чем кричать на членов сообщества (цит.) Есть идеи?, это в конечном счете невежливо, не так ли?   -  person user3666197    schedule 24.11.2017
comment
Вы имеете в виду что-то вроде: result = @parallel (vcat) for i in 1:ncores random_numbers(N) end ?   -  person Liso    schedule 24.11.2017


Ответы (2)


Как было предложено в комментарии, всегда приветствуются дополнительные разъяснения в вопросе. Однако, похоже, pmap сделает то, что требуется. Соответствующая документация находится здесь.

Ниже приведен пример. Обратите внимание, что время, затрачиваемое на метод pmap, составляет половину от обычного map. С 16 ядрами ситуация может быть существенно лучше:

julia> addprocs(2)
2-element Array{Int64,1}:
 2
 3

julia> @everywhere long_rand() = foldl(+,0,(randn() for i=1:10_000_000))

julia> long_rand()
-1165.9596619177153

julia> @time map(x->long_rand(), zeros(10,10))
  8.455930 seconds (204.89 k allocations: 11.069 MiB)
10×10 Array{Float64,2}:
   ⋮
   ⋮ 

julia> @time pmap(x->long_rand(), zeros(10,10));
  6.125479 seconds (773.08 k allocations: 42.242 MiB, 0.25% gc time)

julia> @time pmap(x->long_rand(), zeros(10,10))
  4.609745 seconds (20.99 k allocations: 954.991 KiB)
10×10 Array{Float64,2}:
   ⋮
   ⋮ 
person Dan Getz    schedule 24.11.2017
comment
Не могли бы вы подробнее рассказать об определении функции long_rand()? Итак, в функцию addprocs я передаю количество ядер, верно? Что вы думаете о комментарии @Liso? Это достигает моей первоначальной цели, но мне очень интересно, как вы представляете решение. - person dapias; 25.11.2017
comment
Во-первых, комментарий Liso описывает другой способ сделать то же самое - очень похожий. Определение long_rand - это просто расточительный расчет случайного значения (путем добавления множества случайных нормальных переменных), используемого в качестве заполнителя для сложного вычисления, которое вы хотите выполнить. - person Dan Getz; 25.11.2017
comment
@dapias Если вы сделаете addprocs, то ncores = length(procs()) даст вам фактическое количество рабочих процессов. (см. также rmprocs) @DanGetz Я все еще новичок и очень впечатлен производительностью pmap! :) Я протестировал @time @parallel (hcat) for i in 1:3 map(x->long_rand(), zeros(10)) end с 4 рабочими процессами. Несмотря на меньшее распределение, его скорость была ниже, чем у @time pmap(x->long_rand(), zeros(10,3)). И если бы я изменил (примерно в 40_000 раз медленнее) long_rand на rand, то ваше решение было бы сравнительно быстрым для отображения функции! :) - person Liso; 25.11.2017
comment
nworkers() и nprocs() указывают количество рабочих и процессов соответственно. Количество рабочих — это то, на что хотелось бы разделить работу. Кроме того, важно упомянуть, что многопоточность также является путем к параллельной обработке, но она является экспериментальной (но документы объясняют все это и многое другое). - person Dan Getz; 25.11.2017

Я бы рекомендовал более тщательно инициализировать генераторы случайных чисел в параллельных процессах, например:

# choose the seed you want
@everywhere srand(1)
# replace 10 below by maximum process id in your case
@everywhere const LOCAL_R = randjump(Base.GLOBAL_RNG, 10)[myid()]
# here is an example usage
@everywhere f() = rand(LOCAL_R)

Таким образом вы:

  • убедитесь, что ваши результаты воспроизводимы;
  • контролировать, чтобы между случайными последовательностями, сгенерированными разными процессами, не было перекрытия.
person Bogumił Kamiński    schedule 24.11.2017
comment
В ответе DanGetz вы можете написать @everywhere long_rand() = foldl(+,0,(randn(LOCAL_R) for i=1:10_000_000)) все остальное остается без изменений. - person Bogumił Kamiński; 25.11.2017
comment
Важно отметить, что это не делает результаты воспроизводимыми при pmap распараллеливании, поскольку порядок, в котором назначаются процедуры, будет (почти наверняка) отличаться при каждом запуске. Однако это делает результаты воспроизводимыми для @parallel циклов. - person hckr; 22.03.2018