관리 메뉴

Bull

[Dart::Parallelism] 병렬 처리 사용 기본법 (Isolate) 본문

Computer Language/Dart

[Dart::Parallelism] 병렬 처리 사용 기본법 (Isolate)

Bull_ 2024. 8. 20. 15:29

https://dart.dev/language/isolates

 

Isolates

Information on writing isolates in Dart.

dart.dev

요약식 설명이기 때문에 공식문서를 읽어보는 것을 추천드립니다. 제목과 같이 사용 기본법을 설명하기 때문에 코드관점에서 동작예시를 보이는 글입니다.

Dart에서 Isolate를 사용한 병렬 처리 및 동시성 처리

Dart에서는 Isolate를 사용하여 동시성과 병렬 처리를 구현할 수 있습니다. Isolate는 서로 독립된 메모리 공간과 이벤트 루프를 가진 프로세스입니다. Isolate는 서로 다른 스레드에서 병렬로 실행되며, 서로 직접적으로 메모리를 공유하지 않고 메시지를 통해 통신합니다.

동시성과 병렬 처리의 개념

동시성(Concurrency) 여러 작업이 동시에 진행될 수 있도록 하는 프로그래밍 기법입니다. CPU가 한 번에 하나의 작업을 처리하지만, 빠르게 전환하면서 여러 작업이 동시에 실행되는 것처럼 보이게 합니다. Dart의 Futureasync/await을 사용하여 비동기적으로 동시성을 구현할 수 있습니다.

 

병렬 처리(Parallelism)는 여러 작업이 물리적으로 동시에 실행되는 것입니다. 멀티코어 CPU를 사용하여 각 코어가 다른 작업을 동시에 처리함으로써 전체 작업 시간이 단축됩니다. Dart에서는 Isolate를 통해 병렬 처리를 구현할 수 있습니다.

Isolate API

Isolate API는 Dart에서 병렬 처리 및 동시성 처리를 구현할 수 있습니다. Isolate.run()Isolate.spawn() 등의 기능을 통해 새로운 Isolate를 생성하고, 작업을 수행할 수 있습니다.

Isolate.run()의 특징

Isolate.run()은 새로운 Isolate를 생성하고 작업을 실행하며, 작업이 완료되면 결과를 반환하고 Isolate를 종료합니다. Isolate.run()을 사용하면 비동기적으로 코드를 병렬로 실행할 수 있습니다. 메인 Isolate는 다른 작업을 계속 진행할 수 있으며, 작업 결과는 Future로 반환됩니다. Flutter에서는 Isolate.run() 대신 compute 함수를 사용하여 유사한 기능을 구현할 수 있습니다.

장기 실행되는 Isolate에서의 통신

장기 실행되는 Isolate와 메인 Isolate 간의 양방향 통신을 위해 ReceivePortSendPort를 사용할 수 있습니다. Isolate.spawn()을 통해 구현할 수 있습니다.

 

다음은 피보나치 예제를 통해 병렬성을 시각화해보았습니다.

import 'dart:isolate';
import 'dart:async';

Future<int> fibonacci(int n, SendPort sendPort, String isolateName) async {
  await Future.delayed(Duration(milliseconds: 300));
  if (n <= 1) {
    sendPort.send('$isolateName: Fibonacci($n) = $n');
    return n;
  }
  final result = await fibonacci(n - 1, sendPort, isolateName) +
      await fibonacci(n - 2, sendPort, isolateName);
  sendPort.send('$isolateName: Fibonacci($n) = $result');
  return result;
}

void isolateFunction(List<dynamic> message) async {
  SendPort sendPort = message[0];
  int n = message[1];
  String isolateName = message[2];

  final int result = await fibonacci(n, sendPort, isolateName);
  sendPort.send(
      '$isolateName: Computation complete! Final result: Fibonacci($n) = $result');
}

