F#版本的CodeTimer(已支持CPU时钟周期统计)

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

[<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;

    // print
    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

[<DllImport("kernel32.dll")>]
extern int QueryThreadCycleTime(IntPtr threadHandle, uint32* cycleTime)

[<DllImport("kernel32.dll")>]
extern IntPtr GetCurrentThread();

F#在P/Invoke签名中使用*来标记out参数,但是在自定义方法时使用的是byref,这点与C#不同,后者都是使用ref。这个引入看似没有问题,而且普通调用也能得到正常结果:

[<EntryPoint>]
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

这样调用的时候就会抛出异常:

o_fsharp-pinvoke-error.jpg

后经alonesail同学指出,引入QueryThreadCycleTime的时候,第二个参数应该是64位而不是32位无符号整数——我将PULONG64看作PULONG了。改成uint64便没有问题了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在使用C语言获取CPU时钟周期的流程如下: 1. 首先,需要确保编译器支持内联汇编语句。对于大多数常见的编译器,都支持内联汇编。 2. 然后,使用内联汇编语句来获取CPU时钟周期。不同的编译器和CPU架构有不同的语法,以下是一个示例: ``` #include <stdio.h> unsigned long long get_cycle_count() { unsigned int low, high; // 使用RDTSC(Read Time Stamp Counter)指令获取时钟周期计数 __asm__ volatile ("rdtsc" : "=a" (low), "=d" (high)); // 将低32位和高32位合并成一个64位的计数值并返回 return ((unsigned long long)high << 32) | low; } int main() { unsigned long long start, end, cycles; // 获取开始时的时钟周期数 start = get_cycle_count(); // 在这里执行需要计时的代码 // 获取结束时的时钟周期数 end = get_cycle_count(); // 计算经过的时钟周期数 cycles = end - start; printf("经过的时钟周期数:%llu\n", cycles); return 0; } ``` 上述代码中,`get_cycle_count()`函数使用了`rdtsc`指令来读取CPU的时间戳计数器(TSC)寄存器的值,该寄存器记录了从系统启动以来的时钟周期数。然后,通过将低32位和高32位合并成一个64位的计数值,函数返回了经过的时钟周期数。 在`main()`函数中,我们可以使用`get_cycle_count()`函数来获取开始和结束时的时钟周期数,并通过相减得到经过的时钟周期数。最后,使用`printf()`函数将结果打印出来。 需要注意的是,不同的编译器可能有不同的内联汇编语法,并且某些编译器可能对内联汇编的支持有所限制或要求特定的设置。因此,在使用此方法时,需要仔细查阅所用编译器的文档以了解正确的语法和用法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值