1. 程式人生 > 程式設計 >分析Swift效能高效的原因

分析Swift效能高效的原因

自從2014年Apple釋出Swift語言以來,歷時六年多,Swift已經發布到5.3版本,在5.0版本已經ABI stability,5.2版本也已經module stability,不管是語言還是基礎庫都日趨穩定,目前國內外大廠也都積極擁抱Swift陣營。

絕大多數公司選擇Swift語言開發iOS應用,主要原因是因為Swift相比Objc有更快的執行效率,更加安全的型別檢測,更多現代語言的特性提升開發效率;這一系列的優點使Swift語言的熱度越來越高。

大多數人知道Swift語言相比於Objc語言執行效率更高,但是卻不知道為什麼效率更高,在這裡我們Swift編譯層探討一下Swift語言高效的原因。

更加高效的資料型別

在開始討論Swift資料型別之前,我們先討論一下Swift的函式派發機制;

靜態派發、動態派發、訊息派發(static dispatch、dynamic dispatch、message dispatch)
動態派發(dynamic dispatch): 動態派發是指編譯期無法確定應該呼叫哪個方法,需要在執行時才能確定方法的呼叫。

靜態派發(static dispatch):是在編譯期就能確定的呼叫方法的派發方式。

除了上面兩種方式之外,在Swift裡面還會使用Objc的訊息派發(message dispatch))機制;Objc採用了執行時採用obj_msgsend進行訊息派發,所以Objc的一些動態特性在Swift裡面也可以被限制的使用。

靜態派發相比於動態派發更快,而且靜態派發還會進行內聯等一些優化,減少函式的定址及記憶體地址的偏移計算等一系列操作,使函式的執行速度更快,效能更高。

資料型別(struct/class)

我們都知道,記憶體分配可以分為堆區(Heap)和棧區(Stack)。由於棧區記憶體是連續的,記憶體的分配和銷燬是通過入棧和出棧操作進行的,速度要高於堆區。堆區儲存高階資料型別,在資料初始化時,查詢沒有使用的記憶體,銷燬時再從記憶體中清除,所以堆區的資料儲存不一定是連續的。

類(class)和結構體(struct)在記憶體分配上是不同的,基本資料型別和結構體預設分配在棧區,而像類這種高階資料型別儲存在堆區,且堆區資料儲存不是執行緒安全的,在頻繁的資料讀寫操作時,要進行加鎖操作。

我們在swift文件裡面能看到對結構的描述,結構體是值型別(Value Type),當值型別的資料賦值給一個變數或常量,或者傳遞給一個函式時,是值拷貝;

例如:

struct Resolution {
  var width = 0
  var height = 0
}

let hd = Resolution(width: 1920,height: 1080)
var cinema = hd
cinema.width = 2048

print("cinema is now \(cinema.width) pixels wide")
// Prints "cinema is now 2048 pixels wide"

print("hd is still \(hd.width) pixels wide")
// Prints "hd is still 1920 pixels wide"

通過這個例子我們能清楚的看到,當hd賦值給cinema時,是將hd中儲存的值拷貝給cinema,所以當給cinema的width屬性賦值的時候,並不會改變hd中的屬性值,如下圖所示:

分析Swift效能高效的原因

結構體除了屬性的儲存更安全、效率更高之外,其函式的派發也更高效。由於結構體不能被繼承,也就是結構體的型別被final修飾,根據我們對於動態派發及靜態派發的描述,那麼其內部函式應該是屬於靜態派發,在編譯期就確定了函式的執行方式,其函式的呼叫通過內聯(inline)的方式進行優化,其記憶體連續,減少了函式的定址及記憶體地址的偏移計算,其執行相比於動態派發更加高效。

協議型別(protocol type)

多型是面向物件的一大特性,在結構體中不能通過繼承或者引用語言的多型,swift就引入了協議(protocol),通過協議來實現了結構體的多型特性,這也是swift面向協議程式設計的核心所在。

對於類(class)來說,每個類都會建立一個虛擬函式表指標,這個指標則指向一個v-table表,也就是虛擬函式表,表記憶體儲著該類的函式指標陣列,擁有繼承關係的子類會在虛擬函式表內通過繼承順序(C++可以實現多繼承)去展示虛擬函式表指標。類裡面方法的派發則是根據v-table表裡面函式指標來進行派發。

而結構體(struct)沒有繼承,也就是說結構體並沒有v-table表用於函式的派發。為了實現這一特性,在結構體的協議(protocol)的實現裡添加了Protocol Witness Table用於管理協議型別的方法派發。

