Instruments로 Hang 감지해보기

피터·2025년 11월 4일

Instruments

목록 보기
1/3
post-thumbnail

오늘은 Instruments Tutorials를 실습하며 학습해본 내용입니다.

튜토리얼 링크: https://developer.apple.com/tutorials/instruments/

문제 상황

해당 프로젝트는 Collection 탭을 누르면 첫 시작 시 UI가 버벅이는(프리징) 현상이 발생합니다.

참고: Hang이 무엇이고 유저 반응성(User Responsiveness)이 무엇인지는 다음에 자세히 다루어 보겠습니다.

Time Profiler 시작하기

1. Time Profiler 실행

프로파일링을 시작하기 위해 Profile (Command + I)을 눌러 Time Profiler를 실행합니다.

왜 Time Profiler로 먼저 시작하나요?

Time Profiler는 시간 관련 프로파일링에 사용되는 instrument들의 세트로 구성된 새로운 문서를 생성합니다. 앱에서 느린(sluggish) 동작을 느꼈지만 무엇을 찾아야 할지 아직 모르겠다면 좋은 시작점이 됩니다.

2. 녹화 설정

새로운 Instruments 문서를 생성하면 "Next Recording" 뷰에서 시작합니다. 여기서 어떤 데이터를 기록할지 instrument 세트를 선택하여 설정할 수 있습니다.

Tip

이전 단계에서 선택한 템플릿이 새 문서에 기본적으로 추가되는 instrument들을 결정합니다. 언제든지 나중에 변경할 수 있으며, 심지어 녹화 실행 후에도 "Next Recording" 뷰로 돌아와서 다음 녹화를 위한 변경을 할 수 있습니다.

3. 프로파일링 녹화 시작

오른쪽 위의 Record 버튼을 누르고 동일하게 Collection 탭을 누르면 프로파일링이 시작됩니다.

왼쪽 사이드바에는 실행(Run)할 때마다 문서가 늘어나며, 변경사항을 비교하며 분석할 수 있습니다.

CPU 사용량 분석하기

Time Profiler 트랙 이해하기

상단의 Time Profiler 트랙은 CPU 사용량을 보여줍니다.

제가 사용하는 디바이스는 iPhone 15 모델입니다:

현재 CPU 사양을 보면:

  • 성능 코어(Performance): 2개
  • 효율 코어(Efficiency): 4개

Time Profiler는 전체 CPU의 총합을 나타내고 있는 것으로 보입니다.

CPUs 트랙 자세히 보기

상단의 CPUs 트랙을 눌러보겠습니다:

사양과 동일하게:

  • Performance로 표시된 CPU: 4번, 5번 (2개)
  • Efficiency로 표시된 CPU: 0~3번 (4개)

0번부터 시작하는 게 정말 개발자답네요!

프리징이 걸리는 시점에 성능 코어의 CPU를 매우 많이 사용하고 있는 것이 보입니다. 성능 CPU 2개를 동시에 사용하고 있네요.

Threads 트랙 분석하기

Threads 트랙을 살펴보겠습니다:

관찰 결과:

  • Main Thread가 엄청나게 사용되고 있습니다
  • 다른 스레드 11개가 존재하지만 거의 사용하지 않네요
  • 다른 스레드의 이름이 ImageViewer인데... 아, 앱 이름이었네요!

궁금증: Concurrency 공부할 때 스레드 개수는 CPU 코어 수와 동일하게 간다고 했던 것 같은데... 뭐가 맞는 걸까요?

어쨌든 Main Thread가 굉장히 바쁜 상태입니다.

Main Thread CPU 사용량 해석하기

프리징(Hang)을 찾았을 때는 Main Thread의 CPU 사용량을 확인해야 합니다:

  1. 사용량이 너무 많은 경우

    • Main Thread에서 과도한 작업을 하고 있다는 의미
    • → 어떤 작업이 CPU를 많이 사용하는지 찾아야 함
  2. 사용량이 너무 적은 경우

    • Main Thread가 막혀있다(blocked)는 의미
    • 다른 스레드나 시스템의 응답을 기다리고 있는 상태
    • → 메인 스레드를 막고 있는 대기 이벤트의 원인을 찾아야 함

Event Fetch Thread 이해하기

나머지 대부분의 스레드들은 일반적인 워커 스레드(generic worker threads)입니다. 여기서 윈도우 서버(Window Server)로부터 입력 이벤트를 가져오는 스레드를 찾아봐야 합니다.

스레드를 보면 ImageViewer 외에 다른 하나가 눈에 띕니다. com.apple...로 줄임말로 되어있는 부분을 확대해보겠습니다:

com.apple.uikit.eventfetch-thread라고 되어있네요!

이것이 윈도우 서버(화면)로부터 입력 이벤트를 가져오는 역할을 하는 스레드입니다. 사용자 입력을 윈도우 서버로부터 수신하여 앱의 메인 스레드에 전달하는 중요한 연결고리 역할을 합니다.

이벤트 처리 흐름의 비밀

