优化 OCaml 中的文件写入操作?
Optimize a file writing operation in OCaml?
基本上在我的项目中,我试图将字符串列表写入文件,如下所示:
val mutable rodata_list : (string*string) list = []
.....
let zip1 ll =
List.map (fun (h,e) -> h^e) ll in
let oc = open_out_gen [Open_append; Open_creat] 0o666 "final_data.s" in
List.iter (fun l -> Printf.fprintf oc "%s\n" l) (zip1 rodata_list);
这是我的问题,通常rodata_list
可以达到800,000大小,上面的代码在我们的服务器(64-bit, 32 core Intel(R) Xeon(R) CPU E5-2690 0 @ 2.90GHz
)上大约需要3.5秒.. OCaml
我用的版本是4.01.0
.
这是不可接受的,尤其是当我有 4 段这样的代码要写入一个文件时。完全他们可以带我超过 15 秒..
我试过这个:
Printf.fprintf oc "%s\n" (String.concat "\n" (zip1 rodata_list));
但没有明显改善..
所以我想知道,如何优化这部分?我感谢任何解决方案。谢谢!
- 不要在性能关键代码中使用
^
连接一堆字符串,因为这会导致二次复杂度;
- 尽量不要依赖
*printf
函数,当性能很重要时(尽管在 OCaml 4.02 中它非常快);
- 不要连续对一个列表应用多次迭代,因为 OCaml 没有森林砍伐。尝试在一次迭代中做尽可能多的操作;
- 如果您使用的是包含 100 万个元素的列表,那么您实际上做错了什么。尝试使用不同的数据结构;
因此,根据上述建议,我们有以下建议:
List.iter (fun (x,y) ->
output_string oc x;
output_string oc y;
output_char oc '\n') rodata_list
此外,任何优化都应该从分析开始,为了获得您需要的配置文件,您需要使用分析信息对其进行编译,例如:
ocamlbuild myprogram.p.native
然后你可以运行程序来收集配置文件,可以用gprof
读取。我的猜测是,您不会将所有时间花在实际的 IO 上,甚至不会花在连接上,而是花在垃圾收集上,因为您的 zip 将创建数百万个字符串。
应该多快
为了证明您实际上是在尝试优化代码的错误部分,我编写了这个小程序:
let rec init_rev acc = function
| 0 -> acc
| n -> init_rev (("hello", "world") :: acc) (n-1)
let () = List.iter (fun (x,y) ->
print_string x;
print_endline y) (init_rev [] 1000_000)
它创建一个包含一百万个元素的列表并输出它:
$ ocamlbuild main.native
$ time ./main.native > data.txt
real 0m0.998s
user 0m0.211s
sys 0m0.783s
这是在 macbook 笔记本电脑上。此外,我们大部分时间都花在系统上,在 OCaml 中只有 200 毫秒。在不创建列表的情况下进行 1000_000 次迭代的简单循环仅需 11 毫秒。
所以,简介。
基本上在我的项目中,我试图将字符串列表写入文件,如下所示:
val mutable rodata_list : (string*string) list = []
.....
let zip1 ll =
List.map (fun (h,e) -> h^e) ll in
let oc = open_out_gen [Open_append; Open_creat] 0o666 "final_data.s" in
List.iter (fun l -> Printf.fprintf oc "%s\n" l) (zip1 rodata_list);
这是我的问题,通常rodata_list
可以达到800,000大小,上面的代码在我们的服务器(64-bit, 32 core Intel(R) Xeon(R) CPU E5-2690 0 @ 2.90GHz
)上大约需要3.5秒.. OCaml
我用的版本是4.01.0
.
这是不可接受的,尤其是当我有 4 段这样的代码要写入一个文件时。完全他们可以带我超过 15 秒..
我试过这个:
Printf.fprintf oc "%s\n" (String.concat "\n" (zip1 rodata_list));
但没有明显改善..
所以我想知道,如何优化这部分?我感谢任何解决方案。谢谢!
- 不要在性能关键代码中使用
^
连接一堆字符串,因为这会导致二次复杂度; - 尽量不要依赖
*printf
函数,当性能很重要时(尽管在 OCaml 4.02 中它非常快); - 不要连续对一个列表应用多次迭代,因为 OCaml 没有森林砍伐。尝试在一次迭代中做尽可能多的操作;
- 如果您使用的是包含 100 万个元素的列表,那么您实际上做错了什么。尝试使用不同的数据结构;
因此,根据上述建议,我们有以下建议:
List.iter (fun (x,y) ->
output_string oc x;
output_string oc y;
output_char oc '\n') rodata_list
此外,任何优化都应该从分析开始,为了获得您需要的配置文件,您需要使用分析信息对其进行编译,例如:
ocamlbuild myprogram.p.native
然后你可以运行程序来收集配置文件,可以用gprof
读取。我的猜测是,您不会将所有时间花在实际的 IO 上,甚至不会花在连接上,而是花在垃圾收集上,因为您的 zip 将创建数百万个字符串。
应该多快
为了证明您实际上是在尝试优化代码的错误部分,我编写了这个小程序:
let rec init_rev acc = function
| 0 -> acc
| n -> init_rev (("hello", "world") :: acc) (n-1)
let () = List.iter (fun (x,y) ->
print_string x;
print_endline y) (init_rev [] 1000_000)
它创建一个包含一百万个元素的列表并输出它:
$ ocamlbuild main.native
$ time ./main.native > data.txt
real 0m0.998s
user 0m0.211s
sys 0m0.783s
这是在 macbook 笔记本电脑上。此外,我们大部分时间都花在系统上,在 OCaml 中只有 200 毫秒。在不创建列表的情况下进行 1000_000 次迭代的简单循环仅需 11 毫秒。
所以,简介。