内联函数

内联函数 是直接集成到调用代码中的函数。

使用内联函数

使用静态类型参数时,由类型参数参数参数化的任何函数都必须内联。 这可以保证编译器可以解析这些类型参数。 使用普通泛型类型参数时,没有此类限制。

除了启用成员约束的使用外,内联函数还有助于优化代码。 但是,过度使用内联函数可能会导致代码对编译器优化和库函数的实现的更改的抵抗力较低。 因此,应避免使用内联函数进行优化,除非已尝试所有其他优化技术。 使函数或方法内联有时可以提高性能,但情况并非总是如此。 因此,还应使用性能度量来验证是否使任何给定函数内联确实具有积极效果。

inline修饰符可以应用于顶级、模块级别或类中方法级别的函数。

下面的代码示例演示顶层的内联函数、内联实例方法和内联静态方法。

let inline increment x = x + 1
type WrapInt32() =
    member inline this.incrementByOne(x) = x + 1
    static member inline Increment(x) = x + 1

内联函数和类型推理

存在 inline 影响类型推理。 这是因为内联函数可以具有静态解析的类型参数,而非内联函数不能。 下面的代码示例演示了一种非常有用的情况 inline ,因为你使用的是具有静态解析的类型参数(转换运算符)的 float 函数。

let inline printAsFloatingPoint number =
    printfn "%f" (float number)

inline如果没有修饰符,类型推理将强制函数采用特定类型,在本例int中。 但是,使用 inline 修饰符时,函数也推断为具有静态解析的类型参数。 inline使用修饰符时,该类型被推断为以下内容:

^a -> unit when ^a : (static member op_Explicit : ^a -> float)

这意味着该函数接受支持转换为 float 的任何类型。

InlineIfLambda

F# 编译器包括执行代码内联的优化器。 该 InlineIfLambda 属性允许代码选择性地指示,如果参数被确定为 lambda 函数,则该参数本身应始终在调用站点内联。 有关详细信息,请参阅 F# RFC FS-1098。

例如,请考虑以下 iterateTwice 函数遍历数组:

let inline iterateTwice ([<InlineIfLambda>] action) (array: 'T[]) =
    for i = 0 to array.Length-1 do
        action array[i]
    for i = 0 to array.Length-1 do
        action array[i]

如果呼叫站点为:

let arr = [| 1.. 100 |]
let mutable sum = 0
arr  |> iterateTwice (fun x ->
    sum <- sum + x)

然后,在内联和其他优化之后,代码将变为:

let arr = [| 1..100 |]
let mutable sum = 0
for i = 0 to arr.Length - 1 do
    sum <- sum + arr[i] 
for i = 0 to arr.Length - 1 do
    sum <- sum + arr[i] 

无论涉及 lambda 表达式的大小如何,都会应用此优化。 此功能还可用于更可靠地实现循环取消滚动和类似的转换。

可以打开选择加入警告(/warnon:3517 或属性 <WarnOn>3517</WarnOn>),以指示代码 InlineIfLambda 中参数未绑定到调用站点中的 lambda 表达式的位置。 在正常情况下,不应启用此警告。 但是,在某些类型的高性能编程中,确保所有代码都内联和平展非常有用。

优化 curried 回调

为避免在无法内联回调时重复调整 arity,F# 11 同时InlineIfLambda支持OptimizeClosureIfNotInlined:

let inline foldItems
    ([<InlineIfLambda; OptimizeClosureIfNotInlined>] folder)
    state
    (items: 'T[]) =
    let mutable total = state
    for item in items do
        total <- folder total item
    total

该函数必须内联,这两个属性必须存在,并且回调必须用两个到五个参数进行 curriried。 已知的 lambda 可以内联;一次不透明回调可以改编一次。 这需要 FSharp.Core 11 和编译器支持,但不保证零分配。

调试内联函数

若要单步执行内联帮助程序,请编译而不进行优化并保留调试信息。 F# 11 更喜欢以下版本中的正常调用:

let inline addFee (fee: decimal) (amount: decimal) =
    let total = amount + fee
    total

let invoiceTotal = addFee 2m 40m
printfn "%M" invoiceTotal
// 42

在 示例项目中,设置一个断点 let total,单步执行 addFee并检查其参数。

默认情况下,优化的生成仍内联。 某些构造(包括需要静态扩展的可恢复代码和操作)必须内联,即使没有优化也是如此。 用于 --always-inline 请求较旧的行为。

跨库的内联扩展约束

F# 11.2 可以使用公共扩展成员来满足 SRTP 要求。 有关语言设置、范围和库要求,请参阅 SRTP 约束的扩展成员 。

另请参阅