【问题标题】:How to efficiently stop a function after a certain amount of time in OCaml如何在 OCaml 中经过一定时间后有效地停止函数
【发布时间】:2022-02-04 05:48:29
【问题描述】:

最近,我想优化我的 OCaml 程序。有人建议我做一个perf 分析。

好吧,当我看到Sys.time() 是在我的程序中花费最多时间的函数时,我感到很惊讶,因为我在每个循环中都调用它。

我会尽量少调用它,但我想知道,是否存在一种更有效的方法来强制程序在一定时间后返回?

也许比Sys.time 更有效地获取当前处理时间,或者完全不同的东西。

【问题讨论】:

    标签: time ocaml


    【解决方案1】:

    您可以按照 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 秒,则它会运行超过指定的持续时间,因为通常需要几秒钟才能启动第一个主要收集。

    【讨论】:

      【解决方案2】:

      您似乎在 Windows 系统上运行。我无法访问 Windows,但在我的 Mac 上,Unix.gettimeofdaySys.time 快得多。它返回挂钟时间而不是处理时间,但实际上我通常想要挂钟时间。我很少对代码上花费的具体 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快很多。

      【讨论】:

      • 我实际上在 Linux 机器上运行了我的测试,但我会尝试Unix.gettimeofday
      • 太棒了。 “.exe”后缀让我很困惑。我有很多 Linux 系统可以尝试,但应该和 macOS 差不多。
      猜你喜欢
      • 2018-08-28
      • 2011-05-14
      • 1970-01-01
      • 1970-01-01
      • 2012-02-21
      • 1970-01-01
      • 2019-06-28
      • 2013-11-05
      • 2013-03-30
      相关资源
      最近更新 更多