詳解F#版本的CodeTimer方法實(shí)現(xiàn)
對(duì)于F#這個(gè)微軟的新丁,很多人并不熟悉。很多開發(fā)人員知道函數(shù)式編程方面Scala可以算一個(gè),但是不知道F#中CodeTimer妙用。本文借用作者的文章,希望能讓大家對(duì)F#有更深刻的了解。
#T#
CodeTimer很好用,自從在今年三月在.NET技術(shù)大會(huì)上看到Jeffrey Richter用類似的東西之后,我就自己寫了一個(gè)。不過,當(dāng)時(shí)是用C#寫的,現(xiàn)在我需要在F#里做相同的事情就不那么方便了。當(dāng)然,F(xiàn)#與.NET本是無縫集成,因此C#寫的CodeTimer也應(yīng)該可以被F#使用。不過,我平時(shí)在使用CodeTimer時(shí)并不是通過程序集引用,而是使用代碼復(fù)制的方式,因此如果有個(gè)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)時(shí)這樣引入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ù),但是在自定義方法時(shí)使用的是byref,這點(diǎn)與C#不同,后者都是使用ref。這個(gè)引入看似沒有問題,而且普通調(diào)用也能得到正常結(jié)果:
- [<EntryPoint>]  
 - let main args =  
 
但是,一旦我把它加為CodeTimer的一個(gè)方法,如getCycleCount:
- let getCycleCount() =
 - let mutable cycle = 0u
 - let threadHandle = GetCurrentThread()
 - QueryThreadCycleTime(threadHandle, &&cycle) |> ignore
 - cycle
 
這樣調(diào)用的時(shí)候就會(huì)拋出異常:

后經(jīng)alonesail同學(xué)指出,引入QueryThreadCycleTime的時(shí)候,第二個(gè)參數(shù)應(yīng)該是64位而不是32位無符號(hào)整數(shù)——我將PULONG64看作PULONG了。改成uint64便沒有問題了。
原文標(biāo)題:F#版本的CodeTimer(已支持CPU時(shí)鐘周期統(tǒng)計(jì))
鏈接:http://www.cnblogs.com/JeffreyZhao/archive/2009/11/13/fsharp-codetimer.html















 
 
 
 
 
 
 