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
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
340
わいわい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
TAKTでAI駆動開発の品質を設計する
j5ik2o
6
1.1k
不変条件と整合性境界—ビジネスが決める設計判断と実現パターン / Invariants and Consistency Boundaries
nrslib
13
3.6k
Contextとはなにか
chiroruxx
0
150
「エンジニアインターン、どうやって取った?」準備のリアルを語るLT会 Progate BAR
akiomatic
0
130
キャリア迷子上等 ─ "ない道"は自分で作ればいい
16bitidol
3
1.9k
AIで効率化できた業務・日常
ochtum
0
120
CLIであることを活かしたGitHub Copilot CLI活用術 / GitHub Copilot CLI Pro Tips & Tricks
nao_mk2
1
1.2k
さぁV100、メモリをお食べ・・・
nilpe
0
130
jQueryをバージョンアップする前に使いたいjQuery Migrate
matsuo_atsushi
0
200
Modding RubyKaigi for Myself
yui_knk
0
910
JavaDoc 再入門
nagise
0
310
These Five Tricks Can Make Your Apps Greener, Cheaper, & Nicer
hollycummins
0
280
Featured
See All Featured
Docker and Python
trallard
47
3.9k
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
21
1.5k
Game over? The fight for quality and originality in the time of robots
wayneb77
1
190
SEO in 2025: How to Prepare for the Future of Search
ipullrank
3
3.5k
Intergalactic Javascript Robots from Outer Space
tanoku
273
27k
Leadership Guide Workshop - DevTernity 2021
reverentgeek
1
300
The Spectacular Lies of Maps
axbom
PRO
1
790
Side Projects
sachag
455
43k
What's in a price? How to price your products and services
michaelherold
247
13k
Dominate Local Search Results - an insider guide to GBP, reviews, and Local SEO
greggifford
PRO
0
190
The Psychology of Web Performance [Beyond Tellerrand 2023]
tammyeverts
49
3.5k
jQuery: Nuts, Bolts and Bling
dougneiner
66
8.5k
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