Explore Swift performance
探索 Swift 性能
探索 Swift 如何平衡抽象與性能。了解需要考慮的性能元素以及 Swift 優化器如何影響它們。深入了解 Swift 的不同特性及其實現方式,以進一步理解可能影響性能的權衡。
Key takeaways 關鍵要點
Swift Provides tools for abstraction that C doesn’t
Swift 提供了 C 所不具備的抽象工具closures 閉包
generics 泛型
Performance is multidimensional and Situational
表現是多維度和情境性的Low-Level performance 低階性能
Calls aren’t optimized effectively
通話未有效優化Data isn’t represented properly
數據未正確表示Memory isn’t allocated properly
記憶體未正確分配Unneccessary copying/destroying
不必要的複製/銷毀
Presenter 演示者
John McCall, Swift Team 約翰·麥考,Swift 團隊
Optimization Potential 優化潛力
There are limits to the optimization. The way you write code can have a significant impact on how much the optimizer can do. When identifying hot spots during top-down investigation, try to find ways to measure them, and then automate those measurements as part of your development process.
優化是有其限制的。你編寫代碼的方式會對優化器能做多少工作產生重大影響。在自上而下的調查中識別熱點時,嘗試找到測量它們的方法,然後將這些測量自動化,作為你開發過程的一部分。
Function Calls 函數調用
Function calls have four costs, three of which are dependent in how they’re set up.
函數調用有四個成本,其中三個取決於它們的設置方式。
Arguments 參數
Address Resolve 地址解析
Space Allocation 空間分配
Optimization inhibiting in caller and in the function
在呼叫者和函數中抑制優化
Arguments have two cost levels
參數有兩個成本級別
Calling convention conformance (hidden by register renaming in modern processors; little difference is made)
呼叫約定符合性(在現代處理器中被寄存器重命名隱藏;影響不大)Copies of values added to match function ownership showing as retains and releases
值的副本已添加到匹配函數所有權,顯示為保留和釋放
Resolution and Optimization have the same issue. At compile time which functions is called. Yes means the dispatch is static, otherwise it’s dynamic.
解析和優化有相同的問題。在編譯時,哪個函數被調用。是的,這意味著調度是靜態的,否則就是動態的。
Static or direct Dispatch
靜態或直接調度a little faster for processing
處理速度稍快一些optimization possibilities at compile time
編譯時的優化可能性inlining 內聯
generic specialization 通用專門化
Dynamic or indirect or virtual Dispatch
動態或間接或虛擬調度enables polymorphism and other powerful abstraction tools
啟用多態性和其他強大的抽象工具only specific kinds of calls
僅特定類型的呼叫Opaque function values 不透明的函數值
Overridable class methods
可覆寫的類別方法Protocall requirments Protocall 要求
Objective-C or virtual C++ methods
Objective-C 或虛擬 C++ 方法
Example Callsite 範例呼叫位置
func updateAll(models: [any DataModel], from source: DataSource) {
for model in models {
model.update(from: source)
}
}
When declared in the main body of the protocol, it’s a protocol requirement, and the call to it uses dynamic dispatch.
當在協議的主體中聲明時,這是一個協議要求,對它的調用使用動態調度。If declared in a protocol extension, the call uses static dispatch.
如果在協議擴展中聲明,則調用使用靜態調度。
protocol DataModel {
func update(from source: DataSource, quickly: Bool)
}
Functions need memory to run. Allocating space on a stack can be done by just subtracting from the stack pointer.
函數需要記憶體來運行。在堆疊上分配空間可以通過從堆疊指標中減去來完成。
func updateAll(models: [any DataModel],
from source: DataSource) {
for model in models {
model.update(from: source)
}
}
The above function compiles into the following Assembly code
上述函數編譯成以下組合語言代碼
sub sp, sp, #208
stp x29, x30, [sp, #192]
…
ldp x29, x30, [sp, #192]
add sp, sp, #208
ret
When entering the function, the stack pointer is pointing into the C stack.
當進入函數時,堆疊指標指向 C 堆疊。208 bytes are subtracted from stack pointer (this is called a CallFrame)
從堆疊指標減去 208 位元組(這稱為 CallFrame)Function body is run 函數主體正在運行
208 bytes deallocated back to the stack pointer
208 字節已釋放回堆疊指標
// sizeof(CallFrame) == 208
struct CallFrame {
Array<AnyDataModel> models;
DataSource source;
AnyDataModel model;
ArrayIterator iterator;
...
void *savedX29;
void *savedX30;
};
CallFrame has a layout like a C struct. Ideally, all of the local state of the function just becomes fields in the CallFrame. Compiler is always going to emit that subtraction at the start of the function in order to make space to save critical things like the return address. Subtracting a larger constant doesn’t take any longer, so if more memory is needed in the function, allocating it as part of the CallFrame is as close as it gets to free.
CallFrame 的佈局類似於 C 結構。理想情況下,函數的所有本地狀態都會成為 CallFrame 中的欄位。編譯器總是會在函數開始時發出該減法,以便騰出空間來保存關鍵的東西,例如返回地址。減去較大的常數不會花費更長的時間,因此如果函數中需要更多的內存,將其作為 CallFrame 的一部分進行分配就幾乎是免費的。
Memory Allocation 記憶體分配
Three kinds of memory from the same pool of RAM
來自同一個 RAM 池的三種記憶體
Global 全球
Allocated and initialized when the program is loaded. This isn’t free, but it’s close to it.
在程式加載時分配和初始化。這不是免費的,但接近於免費。Only works for specific patterns with a fixed amount of memory that will live for the entire duration of the program. That
僅適用於具有固定內存量的特定模式,該內存將在程序的整個運行期間存在。lets
andvars
declared at global scope
lets
和vars
在全域範圍內宣告static
stored propertiesstatic
儲存屬性
Stack 堆疊
Very cheap 非常便宜
Only works in certain patterns
僅在某些模式下運作Memory has to be scoped: there has to be a point in the current function where there will be no more uses of that memory
記憶體必須有範圍:在當前函數中必須有一個點,在該點之後不再使用該記憶體Local
lets
andvars
本地lets
和vars
Parameters 參數
Other temporary values 其他臨時值
Heap 堆
Very flexible 非常靈活
Allocatable at arbitrary times
隨時可分配Deallocatable at arbitrary times
隨時可釋放
More expensive 更昂貴
Used for class instances or features without strong enough static lifetime restrictions
用於類實例或特徵,沒有足夠強的靜態生命週期限制Shared ownership 共享擁有權
Multiple, independent references to same memory
多個獨立的參考指向相同的記憶體Managed by Swift using reference counting
由 Swift 使用引用計數管理incremented references are retains
增量引用是保留的decremented references are releases
減少的引用是釋放
Memory Layout 記憶體佈局
“Value” is a high-level concept of information content
「價值」是一個高層次的信息內容概念“Representation” is how a value looks in memory
「表示」是值在記憶體中的樣子“Inline Representation” is the inline portion of the representation
「內嵌表示」是表示的內嵌部分
Every value in Swift is a contained in some context
在 Swift 中,每個值都包含在某個上下文中
A local scope (e.g. local variables, intermediate results of expressions)
局部範圍(例如:局部變數、表達式的中間結果)An instance context (e.g. non-static stored properties)
實例上下文(例如非靜態儲存屬性)A global context (e.g. global variables, static stored properties)
全域上下文(例如,全域變數、靜態儲存屬性)A dynamic context (e.g. buffers managed by Array and Dictionary)
動態上下文(例如由 Array 和 Dictionary 管理的緩衝區)
Inline Representation 內聯表示法
Local scopes in Swift place inline representations in the function’s CallFrame if possible.
在 Swift 中,局部範圍如果可能,會將內聯表示放置在函數的呼叫框架中。What is the inline representation of an Array of Double? Array is a struct, and the inline representation of a struct is just the inline representation of all its stored properties. Array has a single stored property, and it’s a class reference. And a class reference is just a pointer to an object. CallFrame just stores that pointer.
陣列的雙精度浮點數的內聯表示是什麼?陣列是一個結構,而結構的內聯表示就是它所有儲存屬性的內聯表示。陣列有一個儲存屬性,它是一個類別參考。而類別參考只是一個指向物件的指標。呼叫框架僅僅儲存該指標。Structs, tuples, and enums use inline storage, where everything they contain is laid out inline in their container.
結構、元組和列舉使用內聯存儲,其中它們所包含的所有內容都在其容器中內聯佈局。Classes and actors use out-of-line storage, where everything they contain is laid out inline in an object, and the container stores a pointer to that object.
類別和演員使用外部儲存,其中它們所包含的所有內容都以內聯方式佈局在一個物件中,而容器則存儲指向該物件的指標。
Value Copying 值複製
The inline representation of an Array is a reference to a buffer object. References like this are managed using reference counting. A container has ownership of an Array value, meaning there’s an invariant that the underlying array buffer has been retained as part of storing the value into the container. The container is then responsible for eventually balancing that retain with a release.
陣列的內聯表示是一個對緩衝區物件的引用。這樣的引用是通過引用計數來管理的。一個容器擁有一個陣列值,這意味著有一個不變性,即底層的陣列緩衝區在將值存儲到容器中時已被保留。然後,容器負責最終平衡該保留與釋放。
Using a value can - Consume - Transfering Ownership - If the compiler can see that aren’t any more uses of the original variable, it should be able to transfer the value without a copy. - Mutate - Temporary Ownership - Variable expects to have ownership after - Swift prevents using variables during calls - Borrow - Asserting nobody can consume of mutate - Calling a normal method on a value or class reference - Passing a value to a normal or borrowing
parameter - Reading a value of a property
使用值可以 - 消耗 - 轉移所有權 - 如果編譯器能夠看到原始變數沒有更多的使用,它應該能夠在不複製的情況下轉移值。 - 變更 - 暫時所有權 - 變數期望在之後擁有所有權 - Swift 防止在調用期間使用變數 - 借用 - 斷言沒有人可以消耗或變更 - 在值或類別引用上調用普通方法 - 將值傳遞給普通或 borrowing
參數 - 讀取屬性的值
Mechanics of copying 複製的機制
Value: copying the inline representation so that you get a new inline representation with independent ownership.
值:複製內聯表示,以便您獲得一個具有獨立所有權的新內聯表示。Class value: copying the ownership of the reference, which just means retaining the object it refers to.
類別值:複製參考的擁有權,這僅意味著保留它所指向的物件。Struct value: recursively copying all of the struct’s stored properties.
結構值:遞歸複製結構的所有儲存屬性。Large structs can lower performance for multiple copies
大型結構體可能會降低多個副本的性能
Write types with value semantics, where a copy of the value behaves like it’s totally unrelated to where copied from. Structs behave this way, but always use inline storage. Class types use out-of-line storage, but they naturally have reference semantics. One way to get both out-of-line storage and value semantics is to wrap the class in a struct and then use copy-on-write. The standard library uses this technique in all of Swift’s fundamental data structures, like Array, Dictionary, and String.
撰寫具有值語義的類型,其中值的副本行為就像與其複製來源完全無關。結構體以這種方式運作,但始終使用內聯存儲。類型使用外聯存儲,但它們自然具有引用語義。獲得外聯存儲和值語義的一種方法是將類包裝在結構體中,然後使用寫時複製。標準庫在所有 Swift 的基本數據結構中使用這種技術,例如 Array、Dictionary 和 String。
Written By 撰寫者
Dalton Alexandre 達爾頓·亞歷山大
Contributed Notes | GitHub | X/Twitter | Blog
貢獻的註解 | GitHub | X/Twitter | 部落格
Missing anything? Corrections? Contributions are welcome!
有遺漏嗎?有修正嗎?歡迎貢獻!
Related Sessions 相關會議
在 Swift 中使用不可複製類型
開始使用 Swift 中的不可複製類型。了解在 Swift 中複製的含義、何時可能想要使用不可複製類型,以及值的擁有權如何讓您清楚地表達您的意圖。
開始使用 Instruments
Xcode 中的 Instruments 應用程式提供了一套豐富的工具和範本,用於分析您的應用程式性能。了解所有有關 Instruments 的資訊,並獲得識別代碼瓶頸的策略。看看如何利用時間分析和興趣點追蹤的力量,對您的代碼進行有意義的更改,從而顯著改善應用程式的響應能力。
理解 Swift 性能
在這個進階課程中,了解結構、類別、協議和泛型在 Swift 中是如何實現的。學習它們在不同性能維度上的相對成本。看看如何應用這些資訊來加速你的代碼。
Legal Notice 法律聲明
All content copyright © 2012 – 2024 Apple Inc. All rights reserved. Swift, the Swift logo, Swift Playgrounds, Xcode, Instruments, Cocoa Touch, Touch ID, FaceID, iPhone, iPad, Safari, Apple Vision, Apple Watch, App Store, iPadOS, watchOS, visionOS, tvOS, Mac, and macOS are trademarks of Apple Inc., registered in the U.S. and other countries. This website is not made by, affiliated with, nor endorsed by Apple.
所有內容版權 © 2012 – 2024 蘋果公司。保留所有權利。Swift、Swift 標誌、Swift Playgrounds、Xcode、Instruments、Cocoa Touch、Touch ID、FaceID、iPhone、iPad、Safari、Apple Vision、Apple Watch、App Store、iPadOS、watchOS、visionOS、tvOS、Mac 和 macOS 是蘋果公司的商標,在美國及其他國家註冊。本網站並非由蘋果公司製作、與其關聯或獲得其認可。