본문 바로가기

학습활동

Swift Performance (1)

Swift Performance

  • Performance: 앱의 성능.성능의 예시
  • → 성능을 최대한으로 이끌어 내어 좋은 앱을 만들기 위해 노력해야함
  • 자료구조에서 시간복잡도(Big O). ex) O(n) → y = x
  • 시간복잡도를 비교할 때 O(n^2+2)의 +2는 결과에 영향이 적다고 판단하여 O(n^2)과 동일하게 취급
  • 마찬가지로 O(2n) = O(n)

다형성

  • 함수의 기능적 다형성과 상속에 의해 만들어진 계층 관계를 활용하는 기술
  • 비슷하지만 살짝 다른 타입들을 효율적으로 표현하기 위한 개념
  • 장점 : 재사용성, 확장성, ISP, 코드 가독성

Perfomance에 영향을 주는 3가지

→ 오른쪽으로 갈 수록 성능 저하, 왼쪽으로 갈 수록 성능 향상

→ 참조타입을 활용하여 추상화를 진행하면 오른쪽으로 편향될 수 있지만, 성능에 미치는 영향을 고려하여 여러 상황에서 값타입과 참조타입 중 어떤 것을 선택해야할 지 결정하는데 도움이 될 수 있다.


Allocation

Allocator의 Performance를 높이기 위해 1) 처리 속도 2) 메모리 사용 효율성을 최대화 해야함

Stack

  • 지역변수와 매개변수 등이 저장되는 영역
  • 이 영역에 할당된 변수는 함수 호출이 완료되면 사라짐
  • 컴파일 시 크기 결정될 가능성이 높음
  • ValueType이 할당됨
  • LIFO 구조
  •  Stack 포인터를 끝에 유지하면 Push(값을 넣음), Pop(값을 뺌)을 구현할 수 있음

→ Stack에 메모리 할당 : Decrement(감소). 스택은 가장 큰 메모리 주소값부터 시작하기때문에 메모리 할당이 주소값의 역순으로 진행. 높은 주소에서 낮은 주소로 할당

→ Stack에서 메모리 할당 해제 : Increment(증가). 가장 큰 메모리 주소값를 향해 가기때문에 메모리 해제가 주소값의 순차적으로 진행.

  • Stack Overflow : 스택의 메모리가 부족하여 넘치는 것
  • Overhead : 부가적인 자원 소모

Heap

  • 동적 메모리 할당을 위한 영역
  • 런타임 시 크기 결정
  • 프로그래머가 할당 및 해제를 해줘야 함→ 메모리 할당 해제를 하려면 해당 메모리를 적절한 위치로 reinsert해야함
  •  ARC에서 메모리 할당 시점을 체크하고, 더이상 필요없는 메모리는 덮어씌운다
  •  메모리 할당을 위해 Heap의 데이터 구조를 Search하여, 미사용의 적절한 데이터 블록을 찾아야함
  • Stack보다 동적(Dynamic)이지만 효율이 떨어짐
  •  다중 스레드가 동시에 Heap 메모리 할당을 할 수 있기 때문에, locking 혹은 기타 동기화 매커니즘을 사용하여 Integrity(무결성)을 보호해야함 → 이것이 Heap 할당의 가장 큰 비용을 차지
  • ReferenceType, ValueType이 전부 할당될 수 있음
  • → Value Type안에 Reference Type이 있을 경우
  • 메모리 파편화(Fragmentation)가 발생하여 메모리 사용 효율성이 떨어질 수 있음

  • Array, String, Dictionary, Set 등 값타입이어도 Heap에 저장되는 경우가 있다
  • → 크기가 정해져있지 않은 경우→ String의 경우 링크드 리스트처럼 저장될 수도 있음
  • → String의 경우 실제 문자 내용은 heap에 저장하는 반면, 상수 String은 Stack에 저장
  • → 변동의 가능성이 있기 때문에 Heap에 저장
  • Dynamic LifeTime을 가진 메모리(런타임에 메모리 생성 및 해제)를 heap에 할당할 수 있음

값 타입 메모리 할당 예시

  • → 코드 실행 전, point1과 point2의 인스턴스에 대해 Stack에 공간을 할당

  • Stack 영역에 순차적으로 저장됨
  • point에 대한 인스턴스를 만들 때, Stack에 이미 할당된 메모리를 초기화하는 것
  • point2는 독립적인 인스턴스로 생성
  • → point2의 값을 변경하면 point2의 값만 변경하게 됨

값 타입 메모리 할당 해제 예시

  • Stack 포인터를 순차적으로 Increment시켜서 메모리 할당 해제
값 타입이기 때문에 var로 선언되어야만 한다
참조 타입의 경우 let으로 선언 가능하다

참조 타입 메모리 할당 예시

  • 코드 실행 전, point1과 point2의 인스턴스에 대해 Stack에 공간을 할당
  • → stack에 순차적으로 저장하는 것이 아닌, reference를 위한 메모리를 할당하는 것
  • → 즉, Heap에 할당될 메모리를 참조할 공간을 만든 것

  • 다중 스레드가 동시에 Heap 메모리 할당을 할 수 있기 때문에, locking 혹은 기타 동기화 매커니즘을 사용하여 Integrity(무결성)을 보호해야함
  • → heap영역을 lock을 하고 적당한 크기의 메모리 블록을 검색, 메모리 공간을 확보

  • Heap에 공간을 찾으면 저장!
  • → Stack은 각 인스턴스 별로 하나의 공간을 할당받음
  •  Heap은 값을 저장하는 공간(초록박스)과 이를 관리하는 공간(파란박스)으로 할당되며, 이때 같은 주소를 Stack에서 바라보게 됨. 그렇기 때문에 point2에서 값을 변경하면 point1의 값도 변경
  • → 노란박스는 하나의 참조타입 인스턴스의 모든 내용

참조 타입 메모리 할당 해제 예시

  • Heap을 lock하고, 사용하지 않는 블럭을 적절한 위치로 재삽입(덮어쓰기)
  • 그 후 Stack을 Pop 할 수 있음

Class는 Heap 할당이 필요하기 때문에, 비용이 더 많이 든다

  • Class는 ID 및 Indirect Storage와 같은 특성이 있음
  • 이 특성이 필요한 경우 값타입이 아닌 참조타입을 사용해야함
  •  ID(값 식별자)
  •  간접저장소(값을 직접 저장하는 것이 아닌 값이 저장되어있는 주소를 저장하는 곳)
  • 추상화에 이러한 특성이 필요하지 않다면 구조체를 사용하라

값 타입 중 Heap에 저장되는 경우 해결하기

문제 상황

  • String을 cache의 Key값으로 사용하는 경우
  • → Dictionary는 값 타입이지만, String은 heap에 간접적으로 저장됨
  • → 그래서 makeBalloon 메소드가 호출될 때마다 cache hit이 일어나도 heap 할당이 생기게 됨
  • cache hit : cache를 가져올 때

해결 방법

  • Key를 String이 아닌 Struct를 사용하여 Heap이 아닌 Stack에 저장될 수 있게 함
  • → heap 할당 시 생길 수 있는 overhead 문제 방지, 속도 향상시킬 수 있음
728x90

'학습활동' 카테고리의 다른 글

KeyChain  (0) 2023.12.11
Swift Performance (2)  (0) 2023.12.11
URL Loading System  (0) 2023.11.30
동시성 프로그래밍  (0) 2023.11.08
Swift Concurrency II  (0) 2023.09.28