← Home
Swift Concurrency · Performance

Swift Concurrency 성능 분석 Q&A

LINE Engineering 블로그 "Swift Concurrency의 성능에 관하여" 읽고 정리한 Q&A

1. Continuation과 Suspend 관계

Q: suspend하면 continuation을 기다리는 거지?
A: 정확히는 suspend = continuation을 만들고, 그 continuation이 resume되기를 기다림
Q: "continuation에서 머문다"는 게 무슨 뜻?
A: 스레드 없이 메모리에 재개 정보만 저장된 채 대기. Executor가 resume해줄 때까지 실행 안 됨.
Continuation 흐름:
await 만남 → continuation 객체 생성 → 스레드 반납 → 대기(메모리에 객체만 존재)
         ↓
   awaited 작업 완료 시 continuation.resume() → Task 재개
용어의미
suspendTask가 멈춤 (스레드 반납)
continuation"어디서 멈췄고, 어떻게 재개할지" 담은 객체
resumecontinuation을 실행해서 Task 재개

2. Swift Concurrency 스레드 할당 규칙

  • Suspension point에서 높은 우선순위 Task가 스레드 할당받을 확률 높음
  • 가장 높은 우선순위 Task가 코어 수만큼 스레드 점유 중이면 → 낮은 우선순위용 별도 스레드 추가
  • 따라서 스레드 수는 코어 수로 고정 X, 상황에 따라 더 많을 수 있음
  • 추가된 스레드는 낮은 우선순위 작업만 실행 → 무한정 대기(starvation) 방지
예: 6코어 기기, High 6개 + Low 4개 Task
┌─────────────────────────────────────┐
│ Thread 1-6: High Task (코어 수만큼)  │
│ Thread 7:   Low Task (추가 스레드)   │ ← starvation 방지
└─────────────────────────────────────┘

3. 자식 Task 생성 = Suspension point 역할

자식 작업을 생성하면 OS가 스케줄링을 진행 → 스레드를 우선 할당받아야 하는 작업에 스레드 재할당

자식 Task 생성 방법분류
async let child = ...Structured (자동 취소)
withTaskGroup { group.addTask { } }Structured (자동 취소)
withThrowingTaskGroup { ... }Structured (자동 취소)

Task { }, Task.detached { }는 새 Task 생성이지만 "자식"은 아님 (unstructured)

"자식(child)"의 의미:
  • 부모 취소 시 자동 취소
  • 부모 scope 끝나면 자동 await (암묵적 대기)
  • = Structured Concurrency의 핵심

4. Suspension point가 없으면 동시성 활용 못 함

Q: 왜 Suspension point가 있어야 동시성을 살릴 수 있나?
A: 이 포인트들이 있어야 한 스레드에서 한 Task가 점령하지 않고, 다른 Task들을 골고루 처리할 수 있어서 동시성 프로그래밍 가능
Suspension point 없음:
Task A: ████████████████ (끝까지 스레드 점유)
Task B: ................████████ (A 끝나야 시작)

Suspension point 있음:
Task A: ███░░░███░░░███ (await에서 양보)
Task B: ░░░███░░░███░░░ (끼어들어 실행)
         ↑
      다른 Task 실행 기회
조건결과
Suspension point 없음코어 수(6개)만 동시 실행, 나머지 대기
Suspension point 있음여러 Task가 번갈아 진행 (진정한 동시성)

5. GCD + Swift Concurrency 혼용 시 성능 저하

문제: GCD가 스레드를 많이 생성하면 → 코어가 Concurrency 스레드에 할당할 CPU 시간 부족
Q: 왜 GCD와 혼용하면 느려지나?
GCD가 스레드 많이 생성 → 코어는 모든 스레드에 시간 분배 → Concurrency의 정해진 스레드에 돌아오는 CPU 시간이 줄어듦 → Task 느려짐
Concurrency 단독 (정상):
Core 1: ████████ Concurrency Thread (100% 시간)
Core 2: ████████ Concurrency Thread (100% 시간)

GCD 혼용 (문제):
Core 1: █░█░█░█░ GCD 8개 + Concurrency 1개 경쟁
Core 2: █░█░█░█░ → Concurrency 받는 시간 ↓↓↓
Swift ConcurrencyGCD
스레드 수코어 수 고정 (~6)무제한 생성 가능
철학"코어 독점" 가정"필요하면 더 만들어"

6. withCheckedContinuation 동작 방식

Q: withCheckedContinuation 내 코드는 바깥에서 받은 context에서 동작?
A: 네, 클로저 내부 코드는 호출한 Concurrency 스레드에서 동기 실행됩니다.
Q: withCheckedContinuation이 하나의 Task야?
A: 아니요, 별도 Task가 아닙니다. 현재 Task를 suspend/resume하는 메커니즘입니다.
핵심: await withCheckedContinuation이 호출된다고 바로 suspend가 아니라, 클로저 내부가 먼저 동기 실행되고, resume을 기다리는 동안 suspend
await withCheckedContinuation { continuation in
    // 1. 이 클로저 전체가 먼저 동기 실행
    // 2. 클로저 끝나면 suspend
    // 3. resume() 호출 대기
}

타임라인:
┌─────────────────────┐
│ 클로저 동기 실행      │ ← 아직 running
│ (someNormalFunction) │
│ (Code B)            │
│ (async 등록)         │
└─────────────────────┘
        │
        ▼ 클로저 리턴
┌─────────────────────┐
│ Task suspend        │ ← 여기서 멈춤
│ resume() 대기        │
└─────────────────────┘
        │
        ▼ resume() 호출됨
┌─────────────────────┐
│ Task 재개            │
└─────────────────────┘

핵심 요약