内联函数

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

使用内联函数

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

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

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 表达式的位置。 在正常情况下,不应启用此警告。 但是,在某些类型的高性能编程中,确保所有代码都内联和平展非常有用。

另请参阅