我写了以下函数
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
现在是时候调整它了。
为此,我已经安装了dot Trace Performance
并在我的应用程序上运行了一个非常大的maxPerimeter
,以确保该程序需要一段时间才能运行。
这就是我得到的:
正如您可能想象的那样,我真正想知道的是如何在 getTriangles'
函数体内部分配使用时间,所以这似乎并没有特别的帮助。我已经尝试在Build
窗格中关闭代码优化,但它似乎对我没什么帮助。
我所拥有的所有剖析经验都是使用Java,因此我可能会对CLR世界有所了解。我还涉及ANTS Performance,但结果是一样的。
答案 0 :(得分:4)
我不知道dotTrace,我使用ANTS Performance Profiler,因为我发现它与F#的效果非常好;我最近一直在使用新版本(v8)来描述我的fsharp-tools项目。
在ANTS Performance Profiler中完成分析运行并显示结果后,默认视图仅显示您拥有源的方法(即,您正在分析的.exe / .dll旁边有.pdb)它指向您机器上的某个有效源位置)。您可以使用下拉列表(请参见屏幕截图)显示所有方法,这对F#代码非常有用;因为您正在传递函数,执行堆栈往往会进出您可能正在使用的库,因此查看“所有方法”可以更好地了解代码实际执行的操作以及外部库中的代码可能会影响代码的性能。
那说 - F#有两种不同的for
循环;它们看起来很相似,但实际上却完全不同。你使用的那个(for x in y do
)大致相当于C#中的一个foreach
循环 - 也就是说,循环编译成一个迭代器,它从某个值序列中提取每个值。第二种,更快的循环(for i = x to y do
或for i = x downto y do
)编译为非常简单的IL,就像你在C#,Java,C等中使用for
循环一样
这是您的函数的修改版本,它使用第二种for
循环。在这种情况下,它只会稍微快一些(我的笔记本电脑上13.749s
与13.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
就功能中的行为而言,ANTS Performance Profiler还可以为您提供行级时序(但仅适用于您拥有源的方法)。我编译并分析了您的函数和我的修改版本(使用maxPerimeter = 2000
):