如何在 OCaml 中经过一定时间后有效地停止函数
How to efficiently stop a function after a certain amount of time in OCaml
最近,我想优化我的 OCaml 程序。有人建议我做一个 perf
分析。
好吧,当我看到 Sys.time()
是在我的程序中花费最多时间的函数时,我感到很惊讶,因为我在每个循环中都调用它。
我会尽量减少调用它的次数,但我想知道,是否存在一种更有效的方法来强制程序在一定时间后 return?
也许比 Sys.time
更有效地获取当前处理时间,或者完全不同的东西。
看起来您 运行 在 Windows 系统上。我无法访问 Windows,但在我的 Mac 上,Unix.gettimeofday
比 Sys.time
快得多。它 returns 挂钟时间而不是处理时间,但实际上我通常想要挂钟时间。我很少对在代码上花费的 CPU 个周期的具体数量感兴趣。
这是我 运行 在 Mac 上的测试程序:
let start_time = Unix.gettimeofday () in
for i = 1 to 10000000 do
ignore (Sys.time ())
done;
let end_time = Unix.gettimeofday () in
Printf.printf "Sys.time %f\n" (end_time -. start_time);
let start_time = Unix.gettimeofday () in
for i = 1 to 10000000 do
ignore (Unix.gettimeofday ())
done;
let end_time = Unix.gettimeofday () in
Printf.printf "Unix.gettimeofday %f\n" (end_time -. start_time)
输出:
$ ./m
Sys.time 4.060067
Unix.gettimeofday 0.320934
在 Linux 环境中,有类似的结果(由@Butanium 贡献):
Sys.time 4.097320
Unix.gettimeofday 0.508475
这是我在 Ubuntu 18.04.4
的结果
Sys.time 6.156210
Unix.gettimeofday 0.309533
(这是一个装饰有头骨图片的小型 NUC 系统。)
总结一下,不同系统的时间不同,但是gettimeofday要快很多。
您可以按照 Jeffrey 的建议使用更高效的 Unix.gettimeofday
函数,但除此之外,您还可以尝试进行更少的比较。
首先,你可以每千次(或更多,或更少,选择正确的数字)迭代检查时间,例如,
if i mod 10000 = 0 && check_timeout ()
then do_work ()
else raise Timeout
或者,如果您的代码进行了分配,并且您并不真正在意超时的确切时间(即,您可以等待比所选超时时间长一点的时间,但希望 运行 您的函数位于至少指定的时间),然后您可以设置闹钟。它会检查每个主要的集合,如果你的代码没有分配很多1),它可能需要一些时间才能触发,例如
exception Timeout
let rec infinite () =
let x = Random.int64 0xDEADBEEFL in
Format.printf "%Ld\n" x;
infinite ()
let max_time = 60.
let () =
let start = Sys.time () in
let alarm = Gc.create_alarm (fun () ->
if Sys.time () -. start > max_time
then raise Timeout) in
let () = try infinite () with Timeout -> () in
Gc.delete_alarm alarm
另一个适用于 Unix 系统且仍然需要在循环内部分配的选项是依赖于 Unix 警报信号。这是性能最高的选项,但存在一些问题,例如便携性和可靠性(如果其他人决定使用警报信号,它将无法工作)。
let timeout = ref false
let rec infinite () =
if not timeout.contents then infinite ()
let () =
let stopit = Sys.Signal_handle (fun _ -> timeout := true) in
Sys.set_signal Sys.sigalrm stopit;
ignore (Unix.alarm 10);
infinite ();
1) 这个例子并没有分配多少东西,而且在几十秒的超时情况下也能正常工作。如果超时小于 10 秒,它会比指定的持续时间长 运行s,因为在第一个主要收集开始之前通常需要几秒钟。
最近,我想优化我的 OCaml 程序。有人建议我做一个 perf
分析。
好吧,当我看到 Sys.time()
是在我的程序中花费最多时间的函数时,我感到很惊讶,因为我在每个循环中都调用它。
我会尽量减少调用它的次数,但我想知道,是否存在一种更有效的方法来强制程序在一定时间后 return?
也许比 Sys.time
更有效地获取当前处理时间,或者完全不同的东西。
看起来您 运行 在 Windows 系统上。我无法访问 Windows,但在我的 Mac 上,Unix.gettimeofday
比 Sys.time
快得多。它 returns 挂钟时间而不是处理时间,但实际上我通常想要挂钟时间。我很少对在代码上花费的 CPU 个周期的具体数量感兴趣。
这是我 运行 在 Mac 上的测试程序:
let start_time = Unix.gettimeofday () in
for i = 1 to 10000000 do
ignore (Sys.time ())
done;
let end_time = Unix.gettimeofday () in
Printf.printf "Sys.time %f\n" (end_time -. start_time);
let start_time = Unix.gettimeofday () in
for i = 1 to 10000000 do
ignore (Unix.gettimeofday ())
done;
let end_time = Unix.gettimeofday () in
Printf.printf "Unix.gettimeofday %f\n" (end_time -. start_time)
输出:
$ ./m
Sys.time 4.060067
Unix.gettimeofday 0.320934
在 Linux 环境中,有类似的结果(由@Butanium 贡献):
Sys.time 4.097320
Unix.gettimeofday 0.508475
这是我在 Ubuntu 18.04.4
的结果Sys.time 6.156210
Unix.gettimeofday 0.309533
(这是一个装饰有头骨图片的小型 NUC 系统。)
总结一下,不同系统的时间不同,但是gettimeofday要快很多。
您可以按照 Jeffrey 的建议使用更高效的 Unix.gettimeofday
函数,但除此之外,您还可以尝试进行更少的比较。
首先,你可以每千次(或更多,或更少,选择正确的数字)迭代检查时间,例如,
if i mod 10000 = 0 && check_timeout ()
then do_work ()
else raise Timeout
或者,如果您的代码进行了分配,并且您并不真正在意超时的确切时间(即,您可以等待比所选超时时间长一点的时间,但希望 运行 您的函数位于至少指定的时间),然后您可以设置闹钟。它会检查每个主要的集合,如果你的代码没有分配很多1),它可能需要一些时间才能触发,例如
exception Timeout
let rec infinite () =
let x = Random.int64 0xDEADBEEFL in
Format.printf "%Ld\n" x;
infinite ()
let max_time = 60.
let () =
let start = Sys.time () in
let alarm = Gc.create_alarm (fun () ->
if Sys.time () -. start > max_time
then raise Timeout) in
let () = try infinite () with Timeout -> () in
Gc.delete_alarm alarm
另一个适用于 Unix 系统且仍然需要在循环内部分配的选项是依赖于 Unix 警报信号。这是性能最高的选项,但存在一些问题,例如便携性和可靠性(如果其他人决定使用警报信号,它将无法工作)。
let timeout = ref false
let rec infinite () =
if not timeout.contents then infinite ()
let () =
let stopit = Sys.Signal_handle (fun _ -> timeout := true) in
Sys.set_signal Sys.sigalrm stopit;
ignore (Unix.alarm 10);
infinite ();
1) 这个例子并没有分配多少东西,而且在几十秒的超时情况下也能正常工作。如果超时小于 10 秒,它会比指定的持续时间长 运行s,因为在第一个主要收集开始之前通常需要几秒钟。