-
Swift PerformanceSwift 2021. 3. 1. 17:03
안녕하세요. 그린입니다!
이번 포스팅에서는 Swift Performance, 스위프트 퍼포먼스에 대해 이야기 해볼까 합니다😃
우선 Performance는 뭐라고 생각하면 좋을까요?
저는 퍼포먼스가 성능이라고 생각됩니다.
같은 애플리케이션을 구현하더라도 클래스로 구현하냐 구조체로 구현하냐에 따라 앱 성능 차이가 있을겁니다.
이러한 앱의 성능을 최대한으로 이끌어낼 수 있어야 좋은 앱이라고 할 수 있겠죠??🙌
- 성능을 자료구조에서는 어떤식으로 표현해볼 수 있을까요?
: 시간복잡도 (빅오 표기법, Big O)로 표현해볼 수 있습니다.
- 시간복잡도?
: 코드 알고리즘의 성능을 특수한 표현으로 나타낸 표기법으로 성능이랑 연관되어 있습니다.
: 예를들어 이중 for문을 시간복잡도로 나타내면 O(N^)으로 나타낼 수 있습니다. 정확하게는 O(N^+2)와 같지만 여기서
+2처럼 큰 사이즈에서 결과에 영향을 미치는 부분이 작다면 무시하고 표기할 수 있습니다.
- Swift에서의 예시
: 배열과 딕셔너리를 예를 들어 시간복잡도를 생각해 볼 수 있습니다.
: 배열은 해당 자료를 탐색하려면 최악의 경우 전체 인덱스를 다 접근해야함으로 O(N)이 되고
딕셔너리는 키와 값이 있기에 O(1)의 시간복잡도를 가짐으로 딕셔너리가 더 나은 성능을 발휘할 수 있습니다.
: 배열에서도 reverse()와 reversed() 메서드가 있는데 비슷한 역할을 하지만 다른 시간 복잡도를 가집니다.
-> reverse()는 배열의 순서를 반전시켜 다시 저장시킴으로 O(N),
reversed()는 배열의 순서를 반전시켜 새로 저장시킴으로 O(1)의 시간 복잡도를 가집니다.
- 성능에 영향을 주는 3가지 사항
: WWDC2016에서도 소개되었는데 아래 그림과 같이 스위프트 성능에 크게 3가지 영향으로 나타낼 수 있습니다.
1) Allocation: 인스턴스의 저장 위치, 즉 할당의 의미를 지닙니다. (Stack or Heap)
: 스택은 함수 호출과 함께 할당되고 완료되면 소멸 (스택포인터를 변수 값으로 관리 가능)
: 힙은 동적이여서 성능에는 덜 효율적 (오버헤드가 많을 경우 스레드 안정성을 체크하고 무결성을 지켜야하기에)
: 배열은 값 타입 사용이 권장된다. (클래스 사용 시 NSArray로 사용될 수 있어 컴파일러 최적화가 불가능하다)
-> ContigousArray로 사용하면 요소들이 메모리에 매번 연속적으로 저장됨으로 클래스 사용 시 권장된다.
: 오버플로우가 발생하지 않는 조건에서도 해당 오버플로우가 발생하는지 안하는지 체크하는것만으로도 성능에 영향을 준다.
-> Swift Flags에 추가하여 전처리기 사용으로 분기처리를 하면 성능에 도움이 된다.
-. 요부분에 추가
2) Reference Counting: 참조 횟수 (ARC와 연관)
: 힙 메모리를 위해 사용되고 있는 ARC
: Strong, Weak, Unowned에 따라 성능이 다름 (참조 종류에 따라)
-> Weak: 참조하는 인스턴스가 메모리에서 해제 시 ARC가 자동으로 참조를 nil로 변경
(추적 후 nil로 변경함으로 오버헤드가 발생될 수 있음)
-> Unowned: ARC가 참조 값을 nil로 변경하지 않음 (오버헤드 발생하지 않지만 없는 값을 참조할 때 앱 크래쉬가 날 수 있음),
참조하는 인스턴스가 항상 메모리에 존재할거라는 확신?이 있을때 사용
: Class VS Struct
-> Class는 Reference Counting이 존재
-> Struct는 Stack 메모리에 할당되어 Reference Counting 미존재
-> String, Array, Dictionary의 타입은 값 타입인데 Class 타입을 가지고 있다??
(COW(Copy On Write)를 위해... copy 시 Reference Counting 발생)
3) Method Dispatch: 메서드 호출 시 디스패치 종류 (Static or Dynamic), 실행하는 코드의 메모리 주소를 탐색하는 프로세스
: 메서드 디스패치의 종류로는 크게 Static과 Dynamic이 있고, Dynamic에서는 Table과 Message Dispatch로 구분된다.
: 스위프트에서는 위 3가지의 메서드 디스패치를 모두 갖고있다.
: Static
-> 가장 빠름
-> 컴파일 중 호출 메서드를 알고 처리 (메서드 인라이닝 방식)
: Dynamic
-> 클래스의 메서드 주소 저장을 위한 포인터 배열 생성 (Table로 호출되어 런타임 시 Table 조회)
: Dynamic 방식의 Class를 Static 방식으로 변경하는 방법?
-> final, private, fileprivate과 같은 접근제한자를 선언해줌으로써 상속이 없는 클래스라는걸 명시하여 방식을 변경한다.
- 보다 빠른 성능의 조건
1) Stack에 인스턴스 할당
2) Reference Counting 적음
3) Static Dispatch
[정리]
오늘 스위프트 퍼포먼스에 대해 이정도 이해를 하였는데 너무너무 어려운 부분인것같다...
더 확실한 이해를 하기 위해 공부를 계속 해보면서 하나씩 차근차근 이해해봐야겠다.
잘 아직 결론이 안난 부분은 두가지이다.
1) 구조체니까 추상화하는 과정이 있어야하는데 추상화의 성능을 좋게하려면 위의 3가지 조건이 필요하다는말..???
2) 클래스는 힙/레퍼런스 카운팅/다이나믹 메서드 디스패치 위 성능이 좋지않은 3가지 조건을 모두 사용하는데 그럼 왜 클래스를 사용할까? 상속때문에?? 더 좋은 방법은 없을까???
더 공부하고 보완해봐야겠습니다👀
[참고자료]
https://developer.apple.com/videos/play/wwdc2016/416/
'Swift' 카테고리의 다른 글
GCD & Operation (0) 2021.04.09 lazy var (0) 2021.03.17 ARC 심화 (0) 2021.02.17 Type Casting (0) 2021.01.29 Protocol Oriented Programming (0) 2021.01.28