Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
わいわいswiftc#17Genericsの特殊化
Search
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
Iceman
January 17, 2020
Programming
110
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
わいわいswiftc#17Genericsの特殊化
Iceman
January 17, 2020
More Decks by Iceman
See All by Iceman
わいわいswift#39 Swiftの型をTypeScriptで表す
sidepelican
0
370
わいわいswiftc#35夢が広がる!コード生成でどこでもSwift
sidepelican
0
490
元ゲーム開発者が贈る描画パフォーマンス改善 / Rendering performance improvement from a game developer
sidepelican
4
1.9k
わいわいswiftc#19Genericsの特殊化
sidepelican
0
500
SwiftUI: 更新検知と値の生存期間
sidepelican
2
1.2k
クックパッドiOSアプリのパフォーマンス改善
sidepelican
0
820
DispatchQueue.syncが動作するスレッド
sidepelican
0
400
Other Decks in Programming
See All in Programming
AI Agent時代のリアーキテクチャ戦略と実践
hokaccha
9
5.1k
re:Inventに行く前に知っておきたい現地参加のノウハウ
nokomoro3
0
220
[2026-09-26]空論ジェネリックプロセス~テスト資産とAIで紡ぐ、再現可能なパフォーマンスチューニングの話~
tosite
0
220
The Past, Present, and Future of Enterprise Java
ivargrimstad
0
580
AWS DevOps Agentで インシデント対応をAIに任せたい
honmarkhunt
7
3.3k
モバイル交通系ICへのチャージ実例から考える、クロスプラットフォーム開発におけるiOS実機テスト設計とCI運用
yusuga
1
550
Workers Cache を知る
syumai
0
300
wkhtmltopdfの次どうするか問題2026
willnet
2
1.8k
ゲームコントローラやキーボードのファームウェアをSwiftで書く
kishikawakatsumi
1
270
海上で動くGoサーバー: goroutineとchannelでさばく航行データストリーム
atsuki_seo
0
980
AGENTS.md Is Not Enough:Build Skills, Don't Download Them
lx_t
0
130
ハーネス設計入門 〜 基礎知識の整理から実務へのステップアップ 〜
kinopeee
18
18k
Featured
See All Featured
Optimising Largest Contentful Paint
csswizardry
37
4k
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
141
35k
Winning Ecommerce Organic Search in an AI Era - #searchnstuff2025
aleyda
2
2.2k
[SF Ruby Conf 2025] Rails X
palkan
3
1.4k
Automating Front-end Workflow
addyosmani
1369
210k
The Director’s Chair: Orchestrating AI for Truly Effective Learning
tmiket
1
310
Fantastic passwords and where to find them - at NoRuKo
philnash
52
3.9k
Side Projects
sachag
456
43k
How STYLIGHT went responsive
nonsquared
100
6.3k
Keith and Marios Guide to Fast Websites
keithpitt
413
23k
Unlocking the hidden potential of vector embeddings in international SEO
frankvandijk
0
950
Groundhog Day: Seeking Process in Gaming for Health
codingconduct
0
380
Transcript
わいわいswiftc #17 SwiftのGenerics関数の特殊化 Twitter @iceman5499 2020年1⽉17⽇ 1
Generics関数の特殊化とは コンパイル時にgenerics関数に対し実際に使⽤される型パラ メータを埋め込んで展開し、型ごとに専⽤の実装を⽣やす最 適化 func f<T>(_ v: T) {} f(1)
↓ 最適化としてIntをあらかじめ埋め込む func f<T>(_ v: T) {} func f(_ v: Int) {} // 最適化時に⽣成される関数 f(1) 2
Generics関数の特殊化の利点 Swiftのジェネリクスは実⾏時にいろいろなことをやるのでオ ーバーヘッドがある 特殊化されているとそのいろいろを無視できるので速い 参考 C++のテンプレートやRustのジェネリクスはコンパイル 時にすべて展開されるのでオーバーヘッドがない ただし展開される数だけバイナリが太る 3
特殊化が⾏われるタイミング SILOptimizerのフェーズで⾏われる (引⽤元: https://www.slideshare.net/kitasuke/sil-for-first-time-leaners/1 by kitasuke) 4
特殊化される様⼦を観察する // generics.swift struct A<T> { var value: T }
@inline(never) func f() -> UInt16 { let a = A(value: UInt16(6)) // ← これが特殊化される return a.value } f() $ swift -O -Xllvm -sil-print-all -Xllvm -sil-print-only-functions=s8generics1fs6UInt16VyF generics.swift 5
*** SIL module before Guaranteed Passes *** // f() sil
hidden [noinline] [ossa] @$s8generics1fs6UInt16VyF : $@convention(thin) () - > UInt16 { bb0: %0 = alloc_stack $A<UInt16> // users: %13, %11, %9 %1 = metatype $@thin A<UInt16>.Type // user: %9 %2 = integer_literal $Builtin.IntLiteral, 6 // user: %5 %3 = metatype $@thin UInt16.Type // user: %5 // function_ref UInt16.init(_builtinIntegerLiteral:) %4 = function_ref @$ss6UInt16V22_builtinIntegerLiteralABBI_tcfC : $@convention (method) (Builtin.IntLiteral, @thin UInt16.Type) -> UInt16 // user: %5 %5 = apply %4(%2, %3) : $@convention(method) (Builtin.IntLiteral, @thin UInt16 .Type) -> UInt16 // user: %7 %6 = alloc_stack $UInt16 // users: %10, %9, %7 store %5 to [trivial] %6 : $*UInt16 // id: %7 // function_ref A.init(value:) %8 = function_ref @$s8generics1AV5valueACyxGx_tcfC : $@convention(method) <τ_0 _0> (@in τ_0_0, @thin A<τ_0_0>.Type) -> @out A<τ_0_0> // user: %9 %9 = apply %8<UInt16>(%0, %6, %1) : $@convention(method) <τ_0_0> (@in τ_0_0, @ thin A<τ_0_0>.Type) -> @out A<τ_0_0> dealloc_stack %6 : $*UInt16 // id: %10 %11 = load [trivial] %0 : $*A<UInt16> // users: %14, %12 debug_value %11 : $A<UInt16>, let, name "a" // id: %12 dealloc_stack %0 : $*A<UInt16> // id: %13 %14 = struct_extract %11 : $A<UInt16>, #A.value // user: %15 return %14 : $UInt16 // id: %15 } // end sil function '$s8generics1fs6UInt16VyF' 6
⼀番最初のフェーズではUInt16のメタタイプを渡して呼出 (↓前ページの⼀部を抜粋) %8 = function_ref @$s8generics1AV5valueACyxGx_tcfC : $@convention(method) <τ_0_0 >
(@in τ_0_0, @thin A<τ_0_0>.Type) -> @out A<τ_0_0> // user: %9 %9 = apply %8<UInt16>(%0, %6, %1) : $@convention(method) <τ_0_0> (@in τ_0_0, @th in A<τ_0_0>.Type) -> @out A<τ_0_0> $ swift demangle s8generics1AV5valueACyxGx_tcfC $s8generics1AV5valueACyxGx_tcfC ---> generics.A.init(value: A) -> generics.A<A> 7
*** SIL function after #69, stage HighLevel+EarlyLoopOpt, pass 13: GenericSpecializer
(generic-specializer) // f() sil hidden [noinline] @$s8generics1fs6UInt16VyF : $@convention(thin) () -> UInt16 { bb0: %0 = alloc_stack $A<UInt16> // users: %9, %11, %13 %1 = metatype $@thin A<UInt16>.Type // user: %8 %2 = integer_literal $Builtin.Int16, 6 // user: %3 %3 = struct $UInt16 (%2 : $Builtin.Int16) // user: %5 %4 = alloc_stack $UInt16 // users: %7, %5, %10 store %3 to %4 : $*UInt16 // id: %5 // function_ref specialized A.init(value:) %6 = function_ref @$s8generics1AV5valueACyxGx_tcfCs6UInt16V_Tg5 : $@convention(me thod) (UInt16, @thin A<UInt16>.Type) -> A<UInt16> // user: %8 %7 = load %4 : $*UInt16 // user: %8 %8 = apply %6(%7, %1) : $@convention(method) (UInt16, @thin A<UInt16>.Type) -> A<UInt16> // user: %9 store %8 to %0 : $*A<UInt16> // id: %9 dealloc_stack %4 : $*UInt16 // id: %10 %11 = struct_element_addr %0 : $*A<UInt16>, #A.value // user: %12 %12 = load %11 : $*UInt16 // user: %14 dealloc_stack %0 : $*A<UInt16> // id: %13 return %12 : $UInt16 // id: %14 } // end sil function '$s8generics1fs6UInt16VyF' 8
GenericSpecializerを通過すると特殊化された実装が⽣える // function_ref specialized A.init(value:) %6 = function_ref @$s8generics1AV5valueACyxGx_tcfCs6UInt16V_Tg5 :
$@convention(method) (UInt16, @thin A<UInt16>.Type) -> A<UInt16> // user: %8 %7 = load %4 : $*UInt16 // user: %8 %8 = apply %6(%7, %1) : $@convention(method) (UInt16, @thin A<UInt16>.Type) -> A<UInt16> // user: %9 store %8 to %0 : $*A<UInt16> // id: %9 $ swift demangle s8generics1AV5valueACyxGx_tcfCs6UInt16V_Tg5 $s8generics1AV5valueACyxGx_tcfCs6UInt16V_Tg5 ---> generic specialization <Swift.UInt16> of generics.A.init(value: A) -> generics.A<A> 9
*** SIL module after #2, stage IRGen Preparation, pass 1:
LoadableByAddress (loadable-address) // f() sil hidden [noinline] @$s8generics1fs6UInt16VyF : $@conventi on(thin) () -> UInt16 { bb0: %0 = integer_literal $Builtin.Int16, 6 // user: %1 %1 = struct $UInt16 (%0 : $Builtin.Int16) // user: %2 return %1 : $UInt16 // id: %2 } // end sil function '$s8generics1fs6UInt16VyF' 最終的には全部消える 10
fを観察した結果 最初はそのまま呼び出されていた A<T>.init(_:) が ↓ A<UInt16>.init(_:) に特殊化され ↓ さらにインライン化されて消えた 11
特殊化されたA<UInt16>.initができてから消える様⼦ $ swift -O -Xllvm -sil-print-all -Xllvm -sil-print- only- functions=s8generics1AV5valueACyxGx_tcfCs6UInt16V_Tg5
generics.swift 途中から⽣えて最後には無くなってる様⼦が確認できる 12
具体的なSILOptimizerにおける最適化プ ロセス このへんで⾏われてる GenericSpecializer.cpp Generics.cpp 13
特殊化の流れ 1. 型パラつきの apply 命令を集める 2. 特殊化できないものを除外する 3. 集めた apply
ごとに特殊化 ここでも精査され特殊化に失敗しうる 4. 特殊化に成功した apply の呼び出し先を新しい関数に置き換 えて、既存の呼び出しを削除 14
特殊化できない呼び出し① いろいろな条件がある 呼び出し先の実装が参照不可能(外部モジュールなど) アノテーションがついてる dynamicがついてる @_semantics(optimize.sil.specialize.generic.never) func f<T>() {} dynamic
func f<T>() {} 15
特殊化できない呼び出し② archetype(実⾏時に決まる型)がある 特殊化の過程でarchetypeがすべて潰されると最適化で きるようになることがある 型が複雑すぎる 型パラがネストを含め50個以上ある NTDの要素が2000個以上ある 要素2000個以上のタプル 引数2000個以上のクロージャ 16
archetype(実⾏時に決まる型)があって失敗する例 17
archetype(実⾏時に決まる型)があって失敗する例 protocolのほう(前ページ左)は特殊化に失敗する classのほう(前ページ右)は特殊化に成功する 事前にdevirtualizeが適⽤されてよりシンプルなコードに なっているため // 特殊化までにこのようなコードに変形されている func g() ->
Bool { let result = makeAorB() if let a = result as? A { return a.f(UInt16(9)) } else let b = result as? B { return b.f(UInt16(9)) } else { return result.f(UInt16(9)) } } 18
型が複雑すぎる struct A<T> { var v: T init(_ v: T)
{ self.v = v } } func use<T>(_ v: T) -> T { v } let a49 = A(A(A(A(A(A(A(A(A(A(A(A(A(A(A(A(A(A(A(A( A(A(A(A(A(A(A(A(A(A(A(A(A(A(A(A(A(A(A(A( A(A(A(A(A(A(A(A(A(Int16(9))))))))))))))) ))))))))))))))))))))))))))))))))))) let a50 = A(a49) use(a49) // ← 特殊化される use(a50) // ← されない 19
特殊化できない呼び出し③ 特殊化の無限ループが起こるとき どういう状況でそうなるかわからなかった 20
型⾃体の特殊化は⾏われない struct A<T> {} に対して struct A_Int {} みたいな型パラ 埋め込み済みの型は⽣成されない
GenericSpecializer は apply 命令にしか処理を⾏わない 他に特殊化を⾏ってる箇所がなさそう、実際のSILを⾒てもそ れっぽい動きがなさそう そもそも型⾃体を特殊化するメリットはほとんど無いのか も?意⾒募集 21
おまけ: @_specializeによる特殊化 @_specializeをつけると型を指定して特殊化できる 内部で型による分岐が⾛る特殊化が⾏われる // @_specialize(where T == Int) func
f<T: CustomStringConvertible>(_ v: T) -> String { v.description } 22
特殊化後のコードのイメージ // @_specialize(where T == Int) func f<T: CustomStringConvertible>(_ v:
T) -> String { if let v = v as? Int { return v.description } else { return v.description } } 同じ .description 呼び出しだが後者はwitness tableの参 照を⾏うのでオーバーヘッドがある 23
24