Section 11/172 menit
11. Actor Hop: Biaya, Pengukuran, dan Optimasi
11. Actor Hop: Biaya, Pengukuran, dan Optimasi
Setiap await ke method actor (dari konteks berbeda) memerlukan hop: konteks dipindahkan ke executor target. Hop punya cost — bukan gratis.
Apa yang Terjadi Saat Hop
- Continuation (state mesin coroutine saat ini) disimpan.
- Job dienqueue ke executor target.
- Thread caller bisa pulang ke pool / mengerjakan job lain.
- Eventually executor target menjalankan job.
- Saat method actor selesai (atau
awaitlagi), kembali hop lagi.
Cost rata-rata (data emprical, M2 MacBook):
- Hop ke actor instance di pool: ~200-500 ns.
- Hop ke MainActor dari background: 1-3 µs (karena cross-thread).
- Synchronous call di actor yang sama: ~5-10 ns (sama seperti method call biasa).
Mengukur Hop
Pakai Instruments → Swift Concurrency template, atau signpost manual:
swift
import os.signpost
let log = OSLog(subsystem: "app", category: .pointsOfInterest)
actor Worker {
func process(_ item: Item) async {
let id = OSSignpostID(log: log)
os_signpost(.begin, log: log, name: "process", signpostID: id)
defer { os_signpost(.end, log: log, name: "process", signpostID: id) }
// ...
}
}
Di Instruments → Points of Interest, lihat distribusi durasi vs jumlah hop.
Optimasi 1: Batch di Synchronous Method
Anti-pattern: banyak await ke actor untuk operasi kecil.
swift
// ❌ Banyak hop
for id in ids {
await store.add(id) // hop per iterasi
}
// ✓ Satu hop
await store.addBatch(ids) // satu hop, loop internal
Optimasi 2: isolated Parameter untuk Batch
Sudah dibahas Bagian 10. Cocok untuk operasi yang tidak natural sebagai method actor.
swift
// Bukan method actor, tapi diisolasi saat dipanggil
func computeSummary(_ store: isolated UserStore) -> Summary {
var s = Summary()
for user in store.allUsers { // tidak ada await
s.add(user)
}
return s
}
Optimasi 3: Hindari Task { await ... } di Hot Loop
Setiap Task membuat allocation + schedule cost. Kalau kamu butuh fan-out di hot path, prefer TaskGroup (lebih ringan, reuse parent task context).
swift
// ❌ Banyak Task allocation
for item in items {
Task { await process(item) }
}
// ✓ TaskGroup
await withTaskGroup(of: Void.self) { group in
for item in items {
group.addTask { await process(item) }
}
}
Optimasi 4: Minimasi MainActor Hop
UI update sebaiknya di-batch:
swift
// ❌ Update per item → hop per item
for change in changes {
await MainActor.run { table.apply(change) }
}
// ✓ Satu hop
await MainActor.run {
for change in changes { table.apply(change) }
}