void main()  {
  ReceivePort receivePort1 = ReceivePort();
  ReceivePort receivePort2 = ReceivePort();
  ReceivePort receivePort3 = ReceivePort();

  Isolate.spawn(isolateFunction, [receivePort1.sendPort, 5, 'Isolate 1']);
  Isolate.spawn(isolateFunction, [receivePort2.sendPort, 5, 'Isolate 2']);
  Isolate.spawn(isolateFunction, [receivePort3.sendPort, 5, 'Isolate 3']);

  receivePort1.listen((message) {
    print(message);
  });
  receivePort2.listen((message) {
    print(message);
  });
  receivePort3.listen((message) {
    print(message);
  });
}

ReceivePort() 를 통해 양방향 통신을 할 수 있는 인스턴스를 3개 생성해줍니다. Isolate.spawnIsolate.run() 보다 복잡하거나 양방향 통신이 필요할 때 사용합니다. 2번째 아규먼트를 통해 전달할 메시지를 넣을 수 있습니다. 다이나믹 리스트로 통신 인스턴스와 계산할 피보나치 수, 어느 Isolate 인지 구분하는 문자열을 넣어 줍니다. receivePort1.listen 를 통해 Isolate를 구독해줍니다.

 

isolateFunction 은 병렬 처리를 수행할 메소드입니다. 여기서 비동기 처리를 해주었는데 시각화를 위해서 0.3초 정도의 딜레이 단위로 수행하도록 했습니다. sendPort.send 를 통해 완료됨을 알려줍니다.

 

fibonacci 를 수행합니다. 수행될 때 0.3초의 딜레이를 줍니다. 여기서도 통신 인스턴스를 파라미터로 받아서 sendPort.send로 연산이 수행됐음을 알립니다.

# 결과
Isolate 1: Fibonacci(1) = 1
Isolate 3: Fibonacci(1) = 1
Isolate 2: Fibonacci(1) = 1
Isolate 3: Fibonacci(0) = 0
Isolate 1: Fibonacci(0) = 0
Isolate 2: Fibonacci(0) = 0
Isolate 1: Fibonacci(2) = 1
Isolate 2: Fibonacci(2) = 1
Isolate 3: Fibonacci(2) = 1
Isolate 1: Fibonacci(1) = 1
Isolate 3: Fibonacci(1) = 1
Isolate 1: Fibonacci(3) = 2
Isolate 3: Fibonacci(3) = 2
Isolate 2: Fibonacci(1) = 1
Isolate 2: Fibonacci(3) = 2
Isolate 1: Fibonacci(1) = 1
Isolate 3: Fibonacci(1) = 1
Isolate 2: Fibonacci(1) = 1
Isolate 1: Fibonacci(0) = 0
Isolate 2: Fibonacci(0) = 0
Isolate 1: Fibonacci(2) = 1
Isolate 1: Fibonacci(4) = 3
Isolate 3: Fibonacci(0) = 0
Isolate 2: Fibonacci(2) = 1
Isolate 2: Fibonacci(4) = 3
Isolate 3: Fibonacci(2) = 1
Isolate 3: Fibonacci(4) = 3
Isolate 2: Fibonacci(1) = 1
Isolate 1: Fibonacci(1) = 1
Isolate 3: Fibonacci(1) = 1
Isolate 1: Fibonacci(0) = 0
Isolate 2: Fibonacci(0) = 0
Isolate 1: Fibonacci(2) = 1
Isolate 3: Fibonacci(0) = 0
Isolate 2: Fibonacci(2) = 1
Isolate 3: Fibonacci(2) = 1
Isolate 3: Fibonacci(1) = 1
Isolate 3: Fibonacci(3) = 2
Isolate 2: Fibonacci(1) = 1
Isolate 1: Fibonacci(1) = 1
Isolate 3: Fibonacci(5) = 5
Isolate 2: Fibonacci(3) = 2
Isolate 1: Fibonacci(3) = 2
Isolate 2: Fibonacci(5) = 5
Isolate 3: Computation complete! Final result: Fibonacci(5) = 5
Isolate 1: Fibonacci(5) = 5
Isolate 2: Computation complete! Final result: Fibonacci(5) = 5
Isolate 1: Computation complete! Final result: Fibonacci(5) = 5

순서가 뒤죽박죽입을 보면 병렬로 처리된 모습을 확인할 수 있습니다.