Erlang'eの初心者向けMapReduce

私は引き続きアーランに飛び込みます。 Erlangで監視するためのサービスの1つを書き換えるには、すでに難しい計画があります。 ここでは、一部の製品およびQAなどの内部タスクのプラットフォームとしてWindows AzureおよびAmazon EC2クラウドを開発しているため、コードを書き換えずに多くのコアとマシンを使用できる可能性があります。



したがって、初心者にとっては、シンプルだが実際の例は〜2000ファイルプロジェクトです。 使用される環境変数のリストを作成する必要があります。 つまり、行 "getenv(...)"および "GetVariable(...)"(これがラッパーです)の出現箇所を見つけて、それらからパラメーターを削除します。



タスクは簡単で、ディレクトリトラバーサルさえ行わないC ++プログラムによって長い間解決されてきましたが、Unixの「find」を呼び出すだけで、マスクによってファイルのリストを生成し、リスト上のファイルをシャベルします。 2000ファイルでは、1つのスレッドで数秒間動作します。



さあ、アーラン。 ここで、シーケンシャルなファイルトラバーサルよりもカーリーなものをかき立てたいと思います。 MapReduceはトピックに関するものです-ファイルのリストを作成し、各ファイルを並行して分析し(Map)、見つかった変数名を蓄積し、最後に受信したすべてのエントリを処理します(Reduce)。この場合、各変数の出現回数をカウントします。



実際、私のコードは「 Programming Erlang 」の例を繰り返し、同じ本のphofs(並列高階関数)モジュールを使用しています。



-module(find_variables). -export([main/0, find_variables_in_file/2, process_found_variables/3]). -define(PATH, "/Projects/interesting_project"). -define(MASK, "\\..*(cpp|c)"). main() -> io:format("Creating list of files...~n", []), %     .   - % ,    . Files = filelib:fold_files(?PATH, ?MASK, true, fun(N, A) -> [N | A] end, []), io:format("Found ~b file(s)~n", [length(Files)]), F1 = fun find_variables_in_file/2, % Map F2 = fun process_found_variables/3, % Reduce %  MapReduce   benchmark,   % . benchmark(fun() -> L = phofs:mapreduce(F1, F2, [], Files), io:format("Found ~b variable(s)~n", [length(L)]) end, "MapReduce"). benchmark(Worker, Title) -> {T, _} = timer:tc(fun() -> Worker() end), io:format("~s: ~f sec(s)~n", [Title, T/1000000]). -define(REGEXP, "(getenv|GetVariable)\s*\\(\s*\"([^\"]+)\"\s*\\)"). % Map.   . find_variables_in_file(Pid, FileName) -> case file:open(FileName, [read]) of {ok, File} -> %    . {_, RE} = re:compile(?REGEXP), %       %      . CallBack = fun(Var) -> Pid ! {Var, 1} end, find_variable_in_file(File, RE, CallBack), file:close(File); {error, Reason} -> io:format("Unable to process '~s', ~p~n", [FileName, Reason]), exit(1) end. % Reduce.  .     %  MapReduce        % ,   .       % {VarName, 1}.      VarName  %  ,       . %      . process_found_variables(Key, Vals, A) -> [{Key, length(Vals)} | A]. %   . find_variable_in_file(File, RE, CallBack) -> case io:get_line(File, "") of eof -> void; Line -> scan_line_in_file(Line, RE, CallBack), find_variable_in_file(File, RE, CallBack) end. %        ( ), %      CallBack      % . scan_line_in_file(Line, RE, CallBack) -> case re:run(Line, RE) of {match, Captured} -> [_, _, {NameP, NameL}] = Captured, Name = string:substr(Line, NameP + 1, NameL), CallBack(Name); nomatch -> void end.
      
      





プログラムをビルドするには、 phofsモジュールが必要です 。 これは、特定のMapおよびReduce機能とは独立した、普遍的なものです。



念のため、Makefile:



  target = find_variables all: erlc $(target).erl erlc phofs.erl erl -noshell -s $(target) main -s init stop clean: -rm *.beam *.dump
      
      







大きく。 すでに述べたように、C ++プログラムは、マシン上の「find」呼び出しの時間とともに1〜2秒間実行されます。 Erlang'eバージョンは約20秒動作します。 悪い? それはあなたがどのように見えるかに依存します。 各ファイルの分析が長くなる場合(つまり、プログラムがディレクトリのクロールではなくファイルの分析にほとんどの時間を費やす場合)、ファイルの数と分析の複雑さを増やす場合に、どのソリューションがより実用的かは完全には明らかではありません。



私はErlangが初めてなので、コードの批判に感謝します。



関連記事:




All Articles