简体   繁体   中英

how to time an arbitrary function in f#

here's the problem. I need to time a function in f# using another function. I have this piece of code

let time f a =
  let start = System.DateTime.Now in
  let res = (fun f a -> f(a)) in
  let finish = System.DateTime.Now in
  (res, finish - start)

which I'm trying to call saying

time ackermann (2,9);;

I have a function ackermann that takes a tuple (s,n) as argument Probably something fundamentally wrong with this but I don't think I'm far away from a solution that could and looks somewhat like this.

Any suggestions?

Oh btw. the error message I'm getting is saying :

stdin(19,1): error FS0030: Value restriction. The value 'it' has been inferred to have generic type val it : (('_a -> '_b) -> '_a -> '_b) * System.TimeSpan
Either define 'it' as a simple data term, make it a function with explicit arguments or, if you do not intend for it to be generic, add a type annotation.

You have at least two issues:

  1. Try let res = fa . You already have values f and a in scope, but you're currently defining res as a function which takes a new f and applies it to a new a .
  2. Don't use DateTime s (which are appropriate for representing dates and times, but not short durations). Instead, you should be using a System.Diagnostics.Stopwatch .

You can do something like this:

let time f =
  let sw = System.Diagnostics.Stopwatch.StartNew()
  let r = f()
  sw.Stop()
  printfn "%O" sw.Elapsed
  r

Usage

time (fun () -> System.Threading.Thread.Sleep(100))

I usually keep the following in my code files when sending a bunch of stuff to fsi.

#if INTERACTIVE
#time "on"
#endif

That turns on fsi's built-in timing, which provides more than just execution time:

Real: 00:00:00.099, CPU: 00:00:00.000, GC gen0: 0, gen1: 0, gen2: 0

I would do it like this:

let time f = fun a ->
    let start = System.DateTime.Now
    let res = f a
    (res, System.DateTime.Now - start)

You can then use it to create timed functions eg

let timedAckermann = time ackermann
let (res, period) = timedAckermann (2,9)

You should also consider using System.Diagnostics.Stopwatch for timing instead of DateTime s.

Inspired by how FSharp Interactive does it (see https://github.com/Microsoft/visualfsharp/blob/master/src/fsharp/fsi/fsi.fs#L175 ), this will time the function plus report how much CPU, allocation, etc.

Example output: Real: 00:00:00.2592820, CPU: 00:00:26.1814902, GC gen0: 30, gen1: 1, gen2: 0

  
  let time f =
      let ptime = System.Diagnostics.Process.GetCurrentProcess()
      let numGC = System.GC.MaxGeneration
      let startTotal = ptime.TotalProcessorTime
      let startGC = [| for i in 0 .. numGC -> System.GC.CollectionCount(i) |]
      let stopwatch = System.Diagnostics.Stopwatch.StartNew()
      let res = f ()
      stopwatch.Stop()
      let total = ptime.TotalProcessorTime - startTotal
      let spanGC = [ for i in 0 .. numGC-> System.GC.CollectionCount(i) - startGC.[i] ]
      let elapsed = stopwatch.Elapsed 
      printfn "Real: %A, CPU: %A, GC %s" elapsed total ( spanGC |> List.mapi (sprintf "gen%i: %i") |> String.concat ", ")
    res

As was already suggested, you should use Stopwatch instead of DateTime for this kind of timing analyses.

What wasn't mentioned yet is that if you for some reason need to use DateTime, then always consider using DateTime.UtcNow rather than DateTime.Now. The implementation of DateTime.Now can be paraphrased as "DateTime.UtcNow.ToLocalTime()", and that "ToLocalTime()" part is doing more than you might think it would do. In addition to having less overhead, DateTime.UtcNow also avoids headaches related to daylight savings time. You can find several articles and blog posts on the web on the differences between DateTime.Now and DateTime.UtcNow

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM