內嵌函式
「內嵌函式」是直接整合到呼叫程式碼中的函式。
使用內嵌函式
當您使用靜態型別參數時,由型別參數所參數化的任何函式都必須內嵌。 這可確保編譯器能夠解析這些型別參數。 當您使用一般泛型型別參數時,則沒有這類限制。
除了可以使用成員條件約束之外,內嵌函式還有助於最佳化程式碼。 不過,過度使用內嵌函式可能會導致程式碼對於編譯器最佳化和程式庫函式實作的變更較無抵抗力。 基於這個理由,除非您已試過所有其他最佳化技術,否則應該避免使用內嵌函式進行最佳化。 讓函式或方法內嵌有時可以改善效能,但不總是如此。 因此,您也應該使用效能測量來確認讓任何指定的函式內嵌確實有正面的影響。
inline
修飾詞可以套用至最上層、模組層級或類別中方法層級的函式。
下列程式碼範例說明最上層的內嵌函式、內嵌執行個體方法和內嵌靜態方法。
let inline increment x = x + 1
type WrapInt32() =
member inline this.incrementByOne(x) = x + 1
static member inline Increment(x) = x + 1
內嵌函式和型別推斷
inline
的存在與否會影響型別推斷。 這是因為內嵌函式可以有靜態解析的型別參數,然而非內嵌函式則不能。 下列程式碼範例示範一個案例,其中由於您使用的函式具有靜態解析的型別參數 (也就是 float
轉換運算子),因此 inline
會很有幫助。
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 運算式的位置。 在正常情況下,不應該啟用此警告。 不過,在某些類型的高效能程式設計中,其有助於確保所有程式碼都已內嵌和壓平合併。