여기서 헷갈리는 점이 있습니다: 사용자 입력을 받는 건 Main Run Loop라고 했는데, 왜 다른 스레드에서 입력을 받아서 메인 스레드로 전달하는 걸까요?

저도 이번에 정확히 알게 된 내용입니다. 공식 문서에서는 찾지 못했지만, 다음과 같은 흐름으로 동작한다고 합니다:

Hardware (터치스크린)
    ↓
IOKit/IOHIDFamily (커널 레벨)
    ↓
com.apple.uikit.eventfetch-thread (이벤트 수신)
    ↓
Event Queue
    ↓
Main Thread (이벤트 처리)

즉, Main Thread는 이벤트의 "처리"만 담당합니다.

그 이유는 Main Thread가 바쁠 경우 입력에 대한 누락이 생길 수 있기 때문입니다.

틀린 부분이 있다면 댓글 남겨주세요!

왜 이렇게 나눴을까?

❌ 만약 메인 스레드가 직접 받는다면:

// 메인 스레드가 바쁠 때
메인 스레드: UI 업데이트 중... (0.1초)
Window Server: "터치 이벤트 보낼게!"
메인 스레드: 바빠서 못 받음 ❌
→ 이벤트 유실 또는 지연

✅ eventfetch-thread를 사용하면:

eventfetch-thread: 항상 대기 중 ✓
Window Server: "터치 이벤트!"
eventfetch-thread: 즉시 받아서 큐에 저장 ✓
메인 스레드: UI 업데이트 끝나면 큐에서 꺼내서 처리

CPU 사용량 계산 방식


스레드 vs 프로세스 CPU 사용량:

  • 특정 스레드: 최대 100%까지 사용 가능 (단일 CPU 코어 하나를 100% 사용)
  • 프로세스 트랙: 프로세스 전체의 CPU 사용량을 합산하여 표시

하나의 프로세스는 여러 개의 병렬 스레드를 가질 수 있기 때문에, 프로세스 트랙에 표시되는 전체 CPU 사용량은 100%보다 훨씬 높을 수 있습니다.

예시: CPU 사용량이 200%라면, 해당 프로세스가 모든 스레드를 통해 CPU 코어 2개에 해당하는 자원을 사용하고 있다는 의미입니다.

Profile View로 Main Thread 작업 이해하기

이제 Main Thread가 바쁘다는 것을 알았으니, 구체적으로 무엇이 문제인지 파악해봅시다.

Detail Area 살펴보기

아래의 상세 영역을 봅시다:

이 영역은 Call TreeExtended Detail Area로 구성되어 있습니다.

  • 왼쪽: 모든 함수의 호출 트리(Call Tree)
  • 오른쪽: 가장 무거운 스택 추적(Heaviest Stack Trace)

이 영역은 어떤 트랙을 선택했는지에 따라 달라집니다.

Call Tree 분석하기

엄청난 Call Tree입니다! 아마 여기서 많은 분들이 해석을 포기하실 텐데요... 왜냐하면 제가 그랬거든요. 😅

이렇게 많은 이유: Time Profiler가 앱의 실행 흐름 전체를 기록하기 때문입니다. 호출 트리의 최상단은 앱이 실행되는 가장 기본적인 시작 함수들로 구성됩니다.

Heaviest Stack Trace 이해하기

'가장 무거운 스택 추적(The Heaviest Stack Trace)'은 성능 분석 도구(Instruments)의 핵심 기능입니다:

  1. 역할: 가장 많은 CPU 시간을 쓴 경로 하나를 자동으로 찾아 요약해 보여줍니다
  2. 원리: 가중치(CPU 시간)가 가장 높은 함수만 재귀적으로 따라갑니다
  3. 한계: 문제의 최종 지점은 알려주지만, 종종 시스템 함수만 보여서 결국 그 함수를 호출한 '내 코드'를 찾기 위해 전체 스택을 봐야 합니다

호출 트리의 요약을 보여주며, 수천 개의 노드를 포함하는 전체 호출 트리를 하나하나 걸러낼 필요 없이 관련 있는 프레임(함수 호출)을 식별해 줍니다.

색상으로 코드 구분하기

Instruments는 함수를 색상으로 구분하여 개발자가 어디에 집중해야 할지 알려줍니다:

  1. 기본 텍스트 색상

    • 의미: 여러분이 작성한 코드 (Xcode가 컴파일한 앱의 코드)
    • 조치: 수정 가능! 성능 문제의 원인이 여기에 있습니다 ✅
  2. 옅은 회색

    • 의미: Apple 시스템 라이브러리 함수
    • 조치: 수정 불가! 이 함수를 호출한 '기본 텍스트 색상'의 코드를 찾아야 합니다

문제 지점 발견하기

ThumbnailView.body.getter 행을 선택하면 관련 정보가 나타납니다:

분석 결과:

  • CPU 사용률: 98.3%
  • 실행 시간: 4.52초

이 부분에서 성능 문제의 원인이 발생했음을 명확히 알 수 있습니다!


다음 시간에...

성능 개선 방법 및 구체적인 문제 파악은 다음 시간에 이어서 하겠습니다.

profile
iOS 개발자입니다.

0개의 댓글