Skip to content

HeartbeatScheduler 0.1.0

Latest

Choose a tag to compare

@markhammond markhammond released this 04 Nov 13:29
· 1 commit to main since this release

HeartbeatScheduler 0.1.0

The first release of HeartbeatScheduler: heartbeat scheduling for parallel loops of micro-tasks in .NET. A loop runs sequentially by default, and at most once per heartbeat (5 µs by default) each thread splits off half of its remaining work for an idle worker, so the cost of parallelism stays a bounded fraction of run time even when an iteration takes nanoseconds. It targets .NET 10.

Highlights

  • Bounded scheduling overhead. Splits are limited to one per heartbeat per thread, independent of iteration size; between iterations a thread checks a shared beat counter with a single load.
  • Short loops stay sequential. A loop shorter than a heartbeat runs as a plain loop on the calling thread, with no hand-off at all.
  • Batched loops. IHeartbeatBatchLoop hands each thread contiguous batches and checks the heartbeat between them, so a body of a few nanoseconds keeps a tight, inlinable inner loop.
  • Struct bodies. For<TLoop> takes a struct body the JIT inlines into the scheduling loop: no interface call per iteration, and still allocation-free.
  • The caller helps. Once its own share is done, the calling thread takes work from busy workers instead of waiting at the join.
  • The host's estimate. Given the mean cost per iteration (in microseconds with ForMicroseconds, of any precision, or as a TimeSpan), a loop predicted to take less than a heartbeat runs as a plain loop on the calling thread, and one predicted to take more is split as soon as a worker is idle, without waiting for a beat; each half of a split keeps a quarter heartbeat of predicted work.
  • Allocation-free per loop. Hand-offs are one mailbox word per worker, claimed with compare-and-swap.
  • The host's threads. StartWorker hands each worker's loop to the host, to run on a thread it creates and sets up itself (priority, CPU affinity, quality of service), or post to one of its event loops; HeartbeatThreads starts the default background threads (named, at a chosen quality) and raises a host's own threads to the user-interactive band on macOS, or the highest priority on Windows.
  • One clock reader. A single idle thread reads the clock and keeps the beat for all; the others only watch their mailboxes, so a slow clock (as on some virtual machines) costs little.
  • Idle policy. By default idle workers spin briefly after a loop, then park (SpinThenPark): for shared machines, development and energy efficiency. HeartbeatIdlePolicy.BusySpin keeps them spinning at all times, so starting a loop signals no one, for reserved cores with loops back to back. The XML docs guide the choice, and calibrating the heartbeat to a workload's loops.
  • Exceptions after the join. After a failure the other threads abandon their ranges at their next beat; the first exception is rethrown with its type after the join, and the scheduler stays usable.

One loop at a time per scheduler: a loop body must not start another.

Install

dotnet add package HeartbeatScheduler

The package carries XML docs and a symbols package, its sources linked to this release's commit.

License

MIT.