Swift에서의 Type Inference(타입 추론)의 작동 원리와 효율적 사용을 위한 전략
타입 추론과 타입 어노테이션
1. 타입 추론(Type Inference)
컴파일러가 주변 컨텍스트에서 세부 정보를 파악할 수 있을 때 코드에서 명시적 타입 정보를 생략할 수 있음
1
let name = "molu"
- 작동 과정
- 1단계 : 어휘분석(Lexical analysis)
컴파일러는 입력 파일 바이트를 숫자 및 문자열과 같은 단위로 분할, 공백과 주석을 버리는 어휘 분석을 수행함. 예를 들어 위 예제를
let
,name
,=
,"
,molu
,"
로 분할함- 2단계 : 구문 분석(Syntax analysis)
컴파일러는 swift 문법에 기초한 abstract syntax tree 를 생성함 관련하여 자료는 여기를 참고
3단계 : 의미론적 분석(Semantic analysis) = 컴파일러 경고 및 오류 발행 단계
molu
에 대한 추가적인 정보가 없기 때문에 이 단계에서 타입추론이 일어남. 저 표현식에서 변수 선언의 오른쪽이string
이라서string
이어야 함을 추론함그 이후
컴파일러는 중간 코드를 생성한 다음 그 코드를 최적화하고, 최종적으로 어셈블리 코드를 생성함. 그 이후 컴파일러 외부의 tool chain은 마지막 실행파일을 생성하는 연결 단계로 이어짐
- 장점
- swift 언어의 구조적 특정중
설계에 의한 안정성
- 직접 타입을 명시해주지 않아도 된다는 간편함
- swift 언어의 구조적 특정중
- 단점
- 타입 추론은 컴파일러가 타입을 추론하는 과정에서 타입 어노테이션보다 시간이 조금 더 걸림
- 초기값이 없는 경우, 타입을 유추할 수 없어서 사용할 수 없음
- 컴파일러가 초기값을 보고 유추할 때, Character/String, Double/Float 등 애매하면 더 큰 범위의 자료형으로 냅다 지정해버림
2. 타입 어노테이션(Type Annotation)
변수나 상수를 선언할 때 그 타입을 명시적으로 선언해 줌으로써 어떤 타입의 값이 저장될 것인지를 컴파일러에 직접 알려주는 문법
1
let name: String
- 장점
- 컴파일 시간?
- 타입 추론으로 원하는 자료형을 얻지 못할 때 사용할 수 있음
- 초기값 없어도 사용할 수 있음
컴파일 시간에 대하여
1. 타입 어노테이션이 빠르다?
두 가지 방법의 컴파일 시간을 비교했을 때 타입명시가 빠르다는 실험 결과가 나와있음
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var inference: Double = 0
for _ in 0..<10 {
let inferenceTime = measureTime {
for _ in 0..<100000 {
let _ = 1
}
}
inference += inferenceTime
}
var annotation: Double = 0
for _ in 0..<10 {
let annotationTime = measureTime {
for _ in 0..<100000 {
let _: Int = 1
}
}
annotation += annotationTime
}
2. 타입 추론이 빠르다?
위 처럼 타입 어노테이션이 추론하는 과정이 생략되기 때문에 컴파일 시간이 더 빠르다는 것이 보통의 이론이지만, 컴파일러의 성능 향상으로 타입 추론의 컴파일 속도가 더 빠르다는 실험결과도 있음
1
2
3
4
let a = "hello, world!" // type is inferred
let b = String("hello, world!") // type is inferred from String(...) and then passed to the root (the constant b)
let c: String = .init("hello, world!") // type inference is not required
let d: String = "hello, world!" // type inference is not required
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Benchmark #1: xcrun swiftc -typecheck a.swift
Time (mean ± σ): 175.7 ms ± 3.5 ms [User: 82.9 ms, System: 81.9 ms]
Range (min … max): 171.0 ms … 182.8 ms 16 runs
Benchmark #1: xcrun swiftc -typecheck b.swift
Time (mean ± σ): 224.8 ms ± 2.8 ms [User: 131.1 ms, System: 81.7 ms]
Range (min … max): 220.2 ms … 228.2 ms 13 runs
Benchmark #1: xcrun swiftc -typecheck c.swift
Time (mean ± σ): 672.3 ms ± 8.0 ms [User: 568.3 ms, System: 93.7 ms]
Range (min … max): 662.4 ms … 685.1 ms 10 runs
Benchmark #1: xcrun swiftc -typecheck d.swift
Time (mean ± σ): 213.3 ms ± 2.0 ms [User: 119.8 ms, System: 81.6 ms]
Range (min … max): 210.2 ms … 216.5 ms 13 runs
위처럼 가장 첫번째 경우(타입추론) 이 가장 빠른 컴파일 속도를 보임.
- 타입추론은 컴파일러가 타입을 추론하는 과정이 추가되기 때문에 컴파일 시간이 늘어남
- 하지만 타입을 명시하게 되면 컴파일러가 명시된 타입과 초기값을 비교하는 작업이 추가되어 컴파일 시간이 늘어날 수 있음
- 애초에 타입 명시와 타입 추론의 속도 차이 자체도 밀리세컨드 단위 차이로 미세함
- 업데이트마다 컴파일러의 성능이 어떤 지표에 대하여 오락가락하는 것으로 보임
🤔 명시적 타입 선언과 타입 추론 중 어느 경우가 더 바람직한가
컴파일러가 업데이트 될 때마다 성능이 오락가락하는 것으로 보임.. 애초에 비교가 무의미할 정도로 차이가 미미하기 때문에 성능을 비교하기 보다는 다른 측면의 장점(ex. 가독성 등)을 비교하여 적재적소에 사용하는 것이 현명해보임