分析Swift性能高效的原因
自從2014年Apple發(fā)布Swift語言以來,歷時六年多,Swift已經(jīng)發(fā)布到5.3版本,在5.0版本已經(jīng)ABI stability,5.2版本也已經(jīng)module stability,不管是語言還是基礎庫都日趨穩(wěn)定,目前國內(nèi)外大廠也都積極擁抱Swift陣營。
絕大多數(shù)公司選擇Swift語言開發(fā)iOS應用,主要原因是因為Swift相比Objc有更快的運行效率,更加安全的類型檢測,更多現(xiàn)代語言的特性提升開發(fā)效率;這一系列的優(yōu)點使Swift語言的熱度越來越高。
大多數(shù)人知道Swift語言相比于Objc語言運行效率更高,但是卻不知道為什么效率更高,在這里我們Swift編譯層探討一下Swift語言高效的原因。
更加高效的數(shù)據(jù)類型
在開始討論Swift數(shù)據(jù)類型之前,我們先討論一下Swift的函數(shù)派發(fā)機制;
靜態(tài)派發(fā)、動態(tài)派發(fā)、消息派發(fā)(static dispatch、dynamic dispatch、message dispatch)
動態(tài)派發(fā)(dynamic dispatch): 動態(tài)派發(fā)是指編譯期無法確定應該調(diào)用哪個方法,需要在運行時才能確定方法的調(diào)用。
靜態(tài)派發(fā)(static dispatch):是在編譯期就能確定的調(diào)用方法的派發(fā)方式。
除了上面兩種方式之外,在Swift里面還會使用Objc的消息派發(fā)(message dispatch))機制;Objc采用了運行時采用obj_msgsend進行消息派發(fā),所以Objc的一些動態(tài)特性在Swift里面也可以被限制的使用。
靜態(tài)派發(fā)相比于動態(tài)派發(fā)更快,而且靜態(tài)派發(fā)還會進行內(nèi)聯(lián)等一些優(yōu)化,減少函數(shù)的尋址及內(nèi)存地址的偏移計算等一系列操作,使函數(shù)的執(zhí)行速度更快,性能更高。
數(shù)據(jù)類型(struct/class)
我們都知道,內(nèi)存分配可以分為堆區(qū)(Heap)和棧區(qū)(Stack)。由于棧區(qū)內(nèi)存是連續(xù)的,內(nèi)存的分配和銷毀是通過入棧和出棧操作進行的,速度要高于堆區(qū)。堆區(qū)存儲高級數(shù)據(jù)類型,在數(shù)據(jù)初始化時,查找沒有使用的內(nèi)存,銷毀時再從內(nèi)存中清除,所以堆區(qū)的數(shù)據(jù)存儲不一定是連續(xù)的。
類(class)和結(jié)構體(struct)在內(nèi)存分配上是不同的,基本數(shù)據(jù)類型和結(jié)構體默認分配在棧區(qū),而像類這種高級數(shù)據(jù)類型存儲在堆區(qū),且堆區(qū)數(shù)據(jù)存儲不是線程安全的,在頻繁的數(shù)據(jù)讀寫操作時,要進行加鎖操作。
我們在swift文檔里面能看到對結(jié)構的描述,結(jié)構體是值類型(Value Type),當值類型的數(shù)據(jù)賦值給一個變量或常量,或者傳遞給一個函數(shù)時,是值拷貝;
例如:
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中的屬性值,如下圖所示:
結(jié)構體除了屬性的存儲更安全、效率更高之外,其函數(shù)的派發(fā)也更高效。由于結(jié)構體不能被繼承,也就是結(jié)構體的類型被final修飾,根據(jù)我們對于動態(tài)派發(fā)及靜態(tài)派發(fā)的描述,那么其內(nèi)部函數(shù)應該是屬于靜態(tài)派發(fā),在編譯期就確定了函數(shù)的執(zhí)行方式,其函數(shù)的調(diào)用通過內(nèi)聯(lián)(inline)的方式進行優(yōu)化,其內(nèi)存連續(xù),減少了函數(shù)的尋址及內(nèi)存地址的偏移計算,其運行相比于動態(tài)派發(fā)更加高效。
協(xié)議類型(protocol type)
多態(tài)是面向?qū)ο蟮囊淮筇匦裕诮Y(jié)構體中不能通過繼承或者引用語言的多態(tài),swift就引入了協(xié)議(protocol),通過協(xié)議來實現(xiàn)了結(jié)構體的多態(tài)特性,這也是swift面向協(xié)議編程的核心所在。
對于類(class)來說,每個類都會創(chuàng)建一個虛擬函數(shù)表指針,這個指針則指向一個v-table表,也就是虛函數(shù)表,表內(nèi)存儲著該類的函數(shù)指針數(shù)組,擁有繼承關系的子類會在虛函數(shù)表內(nèi)通過繼承順序(C++可以實現(xiàn)多繼承)去展示虛函數(shù)表指針。類里面方法的派發(fā)則是根據(jù)v-table表里面函數(shù)指針來進行派發(fā)。
而結(jié)構體(struct)沒有繼承,也就是說結(jié)構體并沒有v-table表用于函數(shù)的派發(fā)。為了實現(xiàn)這一特性,在結(jié)構體的協(xié)議(protocol)的實現(xiàn)里添加了Protocol Witness Table用于管理協(xié)議類型的方法派發(fā)。
編譯過程
上面介紹了一些swift在數(shù)據(jù)結(jié)構上的一些優(yōu)化,除了數(shù)據(jù)結(jié)構優(yōu)化之外,swift在編譯過程也進行了大量的優(yōu)化,其中最核心的優(yōu)化,是在編譯過程中引入SIL。
SIL,Swift Intermediate Language,是為了優(yōu)化swift編譯過程而設計的中間語言,主要包含了以下功能:
- 一系列的高級別優(yōu)化保障,用于對運行時和診斷行為提供可預測的基線;
- 對swift語言數(shù)據(jù)流分析強制要求,對不滿足強制要求的問題產(chǎn)生診斷。例如變量和結(jié)構體必須明確初始化,代碼可達性即方法return的檢測,switch的覆蓋率;
- 確保高級別優(yōu)化。包含retain/release優(yōu)化,動態(tài)方法的去虛擬化,閉包內(nèi)聯(lián),內(nèi)存初始化提升和泛型方法實例 化.
- 可用于分配"脆弱"內(nèi)聯(lián)的穩(wěn)定分配格式,將Swift庫組件的泛型優(yōu)化為二進制。
Clang編譯流程
Clang編譯過程有以下幾個缺點:
- 與代碼與LLVM IR之間有巨大的抽象鴻溝(Wide abstraction gap between source and LLVM IR );
- IR不適合源碼級別的分析(IR isn't suitable for source-level analysis );
- CFG(Control Flow Graph)缺少精準度(CFG lacks fidelity );
- CFG偏離主道(CFG is off the hot path );
- 在CFG和IR降級中會出現(xiàn)重復分析(Duplicated effort in CFG and IR lowering)。
由于以上這些缺點,swift語言開發(fā)團隊在開發(fā)過程中進行了一系列的優(yōu)化,其中最關鍵的是引入SIL.
swift編譯流程
Swift作為一個高級別和安全的語言具有以下特點:
高級別語言
- 通過代碼充分的展示語言的特性(Move more of the language into code)
- 支持基于協(xié)議的泛型(Protocol-based generics)
安全語言
- 充分的數(shù)據(jù)流檢查:未初始化變量,函數(shù)返回處理檢測,這些項在檢測不合格時會產(chǎn)生對應的編譯錯誤(Uninitialized vars, unreachable code should be compiler errors)
- 邊界和溢出的檢測(Bounds and overflflow checks)
swift編譯流程:
Swift 源碼到IR之間的流程:
Swift 編譯過程引入SIL有幾個優(yōu)點:
- 完成的變數(shù)程序的語義(Fully represents program semantics );
- 既能進行代碼的生成,又能進行代碼分析(Designed for both code generation and analysis );
- 處在編譯管線的主通道(Sits on the hot path of the compiler pipeline );
- 架起橋梁連接源碼與LLVM,減少源碼與LLVM之間的抽象鴻溝(Bridges the abstraction gap between source and LLVM)
Swift編譯器的流程
Swift編譯器作為高級編譯器,具有以下嚴格的傳遞流程結(jié)構。
Swift編譯器的流程如下:
- Parse: 語法分析組件從Swift源碼構成AST
- 語義分析組件對AST進行類型檢查,并對其進行類型信息注釋。
- SILGen組件從AST形成"原始(raw)"SIL
- 一系列在 生 SIL上運行的,用于確定優(yōu)化和診斷合格,對不合格的代碼嵌入特定的語言診斷。這些操作一定會執(zhí)行,即使在-Onone選項下也不例外。之后產(chǎn)生 正式(canonical) SIL.
- 一般情況下,是否在正式SIL上運行SIL優(yōu)化是可選的,這個檢測可以提升結(jié)果可執(zhí)行文件的性能.可以通過優(yōu)化級別來控制,在-Onone模式下不會執(zhí)行.
- IRGen會將正式SIL降級為LLVM IR.
- LLVM后端提供LLVM優(yōu)化,執(zhí)行LLVM代碼生成器并產(chǎn)生二進制碼.
在上面的流程中,SIL對Swift的編譯過程進行了一系列的優(yōu)化,即保證的代碼執(zhí)行的安全性,又提升了代碼執(zhí)行的效率.
結(jié)尾
上面從Swift語言設計的數(shù)據(jù)結(jié)構及編譯流程等方面進行了簡單的分析,中間有很多細節(jié)沒有在文章里闡述特別清晰,如果有興趣了解更多,可以參考以下資料。
- 深入剖析Swift性能優(yōu)化
- static dispatch和dynamic dispatch
- swift的witness table
- Swift的高級中間語言:SIL
- Swift Intermediate Language
以上就是分析Swift性能高效的原因的詳細內(nèi)容,更多關于Swift性能的資料請關注腳本之家其它相關文章!
相關文章
Swift 使用 Observe 監(jiān)測頁面滾動的實現(xiàn)方法
這篇文章主要介紹了Swift 使用 Observe 監(jiān)測頁面滾動的實現(xiàn)方法,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-05-05