在Elixir中嵌套嵌套循环的更好方法

时间:2019-05-18 06:51:53

标签: elixir

请提出将下面的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);

可以通过获得好处的长生不老药并发实现吗?

编辑: 有一点背景,我对长生不老药还是新鲜的,仍然在学习。该问题的主要动机是编写比应用并发更多的惯用e剂代码。

2 个答案:

答案 0 :(得分:7)

要完全实现所写内容的最简单方法是使用for宏:

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

编辑:

: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 这种明显的循环无法通过并发来改善,通常情况下,很难说进程(aka并行化)是否会有所帮助。记住parallel != concurrent也是非常重要的,因此,在具有N调度程序(默认为CPU数量)的计算机上运行N-1 Erlang的进程不会使您加速。

答案 1 :(得分:1)

以下是一个有效的示例,说明了如何实现并发性,实现了1000000000个增量操作,以防万一,您可能会对如何做到这一点感到好奇。

下面的代码产生与您的外循环相对应的100个Elixir进程。因此,内部代码-这两个嵌套循环-(使用Enum.reduce see以更惯用的形式编写)同时运行(由VM尽可能地执行)。每个过程的结果都发送到专用的接收器过程,每当接收到新结果时,该过程将从100开始递减计数。将每个小计加到总计中,然后在收到100个小计时将其打印出来。 测试:将代码另存为文件nested.ex,并使用c nested.ex在Elixir Shell中进行编译。使用Main.main在该shell中运行它。您应该看到以下输出:

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

oktotal快几秒钟。您还应该体验较高的cpu多核使用率。

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 see docs

,可以使

相对于纯并发获得并行优势来获得优势。

非正式速度测试 在我的Win10 PC上测试报告:

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。