코딩

[코루틴 (10)] 고급 주제 & 퍼포먼스 최적화

Eastpark 2025. 1. 25. 12:28
728x90
반응형

지난 9편에서는 코루틴 테스트 전략 & 디버깅을 살펴보며, runTest, TestDispatcher, Turbine, DebugProbes 등을 어떻게 활용할 수 있는지 알아보았습니다. 이제부터는 고급 주제퍼포먼스 최적화에 대해 조금 더 깊이 있게 다뤄보려고 합니다. 대규모 프로젝트나 성능 민감한 상황에서 코루틴을 효율적으로 운영하기 위한 팁들을 정리해 보았습니다.

 

핵심 키워드

CoroutineContext 커스터마이징

DebugProbes 심화

코루틴 퍼포먼스 모니터링

Structured Concurrency 최적화

코루틴 오버헤드 & 튜닝


1. CoroutineContext & Dispatcher 심화

 

1.1 CoroutineContext 구조 복습

CoroutineContext디스패처, Job, CoroutineName, ExceptionHandler 등을 조합해 코루틴 환경을 설정합니다.

예: launch(Dispatchers.IO + CoroutineName("FileLoader") + job + handler) { ... }

이처럼 + 연산자를 이용해 여러 요소를 합칠 수 있습니다.

 

1.2 디스패처 튜닝

Dispatchers.IO: 기본적으로 64개 스레드까지 확장 가능한 스레드 풀입니다. 하지만, 네트워크나 파일 I/O 작업이 매우 많은 경우, 스레드 풀 크기를 조절해야 할 수도 있습니다.

Dispatchers.Default: CPU 집약적 작업에 최적화되어 있으며, 코어 수에 따라 스레드 풀 크기를 결정합니다.

System.setProperty("kotlinx.coroutines.io.parallelism", "128")

위처럼 시스템 속성을 설정해 IO 디스패처의 스레드 풀 크기를 늘릴 수 있지만, 무분별한 확장은 오히려 성능 저하나 스레드 과부하를 야기할 수 있으니 주의가 필요합니다.

 

1.3 Custom Dispatcher

특정 상황에서, 별도의 ThreadPool 혹은 SingleThreadDispatcher를 만들어 사용하는 방식을 고려할 수 있습니다.

예: 데이터베이스 파일에 접근하는 동시에 다수의 코루틴이 경쟁하지 않도록, 전용 단일 스레드를 할당.

val singleThread = newSingleThreadContext("DbThread")
launch(singleThread) {
    // DB 작업 (단일 스레드에서만 실행)
}

2. DebugProbes & 코루틴 모니터링

 

2.1 DebugProbes 심화

이전 편(9편)에서 간단히 언급했던 DebugProbes를 조금 더 살펴봅니다.

DebugProbes.install()을 호출하면, 코루틴 생성·일시 중단·재개 상태를 IDE나 콘솔에서 볼 수 있습니다.

DebugProbes.dumpCoroutines()를 호출해 현재 코루틴 상태를 텍스트로 확인할 수 있고, Android Studio/IntelliJ의 Coroutine Debugger 탭에서 시각적으로 확인 가능합니다.

 

2.2 Coroutine Debugger in IDE

Android Studio(Arctic Fox 이후)나 IntelliJ에서는 Coroutine Debugger가 탑재되어, 디버그 모드에서 Coroutine 탭을 통해 각 코루틴 스택을 확인할 수 있습니다.

일시 중단 함수(suspend fun) 콜 스택을 파악하거나, 어느 지점에서 멈춰 있는지 시각적으로 분석이 가능해 디버깅 생산성이 높아집니다.


3. 퍼포먼스 모니터링 & 분석

 

3.1 Measure 코루틴 오버헤드

코루틴은 일반 스레드에 비해 훨씬 가볍지만, 함수를 만들고 일시 중단/재개하는 과정에서 일정한 오버헤드가 존재합니다.

수많은 코루틴이 짧은 작업을 빠르게 반복한다면, 스레드 풀 활용이 오히려 더 효율적일 수도 있습니다.

 

3.2 Instruments & Profiling

안드로이드 환경이라면 Android Profiler를 통해 CPU/Memory 사용량을 추적할 수 있습니다.

CPU Profiler에서 Coroutine 디스패처 스레드가 활발히 사용되는 구간을 확인하고, 더 효율적인 병렬 방식을 고민할 수 있습니다.

