Performance & Benchmarks

Where WorkflowForge sits on BenchmarkDotNet runs: internal numbers, comparisons, targets, and tuning notes.

Version: 2.2.0
Test System: Windows 11 (25H2), Intel 11th Gen i7-1185G7, .NET SDK 10.0.103
Runtimes: .NET 10.0.3, .NET 8.0.24, .NET Framework 4.8.1
BenchmarkDotNet: v0.15.8, 10 iterations per job
Last Updated: 14 September 2026 (2.2.0 hot-path release; full comparative BenchmarkDotNet re-run on release runner before tag)

Reading the 2.1.0 → 2.2.0 deltas. Several numbers below moved by 10–1000x since the last published run. Before treating that as a real speedup (or a regression, where a competitive multiplier shrank), know which of three things caused it:

  1. Harness fix, not code. The 2.1.0 run used a RunStrategy/iteration-count combination meant for tens-of-ms+ work; on nanosecond/microsecond operations it measured JIT/GC/scheduler noise instead of the real cost. Fixed this release. Control case: DelayOperationExecution, which actually sleeps, moved from 15,192μs to 16.1ms — i.e. ~0%, because real work isn’t affected by the fix. Fast in-process operations moved 100–1000x (e.g. operation creation: ~1.7μs → ~50ns) — that’s the noise leaving, not new code making creation 30x faster.
  2. A fairer comparison, not a regression. Scenario 7 (“Creation Overhead”)’s advantage vs. Elsa fell from 74–212x to a plain 2–3x. Elsa’s old number included rebuilding its DI container on every measured call (a bug in our benchmark harness, since fixed by moving that setup out of the timed region) — so the old multiplier was comparing our real cost against Elsa’s DI-bootstrap cost, not its creation cost. The new number is the fair one.
  3. Real code changes. Where a change touched the exact hot path being measured — e.g. the retried-operations-bypass-middleware fix, LoggingMiddleware no longer being inert — some of the remaining delta is genuine. Concurrency scaling (8.0x @ 8 workers, 15.9x @ 16) is unchanged from 2.1.0, which is the control for “did real throughput change”: it didn’t move, because that ratio cancels out most harness noise on its own.

Table of Contents

  1. Overview
  2. Internal Performance Benchmarks
  3. Competitive Performance Summary
  4. Performance Targets and Guidelines
  5. Optimization Guide
  6. Benchmark Methodology
  7. Version History
  8. Related Documentation

Overview

The engine optimizes for low-latency in-process orchestration:

  • Nanoseconds to low microseconds on the hot path: CPU-bound ops median about 74ns–9.5μs in the harness (delays excluded).
  • Small baseline: ~4.03KB, flat from 10 through 500 iterations in the minimal workflow test.
  • Concurrency: about 8.0x with 8 workflows and 15.9x with 16 in the same suite.
  • Property bag data flow: no per-step serialization or reflection on that path.
  • No host-owned worker pool: parallelism is explicit (ForEachWorkflowOperation, your schedulers).

On .NET 10, 8, and .NET Framework 4.8 the comparative harness reports 2-583x faster wall times and 1–533x lower allocations than Workflow Core and Elsa for the scripted scenarios we mirrored. Ratios are tied to those scripts and hardware, not every app.

584x
Faster (State Machine vs Elsa)
533x
Less Memory
17.9μs
Min Execution Time
4.03KB
Memory Baseline

Internal Performance Benchmarks

These internal benchmarks measure WorkflowForge in isolation across all three runtimes:

Metric .NET 8.0 .NET 10.0 .NET FX 4.8
Operation Execution 76ns–9.5μs median 74ns–9.5μs median 216ns–32μs median
Operation Creation ~51ns median ~48ns median ~60ns median
Workflow Throughput ~4–211μs (custom, 1–50 ops) ~4–211μs (custom, 1–50 ops) ~5–345μs (1–50 ops)
Memory Baseline 4.03KB (constant) 4.03KB (constant) N/A‡
Concurrent Scaling 8.0x (8 wf), 15.9x (16 wf) 8.0x (8 wf), 15.9x (16 wf) 8.1x (8 wf), 16.2x (16 wf)

‡ .NET Framework 4.8 does not report memory allocation metrics in BenchmarkDotNet.

Observations

  • Custom operations allocate the least here (about 392 B per run in the harness).
  • Logging operations post the shortest times (~74ns on .NET 10.0).
  • The minimal-allocation workflow holds 4,126 B from 10 through 500 iterations.
  • Ordinary paths in this matrix stayed off Gen2.

For operation-by-operation results, throughput scaling, memory patterns, and concurrency charts, see Internal Benchmarks.


Competitive Performance Summary

WorkflowForge is run beside Workflow Core and Elsa on twelve shared scenarios (same script) on .NET 10.0, 8.0, and .NET Framework 4.8. Peak ratio in our log: 584x wall time (state machine vs Elsa, .NET 10.0). Shortest median: 6.90μs (creation overhead, .NET FX 4.8). Aggregate bands from the tables: 2–583x time, 1–533x allocation.

