[WWDC16] Understanding Swift Performance - (1)

brick·2023년 7월 29일
0

Watch WWDC 📽️

목록 보기
3/5
post-thumbnail

Understanding Swift Performance

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 할수있다.

참고

0개의 댓글