aio-pika: aio_pika is slower than pika
I have prepared two scripts to publish 10k messages, one using aio_pika and the second using pika. I measure time them need to send these messages. First script:
import asyncio
import aio_pika
import datetime
async def main(loop):
connection = await aio_pika.connect("amqp://guest:guest@172.17.0.2/", loop=loop)
routing_key = "test_queue"
channel = await connection.channel()
num_of_messages = 10000
start = datetime.datetime.now()
msg = aio_pika.Message(body=b'hello')
for _ in range(num_of_messages):
await channel.default_exchange.publish(
msg,
routing_key=routing_key
)
tt = datetime.datetime.now() - start
print(tt.total_seconds(), num_of_messages/tt.total_seconds())
await connection.close()
if __name__ == "__main__":
loop = asyncio.get_event_loop()
loop.run_until_complete(main(loop))
loop.close()
Result:
2.227745 4488.844100199978
Second:
import pika
import datetime
connection = pika.BlockingConnection(pika.URLParameters("amqp://guest:guest@172.17.0.2/"))
channel = connection.channel()
num_of_messages = 10000
routing_key = "test_queue"
start = datetime.datetime.now()
for _ in range(num_of_messages):
channel.basic_publish(exchange='',
routing_key=routing_key,
body=b'hello')
tt = datetime.datetime.now() - start
print(tt.total_seconds(), num_of_messages/tt.total_seconds())
connection.close()
Result:
0.37214 26871.60745955823
It looks pika is 6 time faster than aio_pika. Why there is such difference?
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Comments: 16 (8 by maintainers)
I ran both @mosquito’s benchmarks (I’ll call them bench1 and bench2 respectively) on my laptop and got the following results:
bench1:
8.663177 1154.3109415864412
bench2:1.87766 5325.77782985205
Then I modified the first benchmark:(basically, removed
await
per iteration and instead usedasyncio.gather
to await all coroutines)bench3:
4.197344 2382.4590026454825
(two (not quite, see below) times faster than bench1, two times slower than bench2)Then I ran bench2 and bench3 under
cProfile
, here are top 20 calls sorted bytottime
: bench2:bench3:
Obviously, there are too much function calls happening. 4 times more than required (taking bench2 as the base line). In python, abstractions aren’t free. Let’s get rid of
Message
and publish messages directly:bench4:
3.662427 2730.429848840673
Now let’s sort the cProfile output by cumulative time:We can clearly see the spike here:
So
aio-pika
’s publish is taking a lot of time. Why?I can see two possible culprits: https://github.com/mosquito/aio-pika/blob/master/aio_pika/channel.py#L207 and https://github.com/mosquito/aio-pika/blob/master/aio_pika/channel.py#L212 The latter is actually quite function-calls heavy (previous cProfile output, sorted by
tottime
):I modified my installed aio-pika
channel.py
directly, I removed the write lock completely and changedself._create_future()
toasyncio.Future()
bench5:2.64704 3777.8046421663444
It is 3.3 times faster then the original benchmark, and only 1.4 times slower (instead of 4.6 times) than the pika’sBlockingConnection
.I’m sure we can identify more bottlenecks by looking at the cProfile output more closely; I got these results in 10 minutes or so. I hope they will be helpful 😃
after release
aio-pika>=5
tests passing faster two times then similar foraio-pika<=4
@krieghan could you please repeat your tests?
@malinoff now aio-pika uses aiormq and here has only channel-scope locks. And one more thing is buffered queues for incomming amqp frames. That means the library will parse AMQP protocol ahead of consumer coroutine finished.
@michailj @akhoronko feel free to join this
performance party
. Please repeat your tests, that’s should be so awesome.@mosquito aio_pika benchmark code https://gist.github.com/michailj/f7628b83bcb57e8711c362b745291d2d pika benchmark code https://gist.github.com/michailj/bf152965af877ab90dfa3fc7e5ca3350
@michailj you are publishing to the single channel. Try to use more channels to achieve asyncio advantage. And, afaik, aio-pika uses publisher confirms (https://www.rabbitmq.com/confirms.html#publisher-confirms), but pika doesn’t by default.
Try to disable publishing confirmations
publisher_confirms=False
I mean that for instance when we need to publish a lot of messages using
await channel.default_exchange.publish
it is ineffective due to the write locks at channel level. So if it could be something like this:than there wouldn’t be any problems with speed of publishing. Isn’t it? Correct me if I’m wrong.