Elixir分散コンピューティング-古典的なMapReduceの例

Elixirの分散コンピューティング







ElixirとErlangは、いくつかの、おそらく類似のタスクを並行して実行する分散アプリケーションの作成に最適です。 Erlang仮想マシンを開発する際の主要な側面の1つは、単独で動作する競合プロセスの多くをサポートすることでした。







この機会をテストして、簡単な例を使用してマルチコアプロセッサの可能性を活用してみます。 1つのディレクトリ内のテキストファイルに配置された作家O. Henryの物語に「馬」という単語が何回現れるかを計算します。 技術的には、単語ではなく、「horse」という文字列の出現回数をカウントします。小文字のみです。









ファイル内の部分文字列の出現をカウントする



テキストファイルのコンテンツ内の部分文字列の出現回数カウントする機能から始めましょう。







word_count = fn(file, word) -> {:ok, content} = File.read(file) length(String.split(content, word)) - 1 end
      
      





ファイルの内容を読み取り、単語への参照の数を返します。 簡単にするため、エラー処理は省略されています。







関数に1秒の遅延を追加し、カウントの結果を返す前にコンソールに出力します。







 word_count = fn(file, word) -> :timer.sleep(1000) {:ok, content} = File.read(file) count = length(String.split(content, word)) - 1 IO.puts "Found #{inspect count} occurrence(s) of the word in file #{inspect file}" count end
      
      





次に、各ファイルの部分文字列の数を計算し、合計を出力します。







 Path.wildcard("/data/OGENRI/*.txt") |> Enum.map(fn(file) -> word_count.(file, "") end) |> Enum.reduce(fn(x, acc) -> acc + x end) |> IO.puts
      
      





同時に、プログラム全体の実行時間を測定します。







 # sync_word_count.exs start_time = :os.system_time(:milli_seconds) word_count = fn(file, word) -> :timer.sleep(1000) {:ok, content} = File.read(file) count = length(String.split(content, word)) - 1 IO.puts "Found #{inspect count} occurrence(s) of the word in file #{inspect file}" count end Path.wildcard("/data/OGENRI/*.txt") |> Enum.map(fn(file) -> word_count.(file, "") end) |> Enum.reduce(fn(x, acc) -> acc + x end) |> IO.puts end_time = :os.system_time(:milli_seconds) IO.puts "Finished in #{(end_time - start_time) / 1000} seconds"
      
      





合計で12個のファイルがあり、約12秒待機しなければなりませんでした。各ファイルのカウント結果がモニターに表示されるので、1秒ごとに検討しました。







 iex sync_word_count.exs Erlang/OTP 18 [erts-7.3] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] [dtrace] Found 0 occurrence(s) of the word in file "/data/OGENRI/businessmen.txt" Found 1 occurrence(s) of the word in file "/data/OGENRI/choose.txt" Found 0 occurrence(s) of the word in file "/data/OGENRI/four.txt" Found 1 occurrence(s) of the word in file "/data/OGENRI/light.txt" Found 10 occurrence(s) of the word in file "/data/OGENRI/prevr.txt" Found 0 occurrence(s) of the word in file "/data/OGENRI/r_dl.txt" Found 1 occurrence(s) of the word in file "/data/OGENRI/r_linii.txt" Found 10 occurrence(s) of the word in file "/data/OGENRI/r_sixes.txt" Found 9 occurrence(s) of the word in file "/data/OGENRI/serdce.txt" Found 0 occurrence(s) of the word in file "/data/OGENRI/stihi.txt" Found 0 occurrence(s) of the word in file "/data/OGENRI/voice.txt" Found 0 occurrence(s) of the word in file "/data/OGENRI/ways.txt" 32 Finished in 12.053 seconds Interactive Elixir (1.3.1) - press Ctrl+C to exit (type h() ENTER for help)
      
      





非同期タスク実行



部分文字列の出現回数をカウントするには、 spawn



プロセス作成メソッドとsend



メソッドとreceive



メソッドを使用して、それぞれメッセージをreceive



します。







ファイルごとに個別のプロセスを作成します







 async_word_count = fn(file, word) -> caller = self spawn(fn -> send(caller, {:result, word_count.(file, word)}) end) end
      
      





self



は現在のプロセスです。 self



と同じ値でcaller



変数を作成します。 生成されたプロセスはword_count/2



関数を呼び出し、結果を親プロセスに送り返します。







逆の方法で値を取得するには、親プロセスでreceive



を使用する必要があります(プロセスがある限り)。 このためにget_result/0



メソッドを作成しましょう。







 get_result = fn -> receive do {:result, result} -> result end end
      
      





プログラムを更新します。







 # async_word_count.exs start_time = :os.system_time(:milli_seconds) word_count = fn(file, word) -> :timer.sleep(1000) {:ok, content} = File.read(file) count = length(String.split(content, word)) - 1 IO.puts "Found #{inspect count} occurrence(s) of the word in file #{inspect file}" count end async_word_count = fn(file, word) -> caller = self spawn(fn -> send(caller, {:result, word_count.(file, word)}) end) end get_result = fn -> receive do {:result, result} -> result end end Path.wildcard("/data/OGENRI/*.txt") |> Enum.map(fn(file) -> async_word_count.(file, "") end) |> Enum.map(fn(_) -> get_result.() end) |> Enum.reduce(fn(x, acc) -> acc + x end) |> IO.puts end_time = :os.system_time(:milli_seconds) IO.puts "Finished in #{(end_time - start_time) / 1000} seconds"
      
      





ご覧ください。







 iex async_word_count.exs Erlang/OTP 18 [erts-7.3] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] [dtrace] Found 9 occurrence(s) of the word in file "/data/OGENRI/serdce.txt" Found 0 occurrence(s) of the word in file "/data/OGENRI/businessmen.txt" Found 0 occurrence(s) of the word in file "/data/OGENRI/four.txt" Found 1 occurrence(s) of the word in file "/data/OGENRI/choose.txt" Found 1 occurrence(s) of the word in file "/data/OGENRI/light.txt" Found 10 occurrence(s) of the word in file "/data/OGENRI/prevr.txt" Found 1 occurrence(s) of the word in file "/data/OGENRI/r_linii.txt" Found 10 occurrence(s) of the word in file "/data/OGENRI/r_sixes.txt" Found 0 occurrence(s) of the word in file "/data/OGENRI/stihi.txt" Found 0 occurrence(s) of the word in file "/data/OGENRI/voice.txt" Found 0 occurrence(s) of the word in file "/data/OGENRI/ways.txt" Found 0 occurrence(s) of the word in file "/data/OGENRI/r_dl.txt" 32 Finished in 1.014 seconds Interactive Elixir (1.3.1) - press Ctrl+C to exit (type h() ENTER for help)
      
      





おわりに



冒険と馬はかつて切り離せないものでしたが、今ではおそらくそうではありません。







参照資料



» Http://elixir-lang.org/getting-started/processes.html

» Http://culttt.com/2016/07/27/understanding-concurrency-parallelism-elixir/

» Https://elixirschool.com/lessons/advanced/concurrency/

» コードおよびテキストファイル(OGENRIフォルダー)








All Articles