2014年11月29日土曜日

[OCaml]XML Parser PXPを使ってみた(Bufferモジュールを利用)

前記事の、E_char_dataイベントで文字列を ^ で連結するやり方は効率が悪すぎた。

ブログ更新をtweetしていたら、@camloebaさんに Bufferがあることを教えていただいた。
いつもありがとうございます!

ということで、Bufferを使うように修正してみた。

修正後のプログラム

修正したのはparse関数だけ。
let parse element_func_list =
  let tags = Stack.create () in
  let element_func () =
    let item = List.find element_func_list ~f:(fun (element_list, _) ->
        Stack.to_list tags = element_list
      ) in
    match item with
      Some (_, func) -> Some func
    | None -> None in
  let in_content = ref false in
  let content = Buffer.create 1024 in
  let apply_func = ref (fun _ -> ()) in
  Pxp_ev_parser.process_entity config entry entmng (fun ev ->
      (* let event_string = Pxp_event.string_of_event ev in *)
      match ev with
        Pxp_types.E_start_tag (name, _, _, _) -> begin
          Stack.push tags name;
          (* print_endline @@ List.to_string (Stack.to_list tags) ~f:ident; *)
          match element_func () with
            Some func' -> begin
              apply_func := func';
              in_content := true
            end
          | _ -> ()
        end
      | Pxp_types.E_end_tag (name, _) -> begin
          ignore @@ Stack.pop_exn tags;
          if !in_content then
            begin
              !apply_func @@ Buffer.contents content;
              Buffer.clear content;
              in_content := false;
            end;
        end
      | Pxp_types.E_char_data str -> begin
          if !in_content then
            Buffer.add_string content str
        end
      | _ -> ())
このような修正は、動的な型の言語の場合、必要な修正箇所を見逃しやすいが、 OCamlのような静的な型の言語の場合は、コンパイルエラーで検出できるので非常にやりやすい。
Emacs+Tuaregでコード書いており、保存と同時にエラーがある行の色が変わるので、修正箇所は一目瞭然。

実行結果

satoshi@xubuntu:~/workspace/ocaml-try/pxp$ time ./parse_wiki > /dev/null

real    0m0.093s
user    0m0.081s
sys     0m0.011s
修正前は、
satoshi@xubuntu:~/workspace/ocaml-try/pxp$ time ./parse_wiki > /dev/null

real    0m3.361s
user    0m3.168s
sys     0m0.180s
およそ35倍速くなった!
これなら全く問題なし。
計測していないが、メモリの使用量も、かなり減ったはず。

なお、^による文字列連結に関しては、 Real World OCaml Chapter 3. Lists and Patternsの Performance of String.concat and ^ にも書かれていた。
^ を使うたびに文字列を生成するので、少しずつ文字列を連結して、大きな文字列を作る場合は、非常にパフォーマンスが悪い。