# EventDispatcher Performance

**URL:** <https://discourse.nameko.io/t/eventdispatcher-performance/262>\
**Category:** googlegroup\
**Created:** [June 26, 2018, 11:52am UTC](https://discourse.nameko.io/t/eventdispatcher-performance/262 "2018-06-26T11:52:25Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![PtrBld](https://avatars.discourse-cdn.com/v4/letter/p/e0b2c6/32.png) [@PtrBld](https://discourse.nameko.io/u/PtrBld)\
**Post date:** [June 26, 2018, 11:52am UTC](https://discourse.nameko.io/t/eventdispatcher-performance/262/1 "2018-06-26T11:52:25Z")

</div>

Hi all,

I am currently creating some event-driven microservices with nameko. I have  
one service which dispatches a lot of events per worker (a loop with around  
30000 items). However, the event dispatching per worker feels quite slow  
(22/s) and therefore the execution takes a lot of time.  
Now I wonder if I am doing something wrong or if this is just normal.

Can anyone share their experiences?  
Thanks!

---

<div class="post-metadata">

**Author:** ![mattbennett](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.nameko.io/mattbennett/32/13_2.png) [@mattbennett](https://discourse.nameko.io/u/mattbennett)\
**Post date:** [June 26, 2018, 1:22pm UTC](https://discourse.nameko.io/t/eventdispatcher-performance/262/2 "2018-06-26T13:22:57Z")

</div>

20 msg/sec is slow. Message publishing performance depends on a few factors  
and tradeoffs, and the Nameko defaults choose the safest (and slowest)  
combinations.

Probably the biggest factor is latency between your service and the  
RabbitMQ broker. On my laptop with a local RabbitMQ, this simple service  
gives me ~200 msg/sec:

from nameko.events import EventDispatcher

from nameko.rpc import rpc

class Service:  
&nbsp;&nbsp;&nbsp;&nbsp;name = "dispatcher"

&nbsp;&nbsp;&nbsp;&nbsp;dispatch = EventDispatcher()

&nbsp;&nbsp;&nbsp;&nbsp;@rpc  
&nbsp;&nbsp;&nbsp;&nbsp;def go(self):  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for \_ in range(30000):  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self.dispatch("eventtype", "payload")

It drops to less than 10 msg/sec if I use a (free, slow) cloud-hosted  
RabbitMQ a few hundred milliseconds away.

The impact of the latency is partly because \*publish confirms\* are enabled.  
You can disable them:

&nbsp;&nbsp;&nbsp;&nbsp;...  
&nbsp;&nbsp;&nbsp;&nbsp;dispatch = EventDispatcher(use\_confirms=False)  
&nbsp;&nbsp;&nbsp;&nbsp;...

This bumps my local delivery to to ~300 msg/sec, with my RabbitMQ docker  
image and VM pegging a CPU each. The Nameko service isn't working  
particularly hard.

You can also disable persistence of the messages to disk when they reach  
the broker:

&nbsp;&nbsp;&nbsp;&nbsp;...  
&nbsp;&nbsp;&nbsp;&nbsp;dispatch = EventDispatcher(persistence="transient")  
&nbsp;&nbsp;&nbsp;&nbsp;...

Transient messages may be helpful if your payloads are large.

If latency is the problem, you can improve performance by dispatching  
messages in multiple parallel threads. If I call initiate 10 concurrent RPC  
workers going around the loop, I get 10x the msg/sec at the broker.

(nameko) Matts-13-inch-Macbook-Pro:nameko mattbennett$ nameko shell  
--broker amqp://mxwsvifk:\*\*\*\*@lemur.cloudamqp.com/mxwsvifk  
Nameko Python 3.5.3 (default, Jun 30 2017, 18:28:54)  
[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.42.1)] shell on darwin  
Broker:amqp://mxwsvifk:\*\*\*\*@lemur.cloudamqp.com/mxwsvifk

> > > for \_ in range(10): n.rpc.dispatcher.go.call\_async()

...  
\<nameko.rpc.RpcReply object at 0x10bf1da58\>  
\<nameko.rpc.RpcReply object at 0x10bf1dc88\>  
\<nameko.rpc.RpcReply object at 0x10bf1d470\>  
\<nameko.rpc.RpcReply object at 0x10bfa0400\>  
\<nameko.rpc.RpcReply object at 0x10bf1d3c8\>  
\<nameko.rpc.RpcReply object at 0x10bfa0518\>  
\<nameko.rpc.RpcReply object at 0x10bfa0f98\>  
...

Hope that helps.

Matt.

> **···**
>
> On Tuesday, June 26, 2018 at 12:52:25 PM UTC+1, PtrBld wrote:
> 
> > Hi all,
> > 
> > I am currently creating some event-driven microservices with nameko. I  
> > have one service which dispatches a lot of events per worker (a loop with  
> > around 30000 items). However, the event dispatching per worker feels quite  
> > slow (22/s) and therefore the execution takes a lot of time.  
> > Now I wonder if I am doing something wrong or if this is just normal.
> > 
> > Can anyone share their experiences?  
> > Thanks!

---

<div class="post-metadata">

**Author:** ![PtrBld](https://avatars.discourse-cdn.com/v4/letter/p/e0b2c6/32.png) [@PtrBld](https://discourse.nameko.io/u/PtrBld)\
**Post date:** [June 26, 2018, 2:34pm UTC](https://discourse.nameko.io/t/eventdispatcher-performance/262/3 "2018-06-26T14:34:15Z")

</div>

Hi Matt,

thank you very much (!) for your fast and detailed response. I just tried  
different things and it looks promising.  
I want to share my results here, because someone might find this  
interesting.

First of all I tried the ```use_confirm``` flag.  
This indeed gives me about 3 times faster event dispatching.

However, what really makes it a lot faster is processing the large list in  
parallel. Thereby I get another 10x faster processing (about 400 to 1000  
events per second).  
What I did is spliting my large list in buckets of 1000 items each and  
execute them in parallel (using x worker threads). I have decided to split  
the list and dispatch events to the same service atm. but I might change  
this in the future.

Simple example to get the idea:

class Service(object):  
&nbsp;&nbsp;&nbsp;&nbsp;name = "processing\_service"  
&nbsp;&nbsp;&nbsp;&nbsp;config = Config()  
&nbsp;&nbsp;&nbsp;&nbsp;dispatch = EventDispatcher(use\_confirms=False)  
&nbsp;&nbsp;&nbsp;&nbsp;# more dependencies

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@rpc  
&nbsp;&nbsp;&nbsp;&nbsp;def create\_slices(self):  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;count = get\_large\_list().count() # up to 10000000 items  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;window\_idx = 0  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;window\_size = self.config["PROCESSING\_WINDOW\_SIZE"] if  
"PROCESSING\_WINDOW\_SIZE" in self.config else 1000  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for i in range(window\_size, count + window\_size, window\_size):  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self.dispatch(PROCESS\_ITEM, {"slice\_start": window\_idx})  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;window\_idx = i

&nbsp;&nbsp;&nbsp;&nbsp;@event\_handler(name, PROCESS\_ITEM)  
&nbsp;&nbsp;&nbsp;&nbsp;def process\_item(self, data):  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;start = data["slice\_start"]  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;window\_size = self.config["PROCESSING\_WINDOW\_SIZE"] if  
"PROCESSING\_WINDOW\_SIZE" in self.config else 1000  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;get\_large\_list().skip(start).limit(window\_size)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for item in items:  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# do some stuff  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self.dispatch(ACTUAL\_EVENT,item)

Thanks again!
