Performance Tooling
Instruments 성능 측정 기본
이미지·스크롤·웹뷰 성능 이론을 "측정"으로 잇는 도구. 기본만 정리(다운샘플 메모리 확인·피크 측정 실전 포함).
기본 인스트루먼트 5개
| 인스트루먼트 | 무엇 | 언제 |
|---|---|---|
| Time Profiler | CPU 어디서 시간 쓰나(Call Tree) | 스크롤 hitch·메인 스레드 병목 |
| Allocations | 메모리 할당(Call Tree·Generations) | 다운샘플 메모리·피크·growth |
| Leaks | 순환 참조 누수 | retain cycle |
| Core Animation | FPS·오프스크린(노랑)·블렌딩(빨강) | 스크롤 렌더 |
| (Xcode) Memory Graph / Debug 게이지 | 순환참조 그래프·순간 메모리 | 누수·피크 빠른 확인 |
Call Tree 핵심 옵션 3개
- Separate by Thread: 스레드별로 나눠 봄 → 메인 스레드 병목 찾기.
- Hide System Libraries: 시스템 프레임 숨겨 내 코드만.
- Invert Call Tree: 실제 시간 먹는 leaf 함수부터 위로 → 핫스팟 바로 보임.
⭐ Growth vs Peak (다운샘플 측정 맥락)
| 측정 | 무엇 | 쓰임 |
|---|---|---|
| Growth(Generations / Mark Generation) | 두 마크 사이 남아서 안 사라진 지속 증가분 | 누수·abandoned 메모리(끝나도 안 돌아오는 것) |
| Peak(high-water mark) | 작업 중 순간 최고점 | 다운샘플 효과(풀디코딩 스파이크를 안 만듦) |
다운샘플 효과 증명: 풀디코딩은 순간 48MB 스파이크(peak)를 만들고 다운샘플은 안 만든다 → peak(Allocations 그래프 최고점 / Debug 메모리 게이지)로 봐야 정확. Growth는 "끝나고 메모리가 원위치로 돌아오나(안 남나)"를 볼 때. 재는 게 다르다.
실전 흐름: Allocations Call Tree로 다운샘플 코드가 잡는 할당을 콜스택 단위 확인 → 작업 구간의 그래프 최고점으로 peak 비교(풀디코딩 vs 다운샘플) → 끝난 뒤 Generations로 남는 게 없는지(정상 회수) 확인.
Call Tree vs Mark Generation vs Peak (정밀 구분)
셋은 "거의 같은 것"이 아니라 각자 다른 걸 본다.
| 무엇 | 쓰임 | |
|---|---|---|
| Call Tree | 어느 콜스택(코드)이 얼마 할당했나(attribution) | 기능의 할당 출처 |
| Mark Generation | 두 마크 사이 남아서 안 사라진 증가분 | 작업 후 회수 여부(누수/abandoned) |
| 그래프 최고점 | 한 순간 동시 존재량의 최고점 | 순간 peak |
Call Tree에 peak는 없다. Call Tree 컬럼 = Persistent(끝에 살아있는)·Transient(할당됐다 해제된)·Total(둘 합=해제된 것까지 누적). Total ≠ peak(Total은 왔다 간 것까지 누적, peak는 한 순간 동시 존재 최대). peak는 오직 메모리 그래프 최고점으로만 읽는다.
표현 예: "Call Tree로 다운샘플 코드가 어느 콜스택에서 얼마 할당하는지 확인하고, 메모리 그래프 최고점으로 순간 피크를 확인했다. (Mark Generation은 작업 후 메모리가 안 남고 회수되는지 확인용.)" — 셋을 할당 출처 / 순간 피크 / 회수 여부로 구분.
Transient에 peak가 포함되나? — 구체 예시
시나리오: 이미지 3장 순차 다운샘플. 각 장이 풀디코딩 48MB 버퍼를 잠깐 만들고 → 썸네일 1.4MB 뽑고 → 48MB 해제.
t1: 48MB(buf1) 할당 → 순간 ~48MB → thumb1(1.4MB) → 해제 → 잔여 1.4MB
t2: 48MB(buf2) 할당 → 순간 ~49.4MB → thumb2 → 해제 → 잔여 2.8MB
t3: 48MB(buf3) 할당 → 순간 ~50.8MB(최고!) → thumb3 → 해제 → 잔여 4.2MB
| Call Tree 컬럼 | 값 | 의미 |
|---|---|---|
| Persistent | 3×1.4 = 4.2MB | 끝에 남은 썸네일들 |
| Transient | 3×48 = 144MB | 떴다 사라진 버퍼들(누적 합) |
| Total | 148.2MB | Persistent+Transient |
| 그래프 최고점(peak) | ~50.8MB | t3 순간: buf3 48MB + 누적 썸네일 2.8MB |
Transient(144MB) ≠ peak(50.8MB). peak를 만든 건 transient 버퍼가 맞지만, Transient 숫자(누적 합)는 peak 숫자가 아니다. 반복이 핵심: 3번 돌면 Transient는 144MB로 커지지만 peak는 ~48MB대 유지(한 번에 하나씩 떴다 짐) → 반복할수록 벌어짐. 그래서 peak는 그래프 최고점으로만 정확히.
다운샘플 vs 풀디코딩: 풀디코딩은 48MB 버퍼가 실제로 뜸(Transient·peak ~48MB), 다운샘플(ImageIO)은 목표 크기(1.4MB)로만 디코딩 → 48MB 버퍼 자체가 안 생김 → Transient 작고 peak 낮음. 이 피크 차이가 다운샘플 효과(그래프 최고점/Transient 감소로 증명).
한 줄 요약
Time Profiler=CPU 병목, Allocations=메모리(Call Tree 누가·Generations growth·그래프 최고점 peak), Leaks/Memory Graph=순환참조, Core Animation=노랑(오프스크린)·빨강(블렌딩). 다운샘플 효과는 peak, 누수/abandoned는 growth로.
관련 문서
스크롤 hitch(§12·§13) — Core Animation 노랑/빨강 측정 대상
대형 이미지 메모리·디코딩·다운샘플 — peak 측정 대상