I was interested in the performance differences between Twisted and EventMachine for an application I’ve developing which is going to have to handle a lot of load as clients will be regularly polling it.
Disclaimer: I’m fairly new to Python and Twisted – so there may be a way of speeding them up which I’m not aware of – perhaps by compiling to byte code or using a stackless interpreter.
Rather than test performance at a TCP level, I thought I’d do more of a real world example – and test HTTP servers. Thin (combined with Rack) is a HTTP layer to EventMachine. Twisted has its own HTTP parser built in.
Results (recorded with ab)
Concurrency: 20 Requests: 2000
- EventMachine: Requests per second: 3327.79 #/sec (mean)
- Twisted: Requests per second: 3194.76 #/sec (mean)
Concurrency: 200 Requests: 20000
- EventMachine: Requests per second: 4401.80 #/sec (mean)
- Twisted: Requests per second: 4761.90 #/sec (mean)
Conclusion
Take these tests with a pinch of salt, as the old saying goes something along the lines of ‘There are lies, damned lies, and then there are statistics’.
The results are remarkably similar – with EM beating Twisted at some concurrency levels, and vica versa. There doesn’t seem to be an obvious difference in requests per second – even with an extremely large volume of requests.
To be honest, I’m a bit surprised. Ruby isn’t known for it’s speed, and we’re already using 2 more gems, Thin and Rack which, although they are as light as possible, still add overhead.
When it comes down to virtual memory Ruby uses a hefty 47 mb, but Python isn’t much better at 39 mb. I don’t suppose that would be a deal breaker for most people though.
I call it a draw ;)
Versions
EventMachine | 0.10.0 |
Ruby | 1.8.6 |
Thin | 0.7.0 |
Python | 2.5 |
Twisted | 2.5.0 |
em_test.ru
app = proc do |env|
[200, {"Content-Type" => "text/plain"}, ["Hello. The time is #{Time.now.to_i}"]]
end
run app
Run with: thin start —rackup em_test.ru
twisted_test.py
import time
from twisted.internet import protocol, reactor
class TimeProtocol(protocol.Protocol):
def connectionMade(self):
self.transport.write(
'Hello. The time is %s' % time.time())
self.transport.loseConnection()
class TimeFactory(protocol.ServerFactory):
protocol = TimeProtocol
reactor.listenTCP(3001, TimeFactory())
reactor.run()
Run with: python twisted_test.py