ブログ更新を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 ^ にも書かれていた。
^ を使うたびに文字列を生成するので、少しずつ文字列を連結して、大きな文字列を作る場合は、非常にパフォーマンスが悪い。