简体   繁体   English

分析F#函数 - 分析器看不到函数体的任何调用。 为什么?

[英]Profiling F# function — profiler doesn't see any of the calls of the function's body. Why?

I wrote the following function 我写了以下函数

let getTriangles maxPerimeter =
    let mutable count = 0
    for c in 1..maxPerimeter do
        let cc = (int64 (c*c))
        for b in 1..Math.Min(c-1, maxPerimeter-c-1) do
            let bb = (int64 (b*b))
            for a in 1..Math.Min(maxPerimeter-c-b, (int (Math.Ceiling(Math.Sqrt(float (cc+1L-bb)))))) do
                let aa = (int64 (a*a))
                if cc + 1L = aa + bb then
                    count <- count + 1
    count

and now it's time to tune it up. 现在是时候调整它了。

To do so, I've installed dot Trace Performance and ran it over my application for a very big maxPerimeter , so as to make sure the program would take a while to run. 为此,我安装了dot Trace Performance并在我的应用程序上运行了一个非常大的maxPerimeter ,以确保程序需要一段时间才能运行。

This is what I get: 这就是我得到的:

在此输入图像描述

As you may imagine, what I actually wanted to know is how the usage time is distributed inside getTriangles' function body, so this doesn't seem to be of particular help. 正如您可能想象的那样,我实际想知道的是如何 getTriangles'函数体内分配使用时间,所以这似乎没有特别的帮助。 I've tried turning off code optimizations in the Build pane, but it doesn't seem to help me a bit. 我已经尝试在“ Build窗格中关闭代码优化,但它似乎对我没有帮助。

  1. Am I doing something wrong? 难道我做错了什么?
  2. How should I go about to profile this function? 我应该如何分析这个功能?
  3. Is this F# or CLR specific behavior? 这是F#还是CLR特有的行为?

All the profiling experience I have is with Java, so I may be a bit off here on the CLR world. 我拥有的所有分析经验都是使用Java,因此我可能会对CLR世界有所了解。 I've also dabbled with ANTS Performance but the result was the same. 我也涉足ANTS Performance,但结果是一样的。

I don't know about dotTrace, I use ANTS Performance Profiler as I've found it works pretty well with F#; 我不知道dotTrace,我使用ANTS Performance Profiler,因为我发现它与F#的效果非常好; I've been using the new version (v8) recently to profile my fsharp-tools projects. 我最近一直在使用新版本(v8)来分析我的fsharp-tools项目。

Once you complete a profiling run in ANTS Performance Profiler and the results are displayed, the default view only shows methods for which you have the sources (ie, the .exe/.dll you're profiling has a .pdb alongside it which points to some valid source location on your machine). 在ANTS Performance Profiler中完成分析运行并显示结果后,默认视图仅显示您拥有源的方法(即,您正在分析的.exe / .dll旁边有一个.pdb,它指向您计算机上的某个有效源位置)。 You can use the drop-down (see the screenshot) to show all methods, which is pretty useful for F# code; 您可以使用下拉列表(请参见屏幕截图)显示所有方法,这对F#代码非常有用; because you're passing functions around, the execution stack tends to go in and out of the libraries you may be using, so viewing "All Methods" gives a better picture of what your code is actually doing, and how code from an external library might be affecting the performance of your code. 因为您正在传递函数,执行堆栈往往会进出您可能正在使用的库,因此查看“所有方法”可以更好地了解代码实际执行的操作以及外部库中的代码可能会影响代码的性能。

ANTS Performance Profiler v8截图,分析F#代码(fsharp-tools)

That said -- F# has two different kinds of for loops; 那说 - F#有两种不同的for循环; they look similar but are actually quite different under the hood. 它们看起来很相似,但实际上却完全不同。 The one you used ( for x in y do ) is roughly equivalent to a foreach loop in C# -- that is, the loop compiles down to an iterator which pulls each value from some sequence of values. 你使用的那个( for x in y do )大致相当于C#中的foreach循环 - 也就是说,循环编译成一个迭代器,它从某个值序列中提取每个值。 The second, much faster kind of loop ( for i = x to y do or for i = x downto y do ) compiles down to very simple IL, just like you'd get with a for loop in C#, Java, C, etc. 第二种,更快的循环( for i = x to y do或者for i = x downto y do )编译成非常简单的IL,就像你在C#,Java,C等中使用for循环一样。

Here's a modified version of your function which uses the second kind of for loop. 这是你的函数的修改版本,它使用第二种for循环。 In this case, it's only slightly faster ( 13.749s vs. 13.520s , N = 5000 on my laptop), but it is without question the way you want to write your code if you're doing any tight loops over numerical ranges. 在这种情况下,它只是稍微快一点( 13.749s13.520s ,我的笔记本电脑N = 5000 ),但如果你在数字范围内进行任何紧密循环,那么毫无疑问你想要编写代码的方式。

let getTriangles' maxPerimeter =
    let mutable count = 0
    for c = 1 to maxPerimeter do
        let cc = int64 (c * c)
        for b = 1 to min (c-1) (maxPerimeter-c-1) do
            let bb = int64 (b * b)
            for a = 1 to min (maxPerimeter-c-b) (int <| ceil (sqrt <| float (cc+1L-bb))) do
                let aa = int64 (a * a)
                if cc + 1L = aa + bb then
                    count <- count + 1
    count

As far as behavior within the function, ANTS Performance Profiler can also give you line-level timings (but only for methods you have the source for). 就功能中的行为而言,ANTS Performance Profiler还可以为您提供行级时序(但仅适用于您拥有源的方法)。 I compiled and profiled your function and my modified version (with maxPerimeter = 2000 ): 我编译并分析了你的函数和我的修改版本( maxPerimeter = 2000 ):

ANTS Performance Profiler v8,F#项目的行级时序

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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