본문 바로가기

학습활동

Swift Performance (2)

Reference Counting

  • Heap에 저장된 메모리는 Reference count를 계산하여 0이 되었을 때, 메모리 해제를 안전하게 할 수 있음
  • 이 작업은 빈번하게 일어나며, 단순 count를 계산하는 것에서 끝나지 않음
  • Reference Count의 증감을 계산할 때 Heap에 다중 스레드 문제가 생길 수 있기 때문에, 단일 스레드만 Count를 계산할 수 있도록 해야함 → Reference Count 계산은 빈번하게 일어나기 때문에 스레드 접근을 제한하는 비용은 유의미함
  • → Reference Count를 atomic하게 함으로 안전하게 관리
  • nonatomtic이 기본으로 바뀜
 atomic
- 다른 프로세스나 쓰레드에 의해 interrupt 되지 않고 수행이 완료될 때까지 무결성을 유지
- 안정성이 올라가지만 처리속도가 낮아질 수 있음

 nonatomic
- 멀티쓰레드 환경에서 데이터의 무결성이 보장되지 않아도 될 때 사용
- 안정성이 떨어지지만 처리속도가 높아질 수 있음

값 타입보다 참조 타입을 활용하는 게 더 나은 경우

  • Struct(값타입)안에 참조타입이 있는 경우, Struct의 인스턴스가 복사 되어도 결국 같은 주소를 바라보게 되기 때문에 이중으로 Reference Count가 계산되게 됨
  • Struct에 참조타입이 있는 경우 Reference의 수에 비례하여 Reference Count 오버헤드가 되는 것
  • 둘 이상의 참조타입이 있는 경우 Struct가 아닌 Class를 사용하는 것이 좋다

Method Dispatch

Static Dispatch

  • 컴파일 타임에서 구현을 결정
  • → RunTime에서 호출 시 이 함수구현부로 바로 점프!
  • 컴파일러가 실제로 어떤 구현이 실행될지 가시적으로 보여줌
  • 인라인을 통해 코드 최적화를 할 수 있음 → Dynamic Dispatch보다 빠름
    • 인라인 : 메서드 호출을 해당 메서드의 본문으로 대체하는 컴파일러 최적화 방법
      • 효과가 있다고 판단되는 경우에만 컴파일 시점에 메소드 호출 부분에 메소드 내용을 붙여넣음
      • Call stack 오버헤드 줄임
      • 짧은 메서드나 루프 안에서 불리는 경우 큰 효과
      • 자세한 내용은..https://ios-development.tistory.com/905

Dynamic Dispatch

  • 컴파일 타임에서 어떤 구현으로 갈 지 직접 결정할 수 없음
  • 런타임에서 구현을 찾은 다음 바로 시작
  • → Swift에서는 클래스마다 함수 포인터들의 배열인 vTable(Virtual Method Table)이라는 것을 정적 메모리에 저장
  • → 하위 클래스가 메서드를 호출할 때, 이 vTable 를 참조하여 실제 호출할 함수를 결정

  • 인라인을 통해 코드 최적화 불가능
  • Objective-C의 메서드의 경우 동적으로 호출 
  • → ex) objc_msgSend (anObject, @selector (doMethod:) , aParameter)
  • → 강력하고 유연한 특징을 가지고 있지만 성능 저하 요소
  • → 특히 Loop 안에서 빈번하게 Method 호출이 일어나는 경우
  • 이러한 단점에도 불구하고 Dynamic Dispatch는 polymorphism(다형성)을 가능하게 한다는 장점을 갖음 → 추상화가 가능해짐

→ Drawable을 채택하는 함수들의 포인터를 배열로 저장하여 for구문을 통해 클래스의 draw 메서드를 호출한 상태

→ 컴파일러는 유형을 통해 vTable을 조회

  • 클래스는 기본적으로 override가 가능하기 때문에 Dynamic Dispatch으로 메서드를 전달함그래도 결론적으로 Dynamic Dispatch는 성능을 저하시키기 때문에 Static Dispatch로 바꿀 수 있으면 바꿔서 성능 향상 시켜라!
  • Dynamic Dispatch는 런타임 오버헤드를 발생시키는 대신 언어 표현력을 높임

Class 성능 향상 시키기

1. Class에 final을 붙이기

// 특정 프로퍼티, 메서드에 final 붙이기
class ParticleModel {
	final var point = ( x: 0.0, y: 0.0 )
	final var velocity = 100.0

	final func updatePoint(newPoint: (Double, Double), newVelocity: Double) {
		point = newPoint
		velocity = newVelocity
	}

	// final을 붙이지 않았기 때문에 override 가능
	func update(newP: (Double, Double), newV: Double) {
		updatePoint(newP, newVelocity: newV)
	}
}

// class 전체에 final 붙이기
final class ParticleModel {
	var point = ( x: 0.0, y: 0.0 )
	var velocity = 100.0
	// ...
}

 

2. private 키워드를 적용하여 한 파일에서 참조되는 선언에 final를 유추시키기

// private을 붙이면 사용 범위가 현재 파일로 제한됨
// 이런 경우 컴파일러가 override가능한 모든 선언을 찾을 수 있게 됨
// 서브 클래싱을 하지 않을 것이라고 컴파일러 추론한 경우
// -> final키워드를 자동으로 유추 가능하게 됨
class ParticleModel {
	private var point = ( x: 0.0, y: 0.0 )
	private var velocity = 100.0

	private func updatePoint(newPoint: (Double, Double), newVelocity: Double) {
		point = newPoint
		velocity = newVelocity
	}

	// private이 없어서 dynamic
	func update(newP: (Double, Double), newV: Double) {
		updatePoint(newP, newVelocity: newV)
	}
}

// 클래스 전체에 붙이는 경우
private class ParticleModel {
	var point = ( x: 0.0, y: 0.0 )
	var velocity = 100.0
	// ...
}

 

3. 전체 모듈 최적화를 사용하여 내부 선언에 대한 final을 유추

  • swift는 기본적으로 컴파일할 때 모듈 내의 파일을 하나씩 컴파일 함
  • 전체 모듈 최적화를 활성화시키면 모든 모듈이 동시에 컴파일 됨
  • 이때 컴파일러는 전체 모듈에 대해 추론이 가능해져, override가 없는 internal의 선언에 final을 유추 가능해짐


참고 링크

Swift Performance

Swift ) (1) Understanding Swift Performance (Swift성능 이해하기)

Understanding Swift Performance - WWDC16 - Videos - Apple Developer

스위프트 성능 이해하기 - 유용하

Increasing Performance by Reducing Dynamic Dispatch - Swift Blog

Swift) Dispatch를 이용한 성능 최적화

[iOS] nonatomic vs atomic 알아보기

 

 

 

728x90

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

APFS-iOS File System  (0) 2024.01.08
KeyChain  (0) 2023.12.11
Swift Performance (1)  (0) 2023.12.11
URL Loading System  (0) 2023.11.30
동시성 프로그래밍  (0) 2023.11.08