对于F#这个微软的新丁,很多人并不熟悉。很多开发人员知道函数式编程方面Scala可以算一个,但是不知道F#中CodeTimer妙用。本文借用作者的文章,希望能让大家对F#有更深刻的了解。

创新互联公司主要从事网站设计制作、成都网站制作、网页设计、企业做网站、公司建网站等业务。立足成都服务尼开远,10余年网站建设经验,价格优惠、服务专业,欢迎来电咨询建站服务:18980820575
#T#
CodeTimer很好用,自从在今年三月在.NET技术大会上看到Jeffrey Richter用类似的东西之后,我就自己写了一个。不过,当时是用C#写的,现在我需要在F#里做相同的事情就不那么方便了。当然,F#与.NET本是无缝集成,因此C#写的CodeTimer也应该可以被F#使用。不过,我平时在使用CodeTimer时并不是通过程序集引用,而是使用代码复制的方式,因此如果有个F#版本那么应该使用起来更加方便。
代码如下:
- #light
 - module CodeTimer
 - open System
 - open System.Diagnostics
 - open System.Threading
 - open System.Runtime.InteropServices
 - [
 ] - extern int QueryThreadCycleTime(IntPtr threadHandle, uint64* cycleTime)
 - [
 ] - 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)
 
结果是:
- Wait
 - Time Elapsed: 684ms
 - CPU Cycles: 372,709,908
 - Gen0: 0
 - Gen1: 0
 - Gen2: 0
 与C#版本的CodeTimer相比,第一版的F# CodeTimer少算了CPU使用周期的消耗——不是我不想,而是遇到了问题。我当时这样引入P/Invoke的签名:
- open System.Runtime.InteropServices
 - [
 ("kernel32.dll")>] - extern int QueryThreadCycleTime(IntPtr threadHandle, uint32* cycleTime)
 - [
 ("kernel32.dll")>] - extern IntPtr GetCurrentThread();
 F#在P/Invoke签名中使用*来标记out参数,但是在自定义方法时使用的是byref,这点与C#不同,后者都是使用ref。这个引入看似没有问题,而且普通调用也能得到正常结果:
- [
 ] - let main args =
 - let mutable cycle = 0u
 - let threadHandle = CodeTimer.GetCurrentThread()
 - CodeTimer.QueryThreadCycleTime(threadHandle, &&cycle) |> ignore
 - Console.ReadLine() |> ignore
 - 0
 但是,一旦我把它加为CodeTimer的一个方法,如getCycleCount:
- let getCycleCount() =
 - let mutable cycle = 0u
 - let threadHandle = GetCurrentThread()
 - QueryThreadCycleTime(threadHandle, &&cycle) |> ignore
 - cycle
 这样调用的时候就会抛出异常:
后经alonesail同学指出,引入QueryThreadCycleTime的时候,第二个参数应该是64位而不是32位无符号整数——我将PULONG64看作PULONG了。改成uint64便没有问题了。
链接:http://www.cnblogs.com/JeffreyZhao/archive/2009/11/13/fsharp-codetimer.html
文章题目:详解F#版本的CodeTimer方法实现
标题来源:http://wtcwzsj.com/article/djcpsei.html
Copyright © 2009-2022 www.wtcwzsj.com 青羊区广皓图文设计工作室(个体工商户) 版权所有 蜀ICP备19037934号