nyancoder

WWDC 2021 - Swift concurrency: Behind the scenes 본문

WWDC/WWDC 2021

WWDC 2021 - Swift concurrency: Behind the scenes

nyancoder 2021. 7. 1. 02:44

원본 영상: https://developer.apple.com/videos/play/wwdc2021/10254/

 

이 장에서는 Swift 동시성 모델에 대한 이해와 성능 향상을 위한 방법에 대해 알아보겠습니다.

 

Threading Model

  • 많은 양의 데이터를 비동기로 업데이트하는 코드는 수많은 스레드를 생성할 수 있습니다.
  • CPU 코어의 개수를 넘는 스레드는 성능 문제를 일으킵니다.
  • 많은 스레드는 각각의 메모리 공간을 가지고 있어야 하기 때문에 메모리 오버헤드를 가집니다.

  • 많은 스레드가 생성되면 많은 context switching이 발생하므로 CPU가 비효율 적으로 동작합니다.

 

Concurrency in Swift

  • Swift의 async/await는 매번 새 스레드를 생성하지 않기 때문에 Context switching비용이 줄어듭니다.
  • 각 스레드는 실행할 수 있는 Continuation들을 수행하여 여러 작업을 수행할 수 있습니다.
  • 각각의 메모리 공간은 스레드를 가지는 대신 Continuation를 가지기 때문에 더 적은 메모리 공간을 차지합니다.

 

Language features

  • Swift는 await 키워드를 통해서 비동기로 실행해야 할 작업들을 처리합니다.
  • Swift는 각 Task 간의 의존관계를 파악해 순서대로 처리합니다.

 

Cooperative thread pool

  • Swift를 위한 기본 Executor가 존재합니다.
  • Executor의 스레드 제한은 CPU Core의 개수에 영향을 받습니다.
  • Worker Thread는 Block 되지 않습니다.
  • 스레드 개수가 과하게 늘어나거나 Context switch가 과하게 발생하는 것을 방지해 줍니다.

 

Adoption of Swift concurrency

  • 작업을 병렬로 처리하는 것은 추가적인 비용이 필요합니다.
  • 아주 작은 작업을 병렬로 처리하는 것은 그것을 관리하는 비용이 더 클 수 있습니다.
  • 코드를 항상 profile 하여 병렬로 처리하는 것이 올바른지 판단해야 합니다.

 

Await and atomicity

  • await 키워드의 이전과 이후의 스레드가 다를 수 있습니다.
  • 그러므로 스레드에 관계된 데이터를 유지해서는 안됩니다.
  • await에서 자발적으로 스케줄링이 종료되어 더 이상 실행되지 않을 수 있습니다.
  • 그러므로 lock을 await 전/후로 유지해서는 안됩니다.

 

Preserving the runtime contract

  • 안전한 방법 - await, Actors, Task groups. 이들은 컴파일러에 의해 안전이 보장됩니다.
  • 주의가 필요한 방법 - os_unfair_lock, NSLock을 동기화된 코드에서 사용. 이들은 컴파일러에 의해 지원되지 않습니다.
  • 위험한 방법 - DispatchSemaphore, pthread_cond, NSCondition, pthread_rw_lock 등. 이들은 컴파일러에 의해 지원되지 않습니다.

이와 같은 코드는 await이후의 구문이 실행되지 않으면 영원히 잠금이 해제되지 않을 수 있기 때문에 안전하지 않습니다.

위와 같은 오류는 실행 시에 LIBDISPATCH_COOPERATIVE_POOL_STRICT=1의 환경 변수를 추가하여 체크할 수 있습니다.

 

 

Mutual exclusion

   Locks, Serial Queue의 sync 함수

  • 사용되고 있지 않을 때 - 스레드를 재사용합니다.
  • 사용되고 있을 때 - 호출한 스레드를 블록 합니다.

   Locks, Serial Queue의 async 함수

  • 사용되고 있지 않을 때 - 새로운 스레드를 생성합니다.
  • 사용되고 있을 때 - 비동기 실행됩니다.

   Cooperative pool을 사용하는 Actor

  • 사용되고 있지 않을 때 - 스레드를 재사용합니다.
  • 사용되고 있을 때 - 비동기로 수행됩니다.

 

Actor hopping

위와 같은 상황에서 Database Actor의 함수를 호출하면 즉각 실행됩니다.

이후 다른 스레드에서 Database Actor의 함수를 호출하면 이미 사용 중인 Actor이므로 해당 호출은 이후에 처리됩니다.

 

Reentrancy and prioritization

  • Serial dispatch queue의 경우 들어온 순서대로 작업이 수행되어야 합니다.
  • A, B작업이 우선순위가 높지만 처리는 A, 1, 2, 3, 4, 5, B 순으로 처리됩니다.
  • 이 경우 1, 2, 3, 4, 5가 B의 우선순위를 가지도록 하여 처리 속도를 높이는데 이를 '우선순위 역전'이라고 합니다.

  • Actor의 경우 반드시 들어온 순서대로 작업이 처리되어야 하는 것은 아닙니다.
  • 위의 경우에는 우선순위가 높은 A, B작업을 먼저 처리하고 이후 1, 2, 3, 4, 5, 6, 7 작업을 수행합니다.

 

Actor hopping: Main Actor

  • 위와 같은 코드에서는 Database Actor에서 Article 항목을 가져온 다음 Main Actor에서 처리하는 과정을 반복해서 수행합니다.
  • Main Actor는 다른 Actor들이 Cooperative thread pool을 사용하는 것과는 달리 Main Thread를 사용합니다.
  • 따라서 이 경우의 빈번한 Actor 간의 변경은 빈번한 스레드 변경과 동일하여 성능 저하가 있습니다.

  • Main Thread에 데이터를 전달해야 하는 경우에는 데이터를 한 번에 묶어서 전달하면 스레드 간 전환을 막을 수 있습니다.

 

 

목차: https://nyancoder.tistory.com/2

Comments