Membus: Performance considerations

When developing a piece of code that could potentially be used as a central component, one should have a look at the performance as well. There is no easy answer as to whether MemBus is fast or not – it depends which features you are using.

I have been doing a number of performance measurements wit the following setup:

public void Run(IBus bus, TextWriter w)

    var r = new Random();
    var dict = new Dictionary<int, Func<object>>
                       {0, () => new MessageA()},
                       {1, () => new MessageB()},
                       {2, () => new MessageC()},
    int count = 0;
    var sw = Stopwatch.StartNew();
    while (count < 100000)
        bus.Publish(dict[r.Next(0, 3)]());

    w.WriteLine("Through {0}", sw.ElapsedMilliseconds);

    count = 0;
    while (count < 4)
        w.WriteLine("From MsgA:{0}({1}), B:{2}({3}), C:{4}({5})", aCount, MessageA.Count, bCount,
                    MessageB.Count, cCount, MessageC.Count);

The actual publishing happens in line 18. We publish three different messages by random, in total 100’000 messages. The three subscriptions each increment a counter, the messages themselves increment an internal counter when they are constructed. Let’s start with the simple setup (“Conservative”):


Pretty quick. Let’s compare it to the “AsyncConfiguration”:


That’s taking a while...Let us actually do some work in a subscription and put a small thread.sleep() of 1 milliseconds in the subscription of message B. Let’s go Conservative:


That could be expected! B got 33463 messages, at 1ms each it gives you roughly that number. Let’s push it through the AsyncConfiguration:


The Publishing loop comes back much quicker, but now we can see that the work hasn’t been completed yet – The message construction count differs from the subscription counters! Also note that in this scenario I had to change the Increment code (“++”) to the thread-safe version (“Interlocked.Increment(ref i)”) as the counts would not match up anymore. As opposed to the code shown, I changed the counter output to be delayed by 1 seconds. Hence, after roughly 9 seconds all work was done.

As a final check, let’s use the “Fast” setup:


Here, publishing comes back pretty quick, but the work also takes about the same time. the Fast setup was created after some sessions with the dotTrace performance profiler that showed where time could be saved. The fast setup will not do contravariant publishing (i.e. a subscription on a message of type object will not receive a message of type “MessageA”) and exceptions in subscriptions are not taken care of.

As usual, answering performance related issues isn’t easy. It very much depends on the circumstances, what kind of setup you should take, how much work subscriptions do, how many messages are sent, etc., etc.

Creative Commons License

Frank Quednau 2022