詳解F#版本的CodeTimer方法實現(xiàn)
對于F#這個微軟的新丁,很多人并不熟悉。很多開發(fā)人員知道函數(shù)式編程方面Scala可以算一個,但是不知道F#中CodeTimer妙用。本文借用作者的文章,希望能讓大家對F#有更深刻的了解。
#T#
CodeTimer很好用,自從在今年三月在.NET技術(shù)大會上看到Jeffrey Richter用類似的東西之后,我就自己寫了一個。不過,當(dāng)時是用C#寫的,現(xiàn)在我需要在F#里做相同的事情就不那么方便了。當(dāng)然,F(xiàn)#與.NET本是無縫集成,因此C#寫的CodeTimer也應(yīng)該可以被F#使用。不過,我平時在使用CodeTimer時并不是通過程序集引用,而是使用代碼復(fù)制的方式,因此如果有個F#版本那么應(yīng)該使用起來更加方便。
代碼如下:
- #light
- module CodeTimer
- open System
- open System.Diagnostics
- open System.Threading
- open System.Runtime.InteropServices
- [<DllImport("kernel32.dll")>]
- extern int QueryThreadCycleTime(IntPtr threadHandle, uint64* cycleTime)
- [<DllImport("kernel32.dll")>]
- extern IntPtr GetCurrentThread();
- let private getCycleCount() =
- let mutable cycle = 0UL
- let threadHandle = GetCurrentThread()
- QueryThreadCycleTime(threadHandle, &&cycle) |> ignore
- cycle
- let time name iteration action =
- if (String.IsNullOrEmpty(name)) then ignore 0 else
- // keep current color
- let currentForeColor = Console.ForegroundColor
- Console.ForegroundColor <- ConsoleColor.Yellow
- printfn "%s" name
- // keep current gc count
- GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
- let gcCounts =
- [0 .. GC.MaxGeneration]
- |> List.map (fun i -> (i, GC.CollectionCount(i)))
- |> List.fold (fun acc i -> i :: acc) []
- |> List.rev
- // keep cycle count and start watch
- let threadPtr = GetCurrentThread()
- let cycle = getCycleCount()
- let watch = Stopwatch.StartNew()
- // run
- for i = 1 to iteration do action();
- let cycleUsed = getCycleCount() - cycle
- watch.Stop()
- // restore the color
- Console.ForegroundColor <- currentForeColor;
- watch.ElapsedMilliseconds.ToString("N0") |> printfn "\tTime Elapsed:\t%sms"
- cycle.ToString("N0") |> printfn "\tCPU Cycles:\t%s"
- gcCounts |> List.iter (fun (i, c) ->
- printfn "\tGen%i:\t\t%i" i (GC.CollectionCount(i) - c))
- printfn ""
- let initialize() =
- Process.GetCurrentProcess().PriorityClass <- ProcessPriorityClass.High
- Thread.CurrentThread.Priority <- ThreadPriority.Highest
- time "" 0 (fun() -> ignore 0)
結(jié)果是:
- Wait
- Time Elapsed: 684ms
- CPU Cycles: 372,709,908
- Gen0: 0
- Gen1: 0
- Gen2: 0
與C#版本的CodeTimer相比,第一版的F# CodeTimer少算了CPU使用周期的消耗——不是我不想,而是遇到了問題。我當(dāng)時這樣引入P/Invoke的簽名:
- open System.Runtime.InteropServices
- [<DllImport("kernel32.dll")>]
- extern int QueryThreadCycleTime(IntPtr threadHandle, uint32* cycleTime)
- [<DllImport("kernel32.dll")>]
- extern IntPtr GetCurrentThread();
F#在P/Invoke簽名中使用*來標(biāo)記out參數(shù),但是在自定義方法時使用的是byref,這點與C#不同,后者都是使用ref。這個引入看似沒有問題,而且普通調(diào)用也能得到正常結(jié)果:
- [<EntryPoint>]
- let main args =
但是,一旦我把它加為CodeTimer的一個方法,如getCycleCount:
- let getCycleCount() =
- let mutable cycle = 0u
- let threadHandle = GetCurrentThread()
- QueryThreadCycleTime(threadHandle, &&cycle) |> ignore
- cycle
這樣調(diào)用的時候就會拋出異常:
后經(jīng)alonesail同學(xué)指出,引入QueryThreadCycleTime的時候,第二個參數(shù)應(yīng)該是64位而不是32位無符號整數(shù)——我將PULONG64看作PULONG了。改成uint64便沒有問題了。
原文標(biāo)題:F#版本的CodeTimer(已支持CPU時鐘周期統(tǒng)計)
鏈接:http://www.cnblogs.com/JeffreyZhao/archive/2009/11/13/fsharp-codetimer.html