Skip to content

Dart code generator for Squadron workers. Implement your worker service and let squadron_builder bridge the gap with Web Workers and Isolates!

License

Notifications You must be signed in to change notification settings

d-markey/squadron_builder

Repository files navigation

Squadron logo

Squadron - Multithreading and worker pools in Dart

Offload CPU-bound and long running tasks and give your mobile and Web apps some air!

squadron_builder

Dart code generator for Squadron workers. Implement your worker service and let squadron_builder bridge the gap with Web Workers and Isolates!

Pub Package Dart Platforms Flutter Platforms

License Null Safety Dart Style Pub Points Likes Popularity Last Commit

Usage

squadron_builder is a companion package to Squadron and should be installed as a development dependency.

Its purpose is to interpret Squadron annotations applied to classes and methods from your source code, and generate the worker and worker pool classes for you as well as the plumbing required to make platform threads communicate with each other.

Service classes must be decorated with SquadronService annotations, and service methods with SquadronMethod.

Assuming your service implementation is found in file my_service.dart, the following references are required in your source code:

  • import the generated activator file import 'my_service.activator.g.dart';
  • reference the generated part file part 'my_service.worker.g.dart';

Examples

Example - Hello, World

The classic Hello, World! example code with a HelloWorld service:

import 'dart:async';

// Squadron is required for annotations.
// It is also required by the generated part file.
import 'package:squadron/squadron.dart';

// This file must be imported into your library file.
// It is used to provide the worker's entry point, which depends on the target platform.
// It is generated by squadron_builder and required by the generated part file.
import 'hello_world.activator.g.dart';

// This file is generated by squadron_builder as a part file.
// It implements `HelloWorldWorker` and `HelloWorldWorkerPool` for multithreading.
part 'hello_world.worker.g.dart';

@SquadronService(web: false)
class HelloWorld {
  @squadronMethod
  Future<String> hello([String? name]) async {
    name = name?.trim() ?? '';
    return name.isEmpty ? 'Hello, World!' : 'Hello, $name!';
  }
}

Generate code for HelloWorldWorker and HelloWorldWorkerPool with dart run build_runner build.

Now you're ready to go:

import 'hello_world.dart';

void main() async {
  final worker = HelloWorldWorker();
  print(await worker.hello());
  print(await worker.hello('Mary'));
  print(await worker.hello('John'));
  worker.stop();
}

Sample output:

Hello, World!
Hello, Mary!
Hello, John!

Example - Fibonacci sequence

The example computes Fibonacci numbers recursively, simply applying the definition of the Fibonacci sequence. It is very inefficient, but illustrates the effect of multithreading.

import 'dart:async';

import 'package:squadron/squadron.dart';

import 'fib_service.activator.g.dart';

part 'fib_service.worker.g.dart';

@SquadronService()
class FibService {
  @squadronMethod
  Future<int> fibonacci(int i) async => _fib(i);

  // naive & inefficient implementation of the Fibonacci sequence
  static int _fib(int i) => (i < 2) ? i : (_fib(i - 2) + _fib(i - 1));
}

To have squadron_builder generate the code for the worker and the worker pool, run:

dart run build_runner build

The main program runs the same computations:

  • first with a plain instance of FibService (single-threaded, running in the main program's Isolate),
  • then with an instance of FibServiceWorker (single-threaded, running in a dedicated Isolate),
  • finally with an instance of FibServiceWorkerPool (multi-threaded, running in specific Isolates managed by the worker pool).

The worker and worker pool generated by squadron_builder both wrap the original service and implement it: as a result, they are interchangeable with the original service.

import 'package:squadron/squadron.dart';

import 'fib_service.dart';

void main() async {
  // compute 9 fibonnaci numbers (starting from 37)
  int count = 9, start = 37;

  print('Computing with FibService (single-threaded in the main Isolate)');
  final service = FibService();
  await computeWith(service, start, count);

  print('');
  print('Computing with FibServiceWorker (single-threaded in 1 dedicated Isolate)');
  final worker = FibServiceWorker();
  await worker.start();
  await computeWith(worker, start, count);
  print('  * Stats for worker ${worker.workerHashCode}: ${worker.stats.dump()}');
  worker.stop();

  print('');
  print('Computing with FibServiceWorkerPool (multi-threaded, dedicated Isolates)');
  final concurrency = ConcurrencySettings(minWorkers: 1, maxWorkers: count ~/ 2, maxParallel: 1);
  final pool = FibServiceWorkerPool(concurrencySettings: concurrency);
  await pool.start();
  await computeWith(pool, start, count);
  print(pool.fullStats.map((s) => '  * Stats for pool worker ${s.workerHashCode}: ${s.dump()}').join('\n'));
  pool.stop();
}

Future computeWith(FibService service, int start, int count) async {
  final sw = Stopwatch()..start();
  // start all computations
  final computations = Iterable<int>.generate(count)
      .map((i) => service.fibonacci(start + i))
      .toList();
  // wait for results
  final results = await Future.wait(computations);
  // display results
  print('  * Results = $results');
  print('  * Total elapsed time: ${sw.elapsed}');
}

extension DebugStats on WorkerStat {
  String dump() => 'totalWorkload=$totalWorkload (max $maxWorkload) - upTime=$upTime - idleTime=$idleTime - status=$status';
}

Sample output:

Computing with FibService (single-threaded in the main Isolate)
  * Results = [24157817, 39088169, 63245986, 102334155, 165580141, 267914296, 433494437, 701408733, 1134903170]
  * Total elapsed time: 0:00:11.686547

Computing with FibServiceWorker (single-threaded in 1 dedicated Isolate)
  * Results = [24157817, 39088169, 63245986, 102334155, 165580141, 267914296, 433494437, 701408733, 1134903170]
  * Total elapsed time: 0:00:11.475894
  * Stats for worker 494853282: totalWorkload=9 (max 9) - upTime=0:00:11.479725 - idleTime=0:00:00.006250 - status=IDLE

Computing with FibServiceWorkerPool (multi-threaded in 4 dedicated Isolates)
  * Results = [24157817, 39088169, 63245986, 102334155, 165580141, 267914296, 433494437, 701408733, 1134903170]
  * Total elapsed time: 0:00:05.877645
  * Stats for pool worker 255048060: totalWorkload=3 (max 1) - upTime=0:00:05.883385 - idleTime=0:00:00.004117 - status=IDLE
  * Stats for pool worker 876978540: totalWorkload=2 (max 1) - upTime=0:00:05.871348 - idleTime=0:00:04.375454 - status=IDLE
  * Stats for pool worker 127911517: totalWorkload=2 (max 1) - upTime=0:00:05.870383 - idleTime=0:00:03.585933 - status=IDLE
  * Stats for pool worker 543026067: totalWorkload=2 (max 1) - upTime=0:00:05.870383 - idleTime=0:00:02.196035 - status=IDLE

About

Dart code generator for Squadron workers. Implement your worker service and let squadron_builder bridge the gap with Web Workers and Isolates!

Topics

Resources

License

Stars

Watchers

Forks

Sponsor this project

 

Packages

No packages published