Allocation Tracking으로, 코루틴 스코프 생성/소멸 시점에 과도한 객체가 생성되지 않는지도 살펴보는 것이 좋습니다.

 

3.3 Structured Concurrency 최적화

coroutineScope { ... }supervisorScope { ... } 블록에서 자식 코루틴이 과도하게 생성되지 않는지 점검합니다.

기왕이면, 함수형 설계(immutable data, pure function)나 sink-source 구조(Flow)로 공유 mutable state를 최소화해, 동시성 오버헤드를 줄입니다.

728x90

4. 코루틴 오버헤드 & 튜닝 사례

 

4.1 “Small tasks”가 매우 많을 때

짧은 작업을 수천 개 코루틴으로 실행하는 경우, 코루틴 생성과 스위칭 오버헤드가 무시 못 할 수준이 될 수 있습니다.

이럴 때는 Batching 기법(여러 작은 작업을 묶어서 한 코루틴에서 처리)을 쓰거나, 스레드 풀 자체를 활용해 “run blocking tasks in bulk” 형태로 설계할 수 있습니다.

 

4.2 CPU 바운드 vs I/O 바운드

CPU 바운드: 압축, 이미지 처리 등 CPU 연산이 많은 로직은 Dispatchers.Default를 사용하는 게 일반적입니다.

I/O 바운드: 네트워크, 파일 읽기/쓰기 등은 Dispatchers.IO가 권장됩니다.

작업 유형에 따라 적절히 디스패처를 분리해 병렬 효율을 높이고, 스레드 리소스를 낭비하지 않도록 한다.

 

4.3 Cancellation & 예외 처리 비용

“취소(Cancellation)가 매우 빈번히 일어나는 시나리오”에서는, 매번 예외를 발생(CancellationException)해 스택을 추적하는 비용이 누적될 수 있습니다.

코루틴 설계를 단순화하거나, 필요 없는 “취소-재시작” 과정을 제거해 성능을 높일 수 있습니다.


5. CoroutineContext 커스터마이징 예시

 

아래 예시처럼, CoroutineNameJob, 그리고 특정 Dispatcher를 합쳐 새로운 컨텍스트를 정의할 수 있습니다.

val singleThread = newSingleThreadContext("DbThread")
val dbJob = Job()
val dbContext = singleThread + dbJob + CoroutineName("DatabaseOps")

fun main() = runBlocking {
    // dbContext를 사용해 DB 작업 수행
    launch(dbContext) {
        println("Running DB ops on a single thread")
    }
    // ...
}

명확한 네이밍전담 스레드로 작업을 고립하면, 디버깅과 성능 모니터링이 수월해집니다.

필요에 따라 dbJob.cancel()로 전체 DB 관련 코루틴 작업을 일괄 취소할 수도 있습니다.


마무리 및 다음 예고

 

이번 [10편]에서는 코루틴 고급 주제퍼포먼스 최적화 방안을 정리해보았습니다.

CoroutineContext를 적절히 커스터마이징하고, 디스패처를 잘 설정해 병렬성안정성을 동시에 추구

DebugProbes와 IDE Coroutine Debugger로 문제 지점을 파악하고, 필요하면 프로파일링

작업 특성(CPU 바운드 vs I/O 바운드)에 따라 Dispatcher 분리, 코루틴 오버헤드를 최소화하는 전략

 

다음 11편에서는 WorkManager + 코루틴 연동을 중점적으로 살펴볼 예정입니다. 백그라운드 작업을 스케줄링할 때 코루틴을 활용하는 방법, CoroutineWorker 클래스, 작업 취소와 재시작 시나리오 등을 구체적인 예제로 안내하겠습니다.


시리즈 전체 목차

0. 코루틴 탄생 배경과 기본 개념

1. 코루틴 기초(suspend, CoroutineScope, Dispatcher)

2. Structured Concurrency와 코루틴 스코프 관리

3. 예외 처리와 취소(Cancellation) 기법

4. 코루틴 채널(Channel)과 select

5. Flow 심화(Flow, StateFlow, SharedFlow)

6. 안드로이드 실무 적용(Retrofit, Room, ViewModel)

7. 동시성 문제 해결(Mutex, Semaphore)

8. 코루틴 테스트 & 디버깅 전략

9. 고급 퍼포먼스 최적화

10. WorkManager + 코루틴 연동

11. LifecycleScope & 안드로이드 생명주기 대응

12. Compose + 코루틴 + Flow 시너지

13. Compose Navigation + 코루틴 취소/재시작 사례

728x90
반응형