Choosing the Right Abstarction Mechanism
- 추상화시 stack, heap 어디에 할당할지 생각해라
- 추상화시 이 인스턴스가 얼마나 많은 reference counting overhead를 발생시킬지 생객해라
- 인스턴스에서 method를 호출할때 정적으로 불릴지 동적으로 불릴지 생각해라
- 성능 개선하려면 dynamism, runtime을 피해라
Allocation
- stack의 끝에서만 push, pop이 발생한다.
- stack은 빠르게 할당하고 해제한다.
- 동적이고 stack보다 비효율 적이다.
- 빈공간을 찾고 할당
- 동기화 작업 비용이 매우 크다
- stack에 나란히 저장
- 두개는 독립적인 인스턴스이다
- 사용이 끝나면 메로리에서 해제되고 stack포인터가 올라간다.
- heap에 참조를 위한 메모리할당하려고 한다.
- Swift는 heap을 lock하고 적당한 사이즈의 메모리 블록을 찾습니다.
- 찾으면 메모리에 point1 초기화
- struct와 다르게 2 word 더 할당해 4 word(4 칸)을 사용합니다(파란색 칸)
- 참조를 공유해 의도하지 않은 상태변화가 발생할 수 있습니다.
- point1,2 사용이 끝나면 Swift는 lock하는 대신 메모리에서 해제합니다.
- 사용하지 않는 블락을 재배치하고 스택에서 pop합니다.
성능 개선
- cache의 String은 아무값이나 들어갈 수 있어 불안정
- String을 구성하는 Character들이 간접적으로 힙영역에 저장돼 캐쉬 히트를 하더라도 힙 할당이 발생합니다.
- struct를 사용하면 더 안전하다
- 일급 객체여서 dictionary의 키값으로 사용가능하다.
- 이제는 캐쉬히트해도 힙할당을 하지않습니다.(stack에 할당된다.)
- Swift는 reference count를 사용해서 힙 영역을 관리합니다.
- 참조가 추가되면 count가 증가하고 참조가 제거되면 count가 감소합니다.
- count가 0이 되면 메모리에서 해제됩니다.
- reference count관려해 count를 올리고 내리는것은 빈번하고 말고도 더 많은 것들이 있습니다.
- count를 올리고 내리는것을 몇가지 수준의 indirection과 관련있습니다,.
- thread safe 상태를 유지하기 위해 많은 비용이 듭니다.
- 초기화시 ref count 1
- retain: ref count 증가
- release: ref count 감소
- Label은 struct이지만 String과 UIFont 타입 때문에 ref count가 필요합니다.
- struct도 reference를 포함하면 reference counting이 필요합니다.
- 참조 타입들을 값타입으로 변경해 referenc counting, heap allocation 발생을 줄임
Method dispatch
- compile time에 어떤 함수가 실행될지 결정
- inline을 포함해 적극적인 최적화 가능
- run time에 어떤 함수가 실행될지 결정
- 결정 후 구현부로 점프
- inline, 다른최적화 안됨
- Static Dispatch는 Call Stack을 만들고 분해하지 않고
- Static Dispatch는 inline으로 Call Stack 오버헤드도 발생하지 않는다.
- polymorphism 때문에 dynamic dispatch 사용한다.
- [Drawable] 배열은 참조로 구성돼 크기는 동일하다
- d.draw는 컴파일 타임에 어떤 함수를 실행할지 결정할 수 없다(Point, Line 어떤게 될지 모름)
- 컴파일러는 클래스에 해당 클래스의 type 정보에 대한 포인터 field를 추가하고 static memory에 저장합니다.
- draw를 호출하면 컴파일러는 실행할 구현의 포인터를 포함하는 type 및 static memory의 가상 메소드 테이블을 조회합니다.
- 이후 실제 인스턴스를 implict self-parameter로 전달합니다.
- final 키워드를 사용하면 class도 static dispatch 할수있다.
참고