Skip to content

Commit

Permalink
More benchmarks
Browse files Browse the repository at this point in the history
  • Loading branch information
Emil Koutanov committed Jan 29, 2024
1 parent ef30877 commit fbdc86e
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 5 deletions.
4 changes: 3 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
{
"cSpell.words": [
"Backpresure",
"Schedulable"
"EPYC",
"Schedulable",
"unscheduling"
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

namespace Actors.Benchmarks;

public class AckDelivery
public class AckDeliveryBenchmark
{
class WorkItem
{
Expand Down
98 changes: 98 additions & 0 deletions Actors.Benchmarks/EchoBenchmark.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
using System;
using System.Threading.Tasks;
using BenchmarkDotNet;
using BenchmarkDotNet.Attributes;

namespace Actors.Benchmarks;
#nullable enable

public class EchoBenchmark
{
class SendPings(int pings)
{
public int Pings { get; } = pings;

public TaskCompletionSource Completion { get; } = new TaskCompletionSource();
}

class Ping {}

class Pong {}

class PingActor : Actor
{
internal PongActor? Opponent { get; set; }

private int remainingMessages;

private SendPings? sendPings;

protected override Task Perform(Inbox inbox)
{
switch (inbox.Receive())
{
case SendPings message:
this.sendPings = message;
this.remainingMessages = sendPings.Pings;
Opponent!.Send(new Ping());
break;

case Pong message:
remainingMessages--;
if (remainingMessages > 0)
{
Opponent!.Send(new Ping());
}
else
{
sendPings!.Completion.SetResult();
}
break;

default:
throw new NotSupportedException();
}
return Task.CompletedTask;
}
}

class PongActor : Actor
{
internal PingActor? Opponent { get; set; }

protected override Task Perform(Inbox inbox)
{
switch (inbox.Receive())
{
case Ping message:
Opponent!.Send(new Pong());
break;

default:
throw new NotSupportedException();
}
return Task.CompletedTask;
}
}

private readonly PingActor pingActor = new();
private readonly PongActor pongActor = new();

[GlobalSetup]
public void Setup()
{
pingActor.Opponent = pongActor;
pongActor.Opponent = pingActor;
}

[Params(1_000)]
public int numMessages;

[Benchmark]
public async Task StartAndWait()
{
var sendPings = new SendPings(numMessages);
pingActor.Send(sendPings);
await sendPings.Completion.Task;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

namespace Actors.Benchmarks;

public class FireAndForget
public class FireAndForgetBenchmark
{
class FireAndForgetWorker : Actor<int>
{
Expand Down
5 changes: 3 additions & 2 deletions Actors.Benchmarks/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ public class Program
public static void Main(string[] args)
{
var config = DefaultConfig.Instance;
BenchmarkRunner.Run<FireAndForget>(config, args);
BenchmarkRunner.Run<AckDelivery>(config, args);
BenchmarkRunner.Run<FireAndForgetBenchmark>(config, args);
BenchmarkRunner.Run<AckDeliveryBenchmark>(config, args);
BenchmarkRunner.Run<EchoBenchmark>(config, args);

// Use this to select benchmarks from the console:
// var summaries = BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args, config);
Expand Down
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,24 @@ Pass the name of an example to run just that. For example, to run the `Batch` ex
```sh
just run Batch
```

# Performance
Most of the overhead in an actor system is in the scheduling of dormant actors when they receive their first message and the subsequent unscheduling when they process their last. We'll call this the 'slow path'.

When an actor is constantly posted work at a rate close to its throughput, it will remain scheduled and the overhead is one monitor entry per `Send()` and one per `Receive()`. We'll call this the 'fast path'.

Benchmarks were run using the following setup:

|&nbsp; |&nbsp;
|-----------------|-------------------------------------
|Harness |BenchmarkDotNet v0.13.12
|OS |Ubuntu 22.04.3 LTS (Jammy Jellyfish)
|Framework |.NET SDK 8.0.101
|Processor |AMD EPYC 7763, 1 CPU, 4 logical and 2 physical cores, virtualised

Results:

|Path |Time-cost |Methodology
|-----------------|--------------|---------------
|Fast |106 ns/op |FireAndForgetBenchmark, time / number of messages
|Slow |1 µs/op |EchoBenchmark, time / number of messages / 2

0 comments on commit fbdc86e

Please sign in to comment.