Competitive Benchmark Summary (Median, 10 iterations)

Execution Time (.NET 8.0)

# Scenario WorkflowForge Workflow Core Elsa Speed Advantage
1 Sequential (10 ops) 127μs 5661μs 18257μs 45-144x
2 Data Passing (10 ops) 133μs 5571μs 20319μs 42-153x
3 Conditional (10 ops) 129μs 5559μs 18548μs 43-143x
4 Loop (50 items) 337μs 26325μs 63196μs 78-188x
5 Concurrent (8 workers) 284μs 39289μs 110279μs 138-388x
6 Error Handling 107μs 1027μs 7383μs 10-69x
7 Creation Overhead 22.1μs 44.4μs 6.05μs 2x
8 Complete Lifecycle 105μs N/A 6580μs 63x
9 State Machine (25) 59.7μs 13798μs 33184μs 231-556x
10 Long Running* 72ms 71ms 85ms 1x
11 Parallel (16 ops) 51.7μs 1847μs 27308μs 36-528x
12 Event-Driven* 8ms 9ms 21ms 1-3x

*Long Running and Event-Driven are delay-bound; advantage is in memory.

Execution Time (.NET 10.0)

# Scenario WorkflowForge Workflow Core Elsa Speed Advantage
1 Sequential (10 ops) 119μs 7074μs 22028μs 60-186x
2 Data Passing (10 ops) 129μs 7070μs 22211μs 55-172x
3 Conditional (10 ops) 138μs 7117μs 21034μs 52-153x
4 Loop (50 items) 322μs 32922μs 58601μs 102-182x
5 Concurrent (8 workers) 260μs 47648μs 99384μs 183-383x
6 Error Handling 69.4μs 1225μs 9249μs 18-133x
7 Creation Overhead 17.9μs 45.6μs 4.25μs 3x
8 Complete Lifecycle 108μs N/A 7464μs 69x
9 State Machine (25) 59.3μs 17133μs 34627μs 289-584x
10 Long Running* 72ms 72ms 86ms 1x
11 Parallel (16 ops) 50.2μs 2214μs 22285μs 44-444x
12 Event-Driven* 8ms 8ms 21ms 1-3x

Execution Time (.NET Framework 4.8)

# Scenario WorkflowForge Workflow Core Speed Advantage
1 Sequential (10 ops) 110μs 5896μs 54x
2 Data Passing (10 ops) 122μs 5904μs 48x
3 Conditional (10 ops) 117μs 5892μs 50x
4 Loop (50 items) 326μs 28308μs 87x
5 Concurrent (8 workers) 141μs 37556μs 267x
6 Error Handling 81.0μs 10284μs 127x
7 Creation Overhead 6.90μs 13.9μs 2x
8 Complete Lifecycle 33.6μs N/A N/A
9 State Machine (25) 55.9μs 15166μs 272x
11 Parallel (16 ops) 30.6μs 1561μs 51x

Elsa does not support .NET Framework 4.8 and is excluded from this comparison.

Competitive Memory Summary (.NET 8.0)

# Scenario WorkflowForge Workflow Core Elsa Memory Advantage
1 Sequential (10 ops) 18.20KB 415.16KB 3.05MB 23-172x
2 Data Passing (10 ops) 16.84KB 415.30KB 3.03MB 25-184x
3 Conditional (10 ops) 20KB 416.54KB 3.03MB 21-156x
4 Loop (50 items) 96.18KB 2.01MB 10.76MB 21-115x
5 Concurrent (8 workers) 158.55KB 3.10MB 19.18MB 20-124x
6 Error Handling 9.24KB 44.91KB 1.08MB 5-119x
7 Creation Overhead 4.49KB 8.58KB 424 B 0.1-2x
8 Complete Lifecycle 4.46KB N/A 1.02MB 235x
9 State Machine (25) 23.94KB 1.05MB 5.89MB 45-252x
10 Long Running* 5.77KB 260.61KB 2.21MB 45-393x
11 Parallel (16 ops) 9.00KB 122.84KB 4.67MB 14-531x
12 Event-Driven* 4.21KB 36.21KB 1.03MB 9-249x

Competitive Memory Summary (.NET 10.0)

# Scenario WorkflowForge Workflow Core Elsa Memory Advantage
1 Sequential (10 ops) 18.22KB 408.23KB 3.01MB 22-169x
2 Data Passing (10 ops) 16.86KB 408KB 3.02MB 24-183x
3 Conditional (10 ops) 20KB 407.38KB 3.01MB 20-154x
4 Loop (50 items) 96.20KB 1.96MB 10.89MB 21-116x
5 Concurrent (8 workers) 158.66KB 3.04MB 19.07MB 20-123x
6 Error Handling 7.90KB 43KB 1.12MB 5-145x
7 Creation Overhead 4.51KB 8.59KB 424 B 0.1-2x
8 Complete Lifecycle 4.48KB N/A 1.00MB 229x
9 State Machine (25) 24KB 1.03MB 5.85MB 44-250x
10 Long Running* 5.78KB 255.54KB 2.19MB 44-389x
11 Parallel (16 ops) 8.75KB 120.43KB 4.56MB 14-533x
12 Event-Driven* 4.23KB 35.73KB 1.06MB 8-258x

