Reputation: 790
Lately, I wanted to optimize my OCaml program. Someone suggested me to do a perf
analysis.
And well, I was surprised when I saw that Sys.time()
is the function which takes the biggest amount of time during my program, because I call it every loop.
I'll try to call it less often, but I was wondering, does it exist a more efficient way to force a program to return after a certain amount of time ?
Maybe something more efficient than Sys.time
to get the current processing time, or something completely different.
Upvotes: 0
Views: 430
Reputation: 66823
It looks like you're running on a Windows system. I don't have access to Windows, but on my Mac, Unix.gettimeofday
is much faster than Sys.time
. It returns the wall clock time rather than processing time, but in fact I usually want wall clock time. I very rarely am interested in the specific number of CPU cycles that have been spent on the code.
Here's a test program I ran on my 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)
Output:
$ ./m
Sys.time 4.060067
Unix.gettimeofday 0.320934
In a Linux environment, there are similar results (contributed by @Butanium):
Sys.time 4.097320
Unix.gettimeofday 0.508475
Here are my results from Ubuntu 18.04.4
Sys.time 6.156210
Unix.gettimeofday 0.309533
(This is a small NUC system adorned with a picture of a skull.)
The summary is that the times are different on different systems but gettimeofday is a lot faster.
Upvotes: 2
Reputation: 35270
You can use a more efficient Unix.gettimeofday
function as Jeffrey suggested, but in addition to that, you can also try to make fewer comparisons.
First of all, you can check the time only every thousand (or more, or less, pick the right number) iterations, e.g.,
if i mod 10000 = 0 && check_timeout ()
then do_work ()
else raise Timeout
Alternatively, if your code allocates and you are not really bothered with exact times in your timeout, (i.e., you can wait a bit more than the selected timeout, but would like to run your function at least the specified amount of time), then you can set an alarm. It will be checked every major collection, and if your code doesn't allocate a lot1), it might take some time before it triggers, e.g.,
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
Another option that works on Unix systems and still requires allocation inside of the loop, is to rely on the Unix alarm signal. This is the most performant option, but it suffers from a few problems, like portability and reliability (it won't work if someone else will decide to use the alarm signal).
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) This example doesn't allocate much and it works fine with timeouts that are a matter of dozens of seconds. If the timeout is less than 10 seconds, it runs more than the specified duration as it usually takes a few seconds before the first major collection kicks in.
Upvotes: 2