Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
わいわいswiftc#19Genericsの特殊化
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
Iceman
April 20, 2020
Programming
480
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
わいわいswiftc#19Genericsの特殊化
Iceman
April 20, 2020
More Decks by Iceman
See All by Iceman
わいわいswift#39 Swiftの型をTypeScriptで表す
sidepelican
0
350
わいわいswiftc#35夢が広がる!コード生成でどこでもSwift
sidepelican
0
480
元ゲーム開発者が贈る描画パフォーマンス改善 / Rendering performance improvement from a game developer
sidepelican
4
1.8k
わいわいswiftc#17Genericsの特殊化
sidepelican
0
100
SwiftUI: 更新検知と値の生存期間
sidepelican
2
1.2k
クックパッドiOSアプリのパフォーマンス改善
sidepelican
0
800
DispatchQueue.syncが動作するスレッド
sidepelican
0
390
Other Decks in Programming
See All in Programming
JavaDoc 再入門
nagise
1
420
Oxcを導入して開発体験が向上した話
yug1224
4
340
正しくソフトウェアを作る、前提を疑うための認知の視点 / doubt-premise
minodriven
21
7k
A2UI という光を覗いてみる
satohjohn
1
160
Mujeres en SEO Summit 2026 - Greatest Disaster Hits en Web Performance
guaca
0
200
生成AI時代にこそ効くGo | Why Go Works in the Age of Generative AI
mom0tomo
8
3.3k
キャリア迷子上等 ─ "ない道"は自分で作ればいい
16bitidol
3
2.3k
Hunting Vulnerabilities in Symfony with LLMs
vinceamstoutz
0
560
ローカルLLMを使ってB2Bサービスを作っていての学び
yaotti
0
220
セキュリティの専門家じゃなくてもできる。「セキュリティ意識」をアップデートして サプライチェーン攻撃への耐性を高めよう。
tk3fftk
5
950
スマートグラスで並列バイブコーディング
hyshu
0
260
さぁV100、メモリをお食べ・・・
nilpe
0
160
Featured
See All Featured
Neural Spatial Audio Processing for Sound Field Analysis and Control
skoyamalab
0
350
The Mindset for Success: Future Career Progression
greggifford
PRO
0
370
Conquering PDFs: document understanding beyond plain text
inesmontani
PRO
4
2.8k
Getting science done with accelerated Python computing platforms
jacobtomlinson
2
240
Statistics for Hackers
jakevdp
799
230k
SEO Brein meetup: CTRL+C is not how to scale international SEO
lindahogenes
1
2.7k
We Are The Robots
honzajavorek
0
260
The Cult of Friendly URLs
andyhume
79
6.9k
My Coaching Mixtape
mlcsv
0
150
Designing Dashboards & Data Visualisations in Web Apps
destraynor
231
55k
Impact Scores and Hybrid Strategies: The future of link building
tamaranovitovic
0
310
Music & Morning Musume
bryan
47
7.2k
Transcript
わいわいswiftc #19 SwiftのGenerics関数の特殊化 Twitter @iceman5499 2020年4⽉20⽇ 1
Generics関数の特殊化とは 実際に使⽤される型パラメータをコンパイル時に埋め込んで 展開し、その型専⽤の実装を⽣やす最適化 func f<T>(_ v: T) {} f(Int(1)) ↓
Intをあらかじめ埋め込む func f<T>(_ v: T) {} func f(_ v: Int) {} // Int に特殊化された関数 f(Int(1)) 2
Generics関数の特殊化の利点 Swiftのジェネリクスは実⾏時にいろいろなことをやるのでオ ーバーヘッドがある 特殊化されているとそのいろいろを無視できるので速い いろいろの例 メタタイプの取り出し witness table経由での関数の呼び出し swiftc p3.swift
-emit-sil | code - 3
特殊化が⾏われるタイミング SILOptimizerのフェーズで⾏われる (引⽤元: https://www.slideshare.net/kitasuke/sil-for-first-time-leaners/1 by kitasuke) 4
特殊化される様⼦を観察する func f<T>(_ v: T) -> T { v }
@inline(never) func g() -> UInt16 { f(UInt16(5)) // ← これが特殊化される } g() このコードをコンパイルしてみて、最適化前と最適化後で g 関数 のSILの変化を観察してみる 5
最適化する前の g の状態 $ swiftc -emit-sil p5.swift // g() sil
hidden [noinline] @$s2p51gs6UInt16VyF : $@convention(thin) () -> UInt16 { bb0: %0 = alloc_stack $UInt16 // users: %8, %9, %6 %1 = integer_literal $Builtin.Int16, 5 // user: %2 %2 = struct $UInt16 (%1 : $Builtin.Int16) // user: %4 %3 = alloc_stack $UInt16 // users: %4, %7, %6 store %2 to %3 : $*UInt16 // id: %4 // function_ref f<A>(_:) %5 = function_ref @$s2p51fyxxlF : $@convention(thin) <τ_0_0> (@in_guaran... %6 = apply %5<UInt16>(%0, %3) : $@convention(thin) <τ_0_0> (@in_guaran... dealloc_stack %3 : $*UInt16 // id: %7 %8 = load %0 : $*UInt16 // user: %10 dealloc_stack %0 : $*UInt16 // id: %9 return %8 : $UInt16 // id: %10 } // end sil function '$s2p51gs6UInt16VyF' 6
UInt16のメタタイプを渡して呼出をしている (↓前ページの⼀部を抜粋) // function_ref f<A>(_:) %5 = function_ref @$s2p51fyxxlF :
$@convention(thin) <τ_0_0> (@in_guaran... %6 = apply %5<UInt16>(%0, %3) : $@convention(thin) <τ_0_0> (@in_guaran... $ swift demangle s2p51fyxxlF $s2p51fyxxlF ---> p5.f<A>(A) -> A 7
最適化するとどうなるか。 $ swiftc -O -Xllvm -sil-print-all p5.swift *** SIL function
after #60, stage HighLevel+EarlyLoopOpt, pass 12: GenericSpecializer (generic-specializer) // g() sil hidden [noinline] @$s2p51gs6UInt16VyF : $@convention(thin) () -> UInt16 { bb0: %0 = alloc_stack $UInt16 // users: %8, %10, %11 %1 = integer_literal $Builtin.Int16, 5 // user: %2 %2 = struct $UInt16 (%1 : $Builtin.Int16) // user: %4 %3 = alloc_stack $UInt16 // users: %6, %4, %9 store %2 to %3 : $*UInt16 // id: %4 // function_ref specialized f<A>(_:) %5 = function_ref @$s2p51fyxxlFs6UInt16V_Tg5 : $@convention(thin) (UInt16… %6 = load %3 : $*UInt16 // user: %7 %7 = apply %5(%6) : $@convention(thin) (UInt16) -> UInt16 // user: %8 store %7 to %0 : $*UInt16 // id: %8 dealloc_stack %3 : $*UInt16 // id: %9 %10 = load %0 : $*UInt16 // user: %12 dealloc_stack %0 : $*UInt16 // id: %11 return %10 : $UInt16 // id: %12 } // end sil function '$s2p51gs6UInt16VyF' 8
GenericSpecializerを通過すると特殊化された実装が⽣える // function_ref specialized f<A>(_:) %5 = function_ref @$s2p51fyxxlFs6UInt16V_Tg5 :
$@convention(thin) (UInt16… %6 = load %3 : $*UInt16 // user: %7 %7 = apply %5(%6) : $@convention(thin) (UInt16) -> UInt16 // user: %8 $ swift demangle s2p51fyxxlFs6UInt16V_Tg5 $s2p51fyxxlFs6UInt16V_Tg5 ---> generic specialization <Swift.UInt16> of p5.f<A>(A) -> A 9
最適化完了後の g の状態 // g() sil hidden [noinline] @$s2p51gs6UInt16VyF :
$@convention(thin) () -> UInt16 { bb0: %0 = integer_literal $Builtin.Int16, 5 // user: %1 %1 = struct $UInt16 (%0 : $Builtin.Int16) // user: %2 return %1 : $UInt16 // id: %2 } // end sil function '$s2p51gs6UInt16VyF' 最終的には f の呼び出しが全部消えた 10
特殊化されたf<A>(_:)ができてから消える様⼦ $ swift -O -Xllvm -sil-print-all -Xllvm -sil-print-only-functions=s2p51fyxxlFs6UInt16V_Tg5 p5.swift 途中から⽣えて最後には無くなってる様⼦が確認できる
11
特殊化されるための条件を調べる 特殊化は実⾏時パフォーマンスの観点で積極的に⾏われてほ しい SILOptimizerの実装を⾒て、特殊化のための条件を調べる 具体的な実装はこのへん GenericSpecializer.cpp Generics.cpp 12
特殊化の流れ . 型パラつきの apply 命令(関数呼び出し)を集める . 特殊化できないものを除外する . 集めた apply
ごとに特殊化 ここでも精査され特殊化に失敗しうる . 特殊化に成功した apply の呼び出し先を新しい関数に置き換 えて、既存の呼び出しを削除 特殊化できないものを除外する 特殊化できないものとは? 13
特殊化できない呼び出し① いろいろな条件がある 呼び出し先の実装が参照不可能(外部モジュールなど) 特殊なアノテーションがついてる @_semantics(optimize.sil.specialize.generic.never) func f<T>() {} dynamicがついてる dynamic
func f<T>() {} 14
特殊化できない呼び出し② archetype(実⾏時に決まる型)がある 特殊化の過程でarchetypeがすべて潰されると最適化で きるようになることがある 型が複雑すぎる 型パラがネストを含め50個以上ある NTDの要素が2000個以上ある 要素2000個以上のタプル 引数2000個以上のクロージャ 15
archetype(実⾏時に決まる型)があって失敗する例 f の呼び出しに対して特殊化が失敗する例(左)。右はどうだろうか。 16
archetype(実⾏時に決まる型)があって失敗する例 classのほう(前ページ右)は特殊化に成功する 事前にdevirtualizeが適⽤されてよりシンプルなコードに なっているため これは1⽉ごろの挙動で、現在のmaster(364d2dc2)で は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)) } } 17
型が複雑すぎる 型パラがネストを含め50個以上ある 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) // ← されない 18
型が複雑すぎる 要素2000個以上のタプル typealias Width1999 = (Int8,Int8,Int8,Int8, ... ,Int8) func use<T>(_
v: T.Type) -> Int8 { 9 } func f() -> Int8 { use(Width1999.self) // ← 特殊化されない } $ swiftc -O typetoowidth1998.swift $ swiftc -O typetoowidth1999.swift 19
特殊化できない呼び出し③ -Osize がついてる 特殊化の無限ループが起こるとき どういう状況でそうなるかわからなかった 20
型⾃体の特殊化は⾏われない struct A<T> {} に対して struct A<Int> {} みたいな型パラ 埋め込み済みの型は⽣成されない?
他に特殊化を⾏ってる箇所がなさそう、実際の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