Code and Mind » Simulation Archive · About

Simulate Ring of Actors

This is a naive implementation that explores the idea of simulating a ring of N actors that send the messages to each over M times.

Concept of the ring benchmark and simulated actors is taken from the gist by Preetam Jinka.

The implementation is quite simple and allows us to focus on core concepts:

  • messages used for the communication;
  • actors representing individual processes;
  • simulation environment
  • simulation loop.


Let's start by defining a message and a simulation environment to hold a global inbox.

public struct Message {
    public readonly int Recipient;
    public readonly object Body;

    public Message(int recipient, object body) {
        Recipient = recipient;
        Body = body;
public sealed class Env {
    readonly Queue<Message> _messages = new Queue<Message>();

    public void Send(int recipient, object message) {
        _messages.Enqueue(new Message(recipient, message));

    public bool GetNextMessage(out Message msg) {
        return _messages.TryDequeue(out msg);

Actor is a class that has a recipient and can send a message to it:

public class Actor {
    readonly int NextActor;
    int _counter;
    readonly Env _env;
    readonly int _m;

    public Actor(int nextActor, Env env, int m) {
        NextActor = nextActor;
        _env = env;
        _m = m;

    public void HandleMessage(object message) {
        if (_counter <= _m) {
            _env.Send(NextActor, message);

We can join everything together by the actual simulation loop. It sets up a ring of actors and then sends the first message.

class Program {
    static void Main(string[] args) {
        const int n = 1000;
        const int m = 1000;

        var actors = new List<Actor>();
        var env = new Env();
        for (int i = 0; i < n; i++) {
            var next = (i + 1) % n;
            actors.Add(new Actor(next, sim, m));

        env.Send(0, new {hello = "world"});

        var watch = Stopwatch.StartNew();
        while (env.GetNextMessage(out var msg)) {


Given N=1000 and M=1000, the result on my machine is:

dotnet SimRing.dll


  • How would you implement a ring benchmark in golang?
  • This .NET Core implementation is way faster than Erlang. What does Erlang spend time on?
  • Why is this implementation faster than FoundationDB Flow? What extra work could the Flow do?

Next post in Simulation story: Simulate with Async