Actor Hop은 Swift Concurrency에서 실행 컨텍스트가 한 Actor에서 다른 Actor로 전환되는 현상을 말합니다. 이는 성능에 영향을 미치는 중요한 개념입니다.
Actor는 Swift의 동시성 모델에서 데이터 경쟁(data race)을 방지하기 위해 도입된 참조 타입입니다. 각 Actor는 자신만의 격리된 실행 컨텍스트를 가지며, 한 번에 하나의 작업만 실행할 수 있습니다.
// Actor A
actor DatabaseActor {
func fetchData() async -> String {
// 데이터베이스 작업
return "data"
}
}
// Actor B (MainActor)
@MainActor
class ViewController {
let database = DatabaseActor()
func loadData() async {
// 🔴 Hop 1: MainActor → DatabaseActor
let data = await database.fetchData()
// 🔴 Hop 2: DatabaseActor → MainActor (자동으로 돌아옴)
updateUI(data)
}
func updateUI(_ data: String) {
// UI 업데이트
}
}
각 Actor Hop마다 발생하는 오버헤드:
@MainActor
class BadExample {
let service = NetworkService() // 다른 Actor
let database = DatabaseActor() // 또 다른 Actor
let cache = CacheActor() // 또 다른 Actor
func complexOperation() async {
// 🔴 Hop 1: Main → NetworkService
let data = await service.fetchData()
// 🔴 Hop 2: NetworkService → DatabaseActor
await database.save(data)
// 🔴 Hop 3: DatabaseActor → CacheActor
await cache.store(data)
// 🔴 Hop 4: CacheActor → MainActor
updateUI()
}
// 총 4번의 Actor Hop = 약 0.4~1.2ms 오버헤드
}
@MainActor
class GoodExample {
let dataService = DataService() // 모든 작업을 처리하는 단일 Actor
func optimizedOperation() async {
// 🟢 Hop 1: Main → DataService (한 번만!)
let result = await dataService.performAllOperations()
// 🟢 Hop 2: DataService → Main (자동 복귀)
updateUI(result)
}
// 총 2번의 Actor Hop = 약 0.2~0.6ms 오버헤드
}
Actor Hop을 현실 세계에 비유하면:
🏢 건물 A (MainActor) 🏢 건물 B (DatabaseActor) 🏢 건물 C (NetworkActor)
│ │ │
│ 📦 작업 요청 → │ │
│ (이동 시간 소요) │ │
│ │ 📦 작업 요청 → │
│ │ (이동 시간 소요) │
│ │ │
│ │ ← 📦 결과 반환 │
│ ← 📦 결과 반환 │ │
각 건물 간 이동(Hop)마다 시간이 소요됩니다.
// 성능 측정 예제
func measureActorHops() async {
let startTime = CFAbsoluteTimeGetCurrent()
// 100번의 Actor Hop 발생
for _ in 0..<100 {
await someActor.doWork()
}
let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime
print("Time: \(timeElapsed)초")
// 결과: 약 10~30ms (Actor Hop 오버헤드만!)
}
// ❌ 나쁜 예: 여러 번의 Hop
for item in items {
await actor.process(item) // 각각 Hop 발생
}
// ✅ 좋은 예: 한 번의 Hop
await actor.processAll(items) // 한 번만 Hop
actor DataProcessor {
// 여러 작업을 한 번에 처리
func processMultiple() async -> ProcessResult {
let fetched = await fetchInternal()
let transformed = transform(fetched)
let validated = validate(transformed)
return ProcessResult(validated)
}
}
actor MyActor {
// Actor Hop이 발생하지 않음
nonisolated func getSyncData() -> String {
return "data" // 동기적으로 즉시 반환
}
}
DiContainer 2.0에서는 이러한 Actor Hop을 지능적으로 최소화합니다:
// DiContainer의 최적화된 접근
@MainActor
class OptimizedViewController {
@Inject var userService: UserService
func loadData() async {
// DiContainer가 Actor 컨텍스트를 추적하여
// 불필요한 Hop을 방지
let data = await userService.fetchUser()
updateUI(data) // 이미 MainActor 컨텍스트
}
}
이렇게 Actor Hop을 이해하고 최적화하면 Swift Concurrency 환경에서 훨씬 더 효율적인 코드를 작성할 수 있습니다! 🚀