Встраиваемые функции

Встроенные функции — это функции, интегрированные непосредственно в вызывающий код.

Использование встроенных функций

При использовании параметров статического типа все функции, параметризованные параметрами типа, должны быть встроенными. Это гарантирует, что компилятор может разрешить эти параметры типа. При использовании обычных параметров универсального типа такое ограничение не существует.

Помимо включения ограничений элементов, встроенные функции могут быть полезны при оптимизации кода. Однако чрезмерное использование встроенных функций может привести к снижению устойчивости кода к изменениям в оптимизации компилятора и реализации функций библиотеки. По этой причине следует избегать использования встроенных функций для оптимизации, если вы не пробовали все другие методы оптимизации. Создание встроенной функции или метода иногда может повысить производительность, но это не всегда так. Таким образом, следует также использовать измерения производительности, чтобы убедиться, что выполнение любой встроенной функции фактически оказывает положительное влияние.

Модификатор 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 позволяет коду указывать, что, если аргумент определен как лямбда-функция, этот аргумент всегда должен быть встраивается на сайты вызовов. Дополнительные сведения см. в статье 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] 

Эта оптимизация применяется независимо от размера лямбда-выражения. Эту функцию также можно использовать для реализации циклов отмены и аналогичных преобразований более надежно.

Предупреждение о согласии (/warnon:3517 или свойство <WarnOn>3517</WarnOn>) можно включить, чтобы указать места в коде, где InlineIfLambda аргументы не привязаны к лямбда-выражениям на сайтах вызовов. В обычных ситуациях это предупреждение не должно быть включено. Однако в некоторых видах высокопроизводительного программирования может быть полезно обеспечить встраивать весь код и плоский.

См. также