gpt4 book ai didi

performance - 为什么 F# 编译器不能完全内联高阶函数的函数参数?

转载 作者:行者123 更新时间:2023-12-04 01:09:20 24 4
gpt4 key购买 nike

我喜欢 F# 的一件事是真正的 inline关键词。然而,虽然它允许编写执行与粘贴代码块相同的一阶函数,但对于高阶函数而言,情况并不那么乐观。考虑

let inline add i = i+1
let inline check i = if (add i) = 0 then printfn ""
let inline iter runs f = for i = 0 to runs-1 do f i
let runs = 100000000
time(fun()->iter runs check) 1
time(fun()->for i = 0 to runs-1 do check i) 1

结果是 244 ms对于 iter61 ms用于手动检查。让我们深入研究 ILSpy。直接调用调用的相关函数是:
internal static void func@22-12(Microsoft.FSharp.Core.Unit unitVar0)
{
for (int i = 0; i < 100000000; i++)
{
if (i + 1 == 0)
{
Microsoft.FSharp.Core.PrintfFormat<Microsoft.FSharp.Core.Unit, System.IO.TextWriter, Microsoft.FSharp.Core.Unit, Microsoft.FSharp.Core.Unit> format = new Microsoft.FSharp.Core.PrintfFormat<Microsoft.FSharp.Core.Unit, System.IO.TextWriter, Microsoft.FSharp.Core.Unit, Microsoft.FSharp.Core.Unit, Microsoft.FSharp.Core.Unit>("");
Microsoft.FSharp.Core.PrintfModule.PrintFormatLineToTextWriter<Microsoft.FSharp.Core.Unit>(System.Console.Out, format);
}
}
}

add内联。 iter的相关功能是
internal static void func@22-11(Microsoft.FSharp.Core.Unit unitVar0)
{
for (int i = 0; i < 100000000; i++)
{
Tests.FunctionInlining.f@315-5(i);
}
}
internal static void f@315-5(int i)
{
if (i + 1 == 0)
{
Microsoft.FSharp.Core.PrintfFormat<Microsoft.FSharp.Core.Unit, System.IO.TextWriter, Microsoft.FSharp.Core.Unit, Microsoft.FSharp.Core.Unit> format = new Microsoft.FSharp.Core.PrintfFormat<Microsoft.FSharp.Core.Unit, System.IO.TextWriter, Microsoft.FSharp.Core.Unit, Microsoft.FSharp.Core.Unit, Microsoft.FSharp.Core.Unit>("");
Microsoft.FSharp.Core.PrintfModule.PrintFormatLineToTextWriter<Microsoft.FSharp.Core.Unit>(System.Console.Out, format);
return;
}
}

我们可以看到性能损失来自一个额外的间接级别。正如性能测试所示,JIT 编译器也不会删除此间接。为什么不能完全内联高阶函数?这在编写计算内核时很痛苦。

我的时间组合器(虽然在这里并不真正相关)是
let inline time func n =
func() |> ignore
GC.Collect()
GC.WaitForPendingFinalizers()
let stopwatch = Stopwatch.StartNew()
for i = 0 to n-1 do func() |> ignore
stopwatch.Stop()
printfn "Took %A ms" stopwatch.Elapsed.TotalMilliseconds

最佳答案

需要明确的是,F# 编译器会内联您标记为 inline 的每个定义。 .只是当使用内联函数作为高阶参数时,内联的当前行为不是很有用。 check只能在给定参数时内联,所以 iter runs check被视为 iter runs (fun i -> check i) .然后check被内联,导致相当于

iter runs (fun i -> if (add i) = 0 then printfn "")

(正如您在 IL 中看到的,在生成的 IL 中没有调用 check,但有调用此 lambda 的合成 f@315-5 主体,这是等效的)。 iter也被内联。

话虽如此,我同意当前的行为并不像它应该的那样有用 - 编译器还可以将 lambda 的主体内联到调用站点,这将是安全的并提高性能。

关于performance - 为什么 F# 编译器不能完全内联高阶函数的函数参数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24589480/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com