반응형

 

Hacking with Swift 사이트의 강좌 번역본입니다.

 

[원문 : https://www.hackingwithswift.com/quick-start/swiftui/how-to-use-instruments-to-profile-your-swiftui-code-and-identify-slow-layouts]

 

How to use Instruments to profile your SwiftUI code and identify slow layouts

 

Xcode의 Instruments 도구는 SwiftUI를 위한 환상적인 분석 세트가 제공되며, 뷰가 얼마나 자주 그려지는지, 뷰의 본문(body)를 계산하는데 얼마나 느렸는지, 그리고 시간이 지남에 따라 상태가 어떻게 바뀌었는지를 확인할 수 있게 해줍니다. 

 

우선, Instruments에서 볼수 있는 흥미로운 결과를 제공하기 위한 무언가가 필요합니다. 따라서 다음 코드는 언제나 0.01초마다 동작하는 타이머를 만들고, 본문(body) 뷰는 임의의 UUID를 보여주고 탭할때마다 보여주는 값을 증가하는 버튼을 가지고 있습니다. 

import Combine
import SwiftUI

class FrequentUpdater: ObservableObject {
    let objectWillChange = PassthroughSubject<Void, Never>()
    var timer: Timer?

    init() {
        timer = Timer.scheduledTimer(
            withTimeInterval: 0.01,
            repeats: true
        ) { _ in
            self.objectWillChange.send()
        }
    }
}

struct ContentView: View {
    @ObservedObject var updater = FrequentUpdater()
    @State private var tapCount = 0

    var body: some View {
        VStack {
            Text("\(UUID().uuidString)")

            Button(action: {
                self.tapCount += 1
            }) {
                Text("Tap count: \(tapCount)")
            }
        }
    }
}

 

시뮬레이터에서 해당 코드를 실행하는 경우에 항상 변화하는 값을 가지고 있기 때문에, 끊임없이 다시 그리는 것을 알게 될 것입니다. 

주의 : 다음은 Instruments가 흥미로운 데이터를 보여주기 위해 SwiftUI로 많은 작업을 수행하도록 특별히 설계된 스트레스(stress) 테스트입니다 - 실제 앱에서는 위의 코드를 사용하고 싶지 않습니다.

 

코드 측정하기(Instrumenting our code)

 

Instruments를 사용해서 코드를 실행하기 위해 Cmd+I를 누르고, SwiftUI instrument를 고릅니다. 그것이 보여질때, 앱을 실행하고 관찰을 시작하기 위해 녹화(record) 버튼을 누릅니다. 이제 버튼을 10번정도 클릭하는 동안에 몇 초 동안을 실행하고, Instruments에서 정지를 누릅니다 - 충분한 데이터가 있습니다.

 

기본적으로 SwiftUI instrument는 다양한 것들을 알려줍니다:

 

  1. 그 시간동안 얼마나 많은 뷰가 만들어졌는지와 만드는데 걸린 시간 (View Body)
  2. 뷰의 프로퍼티가 무엇인지와 시간이 지남에 따라 어떻게 변경되었는지(View Properties)
  3. Core animation 커밋 (Core Animation Commits)
  4. 각 함수를 호출하는데 걸린 정확한 시간 (Time Profiler)

이러한 각각의 측정값(instruments)은 SwiftUI 앱에서 성능 문제를 진단하고 해결하는데 도움이 될 수 있으므로, 시간을 내서 살펴볼 가치가 있습니다.

 

스트레스 테스트 샌드박스(sendbox)의 경우에 빨간 깃발(flag)인 View Body, View Properties, Core Animation Commits에 대해 단색(solid)의 벽(walls)이 보일 것입니다. 그것은 SwiftUI가 지속적으로 뷰를 재생성해야 하는 것만이 아니라는 것을 말해주지만, 프로퍼티(properties)는 끊임없이 변하고 그 결과 Core Animation은 이를 유지하기 위해서 과도한 작업을 해야합니다.

본문 호출 감시하기(Monitoring body invocations)

 

View Body 트랙을 선택하는 경우(instruments의 목록에서 첫번째 행입니다) Instruments가 그 결과를 SwiftUI와 프로젝트의 분해하는 것이 가능하다는 것을 알 수 있어야 하며, 전자(former)의 경우에는 텍스트 뷰나 버튼과 같은 기본 타입이 되고, 후자(latter)는 사용자정의 뷰 타입을 포함합니다. 우리의 경우에, 뷰의 이름이기 때문에, 사용자정의 뷰에 ContentView가 나타나야 하는 것을 의미합니다.

 

이제, 여기에서 볼수 없는 것은 SwiftUI 뷰에 코드를 완벽하게 1 대 1 맵핑하며, SwiftUI가 가능한한 작은 작업을 하기 위해 뷰 계층구조를 공격적으로 축소하기 때문 입니다. 따라서, 코드에서 VStack을 만드는 것을 기대하지 마세요 - 그것은 앱에서 사실상 무료입니다.

 

이 화면에서, 중요한 숫자는 Count와 Avg Duration 입니다 - 각각 몇번 만들어졌는지와 얼마나 걸렸는지 입니다. 왜냐하면 이것은 스트레스 테스트 이기 때문에 Count 숫자가 매우 높은것을 알수 있지만, 레이아웃은 작은 것이기에 Avg Duration은 수십 마이크로 초가 될 것입니다.

상태 변화 추적하기(Tracking state changes)

 

다음으로, instruments의 목록에서 두번째 행인, View Properties 트랙을 선택합니다. 이것은 모든 뷰에 대해 모든 프로퍼티(properties)를 보여주고, 현재 값과 이전 값 모두를 포함합니다.

 

예제 앱에 숫자를 추가해서 탭 될때 라벨을 변경하는 버튼이 있고, instrument에서 바로 볼 수 있습니다 - 뷰 타입이 ContentView과 Property Type State인 것을 찾아보세요.

 

슬프게도, Instruments는 (아직) 정확한 프로퍼티 이름을 보여줄수 없으며, 여러개의 정수형 상태를 추적하는 경우에 더 혼동될 수 있습니다. 하지만, 다른 비법이 있습니다: 녹화하는 윈도우의 상단에 현재 뷰 위치를 표시하는 화살표가 있고, 드래그하는 경우에 시간이 지남에 따라 앱 상태가 어떻게 되는지를 정확히 알 수 있습니다 - 버튼을 탭할때마다 상태 정수가 1씩 올라가는 것을 알수 있고 어떤 일이 발생하는지 알기 위해 앞뒤로 감을 수 있습니다.

 

상태 변경될때 느리게 다시 그리거나 다른 작업을 직접 확인할 수 있기 때문에, 엄청난 기능을 해제한 것입니다 - 실행중인 모든 시점에 앱의 정확한 상태를 검사할 수 있는 타임머신에 있는것 같습니다. 

느리게 그리기 식별하기(Identifying slow draws)

 

비록 SwiftUI는 성능향상을 위해서 Metal을 바로 사용할 수 있으며, 대부분의 경우에 그리는 것에 대해 Core Animation을 사용하는 것을 선호합니다. 이는 Instruments에서 내장된 Core Animation 프로파일링 도구를 자동으로 얻는 것과, 비싼 커밋(commits)을 감지하는 기능을 포함하는 것을 의미합니다.

 

Core Animation은 트랜잭션(transaction)으로 알고 있는, 여러개의 변경사항을 하나의 그룹에서 사용할때가 가장 효과적입니다. 하나의 트랜잭션(transaction)에서 선택한 작업을 효과적으로 쌓을 수 있고, 그리는 작업을 진행하도록 CA에게 요청합니다 - 트랜잭션 커밋(committing)이라고 합니다.

 

따라서, Instruments가 비싼 Core Animation 커밋을 보여줄때, 실제로 보여지는 것은 SwiftUI가 업데이트 때문에 화면에 픽셀을 다시 그리도록 강요된 횟수입니다. 이론상으로 SwiftUI는 body 프로퍼티의 새로운 출력과 이전 출력을 비교할 수 있어야 하기 때문에, 이것은 실제 앱의 상태가 다른 뷰 계층 구조에서의 결과인 경우에만 발생합니다. 

느리게 함수 호출하는 것 찾기(Looking for slow function calls)

 

마지막으로 중요한 트랙은 마지막 하나이며, 코드의 각 부분에서 소요된 시간을 정확하게 보여주는, Time Profiler입니다. 이것은 Instruments에 있는 정규(regular) 시간 프로파일러와 동일한 작업이지만, 이전에 시도해보지 않은 경우에 다음은 최소한으로 알아야 하는 것입니다.

  1. 기본적으로 가장 무거운 스택 추적을 오른쪽에서 확장된 상세 윈도우로 보여주며, 실행하는데 가장 오래 걸린 코드 부분입니다. 밝은 코드(macOS의 color scheme에 따라 흰색 또는 검정)는 여러분이 작성한 코드입니다; 어두운 코드(회색)는 시스템 라이브러리 코드입니다.
  2. 왼쪽에는 생성했던 모든 스레드와 호출된 함수를 자세히 살펴볼수 있는 공개지표와 함수를 호출한 함수를 볼수 있습니다. 대부분의 작업은 start 내부에서 발생할 것입니다.
  3. 혼란스러운것을 피하기 위해 하단에 있는 Call Tree 버튼을 클릭할 수 있고, Hide System Libraries를 선택합니다. 이것은 여러분이 작성한 코드만 보여줄것이지만, 시스템 라이브러리를 잘못 사용하고 있는 문제의 경우에는 도움이 되지 않을 수 있습니다. 
  4. 특정 세부사항으로 바로 가려면, Call Tree를 클릭하고 뒤집기 위해 Invert Call Tree를 선택할수 있으므로 leaf 함수는(tree의 끝부분에 있는 것) 상단에 보여지고, 해당 공개 지표(disclosure indicators )는 그것들을 호출했던 함수들을 따라 내려갈(올라갈) 수 있습니다.

비록 시간 프로파일러(Time Profiler)는 성능 문제를 식별하는데 매우 유용하지만, 종종 가장 무거운 스택을 추적하는 것은 가장 큰 문제를 강조(highlight)할 것입니다. 

마지막 팁(Last tips)

 

코드를 프로파일링하기 전에, 알아야할 몇가지가 있습니다.

  1. 앱 성능의 작은 부분을 검사할때, 해당 범위를 클릭하고 드래그해야 하므로, 앱의 해당 부분에 대한 통계만을 알수 있습니다. 이것은 버튼을 누른 것에 대한 응답 처럼, 특정 동작에 대한 성능에 집중할 수 있습니다. 
  2. Instruments에 있는 단색(solid color)의 바를 볼수 있지만, 멀리서만 그렇게 보입니다 - 자세히 보기 위해 Cmd를 누른 상태에서 - +를 눌러 확대할 수 있습니다. 
  3. 가장 정확한 수치는, 언제나 실제 기기에 대한 프로파일입니다. 
  4. 코드를 프로파일링 한 결과로 변경하고자 하는 경우에, 항상 한번에 하나씩 변경하세요. 2가지를 변경하면 하나가 성능이 20%증가하고 다른 하나는 10% 감소할 수 있지만, 함께 처리한다는 것은 아마도 성능이 10%증가되었다고 생각하는 것을 의미합니다. 
  5. Instruments는 릴리즈 모드에서 코드를 실행하며, Swift의 모든 최적화가 가능합니다. 코드에 추가한 디버그 플래그(flags)에도 영향을 주므로 주의해야 합니다.
반응형
Posted by 까칠코더
,