Summary bands (all measured scenarios; delay-heavy 10/12 excluded from speed band math, September 2026 run): 2–583x faster execution; 1–533x lower allocation vs Workflow Core/Elsa.

  • Results captured on Windows 11 (25H2), Intel i7-1185G7, BenchmarkDotNet v0.15.8, 10 iterations per job.
  • Median values used; Elsa omitted on .NET Framework 4.8.
State Machine Execution (25 Transitions)
State machine, 25 transitions (.NET 10.0); values from tables below
59.3μs
17.1ms
34.6ms
.NET 10.0
59.7μs
13.8ms
33.2ms
.NET 8.0
55.9μs
15.2ms
.NET FX 4.8
WorkflowForge
Workflow Core
Elsa Workflows

For scenario breakdowns, parameter sweeps, and architectural comparisons, see Competitive Analysis.


Performance Targets and Guidelines

WorkflowForge maintains the following performance targets:

Metric Target Status
Operation execution <100μs median for simple operations Met (sub-μs for logging; chained ops ~10μs)
Workflow creation <5μs overhead Met (~48ns op creation; ~18μs workflow creation in comparative harness)
Memory baseline <5KB for minimal workflows Met (4.03KB)
GC pressure No Gen2 collections in typical workloads Met
Concurrent scaling Near-linear Met (8.0x for 8, 15.9x for 16)

Targets below come straight from the same BenchmarkDotNet matrix. Keep workflows on custom ops, modest property payloads, and tight middleware stacks if you need the same envelope.


Optimization Guide

1. Choose the Right Operation Type

  • Custom class-based operations use the least memory in benchmarks (~392 B); they are the default choice when you care about allocation.
  • Logging operations measure fastest (~74ns on .NET 10.0) for tiny steps.
  • Delegate operations are handy but cost a bit more than custom ops (~1.5μs vs ~1.3μs in the same harness).

2. Reuse Workflow Definitions

Build workflows once and execute many times. Operation creation is tens of nanoseconds; reuse still helps when definitions are heavy.

3. Optimize Data Passing

  • Use foundry.Properties for all data flow
  • Cache property reads in loops instead of repeated lookups
  • Avoid large object allocations in hot paths

4. Use Concurrency

Use ForEachWorkflowOperation for parallel execution of independent operations. Concurrency scales near-linearly (8.0x for 8 workflows, 15.9x for 16).

5. Minimize Middleware

Add only necessary middleware. Each middleware adds ~1–5μs overhead per operation.

6. Avoid Large Object Allocations

Large allocations (>85KB) trigger Gen2 GC and degrade throughput. Keep operation payloads small where possible.

7. Use .NET 10.0 Where Available

.NET 10.0 shows improved exception handling (16.6μs vs 33.9μs) and some operation gains.

For detailed code examples and patterns, see Internal Benchmarks and Operations.


Benchmark Methodology

Configuration

  • Framework: BenchmarkDotNet v0.15.8
  • Runtimes: .NET 10.0.3, .NET 8.0.24, .NET Framework 4.8.1
  • Iterations: 10 per benchmark job
  • Warmup: per BenchmarkDotNet default job
  • Invocation: 1 per iteration

Hardware

  • OS: Windows 11 (25H2)
  • CPU: Intel 11th Gen i7-1185G7
  • SDK: .NET SDK 10.0.103

Statistical Approach

  • Median values used for comparison (more stable than mean)
  • Standard deviation < 20% of mean for most scenarios
  • P95 values available for consistency verification
  • All scenarios implement identical logic across frameworks

Reproduction

Internal benchmarks:

cd src/benchmarks/WorkflowForge.Benchmarks
dotnet run -c Release -f net48 -f net8.0 -f net10.0

Competitive benchmarks:

cd src/benchmarks/WorkflowForge.Benchmarks.Comparative
dotnet run -c Release -f net48 -f net8.0 -f net10.0

BenchmarkDotNet writes output under BenchmarkDotNet.Artifacts/results/. Expect full comparative runs to take about 30–60 minutes.


Version History

Version 2.2.0 (September 2026)

  • Refreshed competitive and internal benchmark documentation from September 2026 BenchmarkDotNet runs (10 iterations per job).

Version 2.1.2 (July 2026)

  • Fixed ILRepacked extensions (Polly/OpenTelemetry/Serilog) missing transitive dependencies
  • Implemented foundry performance monitoring (EnablePerformanceMonitoring / GetPerformanceStatistics)
  • Fixed pooled-foundry state leak and audit workflow-name resolution
  • Centralized package versioning and shared metadata

Version 2.1.1 (March 2026)

  • Multi-target .NET 10.0, .NET 8.0, .NET Framework 4.8
  • Sealed operation classes
  • ConfigureAwait optimization
  • Compensation always attempts RestoreAsync (no-op default)

Version 2.0.0 (January 2026)

  • Initial release with 12 competitive scenarios
  • Head-to-head comparison with Workflow Core and Elsa Workflows