内联函数

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

使用内联函数

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

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

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

请参阅