Лучший способ написания вложенных циклов в эликсире - PullRequest
0 голосов
/ 18 мая 2019

Пожалуйста, предложите лучший способ (больше эликсира) написания следующего кода C в эликсире.

int some_num = 0;

for(int i = 0; i < 100; i++){
  for(int j = 0; j < 1000; j++){
    for(int k = 0; k < 10000; k++){
      some_num += 1;
    }
  }
}
printf("%d", some_num);

И может ли быть реализован с получением преимуществ параллелизма эликсира?

РЕДАКТИРОВАТЬ: немного фона, я новичок в эликсире и все еще учусь.Основным мотивом этого вопроса было написание большего количества идиоматического кода эликсира, чем применения параллелизма.

Ответы [ 2 ]

7 голосов
/ 18 мая 2019

Простейший способ реализовать то, что вы написали, - использовать макрос for:

sum =
  for i <- 0..100,
      j <- 0..1_000,
      k <- 0..10_000,
      reduce: 0 do
    acc -> acc + 1
  end

EDIT:

:reduce опция доступна в новых версиях Elixir (1.8+). В более старых версиях вы можете использовать вложенные Enum.reduce/3:

Enum.reduce(0..100, 0, fn _, acc ->
  acc + Enum.reduce(0..1_000, 0, fn _, acc ->
    acc + Enum.reduce(0..10_000, 0, fn _, acc ->
      acc + 1
    end)
  end)
end)

О второй части вопроса: нет , этот цикл не сильно выиграет от параллелизма, и если он каким-либо образом изменит время, он будет только медленнее. В этом конкретном случае его можно записать как sum = 100 * 1_000 * 10_000, что может быть быстрее, поскольку компилятор может легко оптимизировать его до 10_000_000 (компилятор IIRC Erlang не может оптимизировать данный цикл до константы).

TL; DR такой очевидный цикл не может быть улучшен параллелизмом, и в общем случае трудно сказать, помогут ли процессы (или распараллеливание) или нет. Также очень важно помнить, что parallel != concurrent, поэтому вы не получите ускорения от запуска N процессов Эрланга на машине с N-1 планировщиками (по умолчанию число процессоров).

1 голос
/ 18 мая 2019

Ниже приведен рабочий пример того, как вы могли бы достичь параллелизма для своих довольно забавных задач по выполнению операций приращения 1000000000 - на случай, если вам интересно, как это можно сделать.

Код ниже порождает 100 процессов эликсира, соответствующих вашей внешней петле. Внутренний код - эти 2 вложенных цикла - (записанные в более идиоматической форме с использованием Enum.reduce см. ), таким образом, выполняется одновременно (насколько это возможно для виртуальной машины). Результаты каждого процесса отправляются выделенному процессу получателя, который начинает отсчет, начиная с 100, всякий раз, когда он получает новый результат. Каждый промежуточный итог добавляется к общему итогу, который затем распечатывается после получения 100 промежуточных итогов. Для проверки: сохраните код в виде файла nested.ex и скомпилируйте в оболочке Elixir, используя c nested.ex. Запустите его в этой оболочке, используя Main.main. Вы должны увидеть следующий вывод:

iex(4)> Main.main    
:ok
total = 1000000000

с ok, идущим за несколько секунд до total. Вы также должны испытать многоядерный процессор.

defmodule Main do 
def main(  ) do
    pid = spawn fn -> Receiver.loop( 100,0 ) end
    1..100 |> Enum.each( fn x -> spawn (fn -> Nested.run(pid) end ) end)
  end

end

#################################
defmodule Nested do
 def run(pid) do
    sub_total= 
    Enum.reduce( 1..1000, 0, fn x, acc_n -> acc_n +
      Enum.reduce( 1..10000, 0, fn y, acc_m -> acc_m + 1  end )
    end )
   send pid, sub_total
   Process.exit(self(), :kill )
 end
end

#################################
defmodule Receiver do
 def loop(0, total) do
   IO.puts "total = #{total}"
   Process.exit(self(), :kill )
 end
 #
 def loop(count_down, total ) do # count down to zero collecting totals
    receive do
      sub_total ->
        loop(count_down-1, sub_total + total)
    end
 end
end
#################################

Параллелизм можно получить, чтобы получить преимущество по сравнению с чистым параллелизмом, разумно преобразовав из простого spawn в Node.spawn см. Документы

Тест неформальной скорости Тестирование на моем компьютере Win10, который сообщает:

Erlang/OTP 20 [erts-9.0] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10]
Interactive Elixir (1.8.2) ..

Код, который я здесь привожу, вычисляет результат в 16 с, где, как и у @Hauleth, это занимает более 10 минут - поскольку его, похоже, выделяется только одно ядро, где мое получает все 4.

...