編譯過程

上面介紹了一些swift在資料結構上的一些優化,除了資料結構優化之外,swift在編譯過程也進行了大量的優化,其中最核心的優化,是在編譯過程中引入SIL。

SIL,Swift Intermediate Language,是為了優化swift編譯過程而設計的中間語言,主要包含了以下功能:

  1. 一系列的高級別優化保障,用於對執行時和診斷行為提供可預測的基線;
  2. 對swift語言資料流分析強制要求,對不滿足強制要求的問題產生診斷。例如變數和結構體必須明確初始化,程式碼可達性即方法return的檢測,switch的覆蓋率;
  3. 確保高級別優化。包含retain/release優化,動態方法的去虛擬化,閉包內聯,記憶體初始化提升和泛型方法例項 化.
  4. 可用於分配"脆弱"內聯的穩定分配格式,將Swift庫元件的泛型優化為二進位制。

Clang編譯流程

分析Swift效能高效的原因

Clang編譯過程有以下幾個缺點:

  1. 與程式碼與LLVM IR之間有巨大的抽象鴻溝(Wide abstraction gap between source and LLVM IR );
  2. IR不適合原始碼級別的分析(IR isn't suitable for source-level analysis );
  3. CFG(Control Flow Graph)缺少精準度(CFG lacks fidelity );
  4. CFG偏離主道(CFG is off the hot path );
  5. 在CFG和IR降級中會出現重複分析(Duplicated effort in CFG and IR lowering)。

由於以上這些缺點,swift語言開發團隊在開發過程中進行了一系列的優化,其中最關鍵的是引入SIL.

swift編譯流程

Swift作為一個高級別和安全的語言具有以下特點:

高級別語言

  • 通過程式碼充分的展示語言的特性(Move more of the language into code)
  • 支援基於協議的泛型(Protocol-based generics)

安全語言

  • 充分的資料流檢查:未初始化變數,函式返回處理檢測,這些項在檢測不合格時會產生對應的編譯錯誤(Uninitialized vars,unreachable code should be compiler errors)
  • 邊界和溢位的檢測(Bounds and overflflow checks)

swift編譯流程:

分析Swift效能高效的原因

Swift 原始碼到IR之間的流程:

分析Swift效能高效的原因

Swift 編譯過程引入SIL有幾個優點:

  1. 完成的變數程式的語義(Fully represents program semantics );
  2. 既能進行程式碼的生成,又能進行程式碼分析(Designed for both code generation and analysis );
  3. 處在編譯管線的主通道(Sits on the hot path of the compiler pipeline );
  4. 架起橋樑連線原始碼與LLVM,減少原始碼與LLVM之間的抽象鴻溝(Bridges the abstraction gap between source and LLVM)

Swift編譯器的流程

Swift編譯器作為高階編譯器,具有以下嚴格的傳遞流程結構。

Swift編譯器的流程如下:

  • Parse: 語法分析元件從Swift原始碼構成AST
  • 語義分析元件對AST進行型別檢查,並對其進行型別資訊註釋。
  • SILGen元件從AST形成"原始(raw)"SIL
  • 一系列在 生 SIL上執行的,用於確定優化和診斷合格,對不合格的程式碼嵌入特定的語言診斷。這些操作一定會執行,即使在-Onone選項下也不例外。之後產生 正式(canonical) SIL.
  • 一般情況下,是否在正式SIL上執行SIL優化是可選的,這個檢測可以提升結果可執行檔案的效能.可以通過優化級別來控制,在-Onone模式下不會執行.
  • IRGen會將正式SIL降級為LLVM IR.
  • LLVM後端提供LLVM優化,執行LLVM程式碼生成器併產生二進位制碼.

在上面的流程中,SIL對Swift的編譯過程進行了一系列的優化,即保證的程式碼執行的安全性,又提升了程式碼執行的效率.

結尾

上面從Swift語言設計的資料結構及編譯流程等方面進行了簡單的分析,中間有很多細節沒有在文章裡闡述特別清晰,如果有興趣瞭解更多,可以參考以下資料。

  • 深入剖析Swift效能優化
  • static dispatch和dynamic dispatch
  • swift的witness table
  • Swift的高階中間語言:SIL
  • Swift Intermediate Language

以上就是分析Swift效能高效的原因的詳細內容,更多關於Swift效能的資料請關注我們其它相關文章!