[Twisted-web] making streams optional in web2
James Y Knight
foom at fuhm.net
Thu Feb 9 11:40:49 MST 2006
On Feb 9, 2006, at 6:21 AM, glyph at divmod.com wrote:
> A few days ago, JP showed me some benchmarks of web2 vs. CherryPy
> hosting a trivial WSGI application.
>
> It turns out it's 4x slower, clocking in only ~250 req/sec on a
> middle-spec Opteron, as opposed to CherryPy's ~1000. From previous
> benchmarks, I happen to know that Apache can do ~3000 req/sec on
> that same hardware, making web2 well over 10x slower.
>
> I didn't look at the profile too closely, but preliminarily it
> seemed clear that the major bottleneck at this point was the
> creation and invocation of tons of Deferreds in
> _NotifyingProducerStream, while generating the response.
>
> Considering that streams aren't used at all in the actual HTTP
> protocol implementation (and thank goodness for that, given this
> discovery) it strikes me that the use of them in the response-
> generating API ought to be more optional, for cases where
> efficiency is important. Like, for example, DAV, which also seems
> to be making embarrassingly heavy use of the streams API - I
> haven't benchmarked that yet, but I'm sure it will be an adventure
> if I do :).
Benchmarking is a dangerous activity.
I only get 131req/sec from ab on my mac from the following IResource
resource, vs 114 from the wsgi resource, vs 706 from apache. Vs 74
from cherrypy's tut01_helloworld.py. So, shrug, I'm sure my benchmark
sucks, and must be measuring something completely different than
yours, given the wide variance in relative numbers. Anyhow, I'm happy
that someone is benchmarking web2, because I haven't done so any time
recently, but what you write above comes quite close to random flaming.
Here's the simple resources:
def simple_wsgi_app(environ, start_response):
start_response("200 OK", [])
return ['Hello world!']
class SimpleResource(object):
implements(iweb.IResource)
def renderHTTP(self, req):
return http.Response(200, None, "Hello world!")
def locateChild(self, req, seg):
return self, ()
Here's all the construction of Deferreds from the native response.
Only one is from the streams module. Looks like some of the others
could be removed without changing any APIs, if that actually helps
things. Going through the WSGI interface adds one additional deferred
construction in addition to these 4.
(Pdb) break twisted.internet.defer.Deferred.__init__
Breakpoint 1 at /Users/jknight/Tw/trunk/twisted/internet/defer.py:163
(Pdb) bt
/Users/jknight/Tw/trunk/twisted/internet/selectreactor.py(139)
_doReadOrWrite()
-> why = getattr(selectable, method)()
/Users/jknight/Tw/trunk/twisted/internet/tcp.py(350)doRead()
-> return self.protocol.dataReceived(data)
/Users/jknight/Tw/trunk/twisted/protocols/basic.py(232)dataReceived()
-> why = self.lineReceived(line)
/Users/jknight/Tw/trunk/twisted/web2/channel/http.py(664)
lineReceived()
-> self.chanRequest.lineReceived(line)
/Users/jknight/Tw/trunk/twisted/web2/channel/http.py(142)
lineReceived()
-> self.processRequest()
/Users/jknight/Tw/trunk/twisted/web2/channel/http.py(384)
processRequest()
-> self.request.process()
/Users/jknight/Tw/trunk/twisted/web2/server.py(256)process()
-> d = self._getChild(self.site.resource, self.postpath)
/Users/jknight/Tw/trunk/twisted/web2/server.py(283)_getChild()
-> return defer.maybeDeferred(
/Users/jknight/Tw/trunk/twisted/internet/defer.py(116)maybeDeferred()
-> return succeed(result)
/Users/jknight/Tw/trunk/twisted/internet/defer.py(49)succeed()
-> d = Deferred()
> /Users/jknight/Tw/trunk/twisted/internet/defer.py(163)__init__()
-> self.callbacks = []
(Pdb) bt
/Users/jknight/Tw/trunk/twisted/internet/selectreactor.py(139)
_doReadOrWrite()
-> why = getattr(selectable, method)()
/Users/jknight/Tw/trunk/twisted/internet/tcp.py(350)doRead()
-> return self.protocol.dataReceived(data)
/Users/jknight/Tw/trunk/twisted/protocols/basic.py(232)dataReceived()
-> why = self.lineReceived(line)
/Users/jknight/Tw/trunk/twisted/web2/channel/http.py(664)
lineReceived()
-> self.chanRequest.lineReceived(line)
/Users/jknight/Tw/trunk/twisted/web2/channel/http.py(142)
lineReceived()
-> self.processRequest()
/Users/jknight/Tw/trunk/twisted/web2/channel/http.py(384)
processRequest()
-> self.request.process()
/Users/jknight/Tw/trunk/twisted/web2/server.py(256)process()
-> d = self._getChild(self.site.resource, self.postpath)
/Users/jknight/Tw/trunk/twisted/web2/server.py(283)_getChild()
-> return defer.maybeDeferred(
/Users/jknight/Tw/trunk/twisted/internet/defer.py(191)addCallback()
-> callbackKeywords=kw)
/Users/jknight/Tw/trunk/twisted/internet/defer.py(182)addCallbacks()
-> self._runCallbacks()
/Users/jknight/Tw/trunk/twisted/internet/defer.py(307)_runCallbacks()
-> self.result = callback(self.result, *args, **kw)
/Users/jknight/Tw/trunk/twisted/web2/server.py(312)_handleSegment()
-> return self._getChild(newres, newpath)
/Users/jknight/Tw/trunk/twisted/web2/server.py(281)_getChild()
-> return defer.succeed(res)
/Users/jknight/Tw/trunk/twisted/internet/defer.py(49)succeed()
-> d = Deferred()
> /Users/jknight/Tw/trunk/twisted/internet/defer.py(163)__init__()
-> self.callbacks = []
(Pdb) bt
/Users/jknight/Tw/trunk/twisted/internet/selectreactor.py(139)
_doReadOrWrite()
-> why = getattr(selectable, method)()
/Users/jknight/Tw/trunk/twisted/internet/tcp.py(350)doRead()
-> return self.protocol.dataReceived(data)
/Users/jknight/Tw/trunk/twisted/protocols/basic.py(232)dataReceived()
-> why = self.lineReceived(line)
/Users/jknight/Tw/trunk/twisted/web2/channel/http.py(664)
lineReceived()
-> self.chanRequest.lineReceived(line)
/Users/jknight/Tw/trunk/twisted/web2/channel/http.py(142)
lineReceived()
-> self.processRequest()
/Users/jknight/Tw/trunk/twisted/web2/channel/http.py(384)
processRequest()
-> self.request.process()
/Users/jknight/Tw/trunk/twisted/web2/server.py(258)process()
-> d.addCallback(self._cbFinishRender)
/Users/jknight/Tw/trunk/twisted/internet/defer.py(191)addCallback()
-> callbackKeywords=kw)
/Users/jknight/Tw/trunk/twisted/internet/defer.py(182)addCallbacks()
-> self._runCallbacks()
/Users/jknight/Tw/trunk/twisted/internet/defer.py(307)_runCallbacks()
-> self.result = callback(self.result, *args, **kw)
/Users/jknight/Tw/trunk/twisted/web2/server.py(355)_cbFinishRender()
-> d = defer.Deferred()
> /Users/jknight/Tw/trunk/twisted/internet/defer.py(163)__init__()
-> self.callbacks = []
(Pdb) bt
/Users/jknight/Tw/trunk/twisted/internet/selectreactor.py(139)
_doReadOrWrite()
-> why = getattr(selectable, method)()
/Users/jknight/Tw/trunk/twisted/internet/tcp.py(350)doRead()
-> return self.protocol.dataReceived(data)
/Users/jknight/Tw/trunk/twisted/protocols/basic.py(232)dataReceived()
-> why = self.lineReceived(line)
/Users/jknight/Tw/trunk/twisted/web2/channel/http.py(664)
lineReceived()
-> self.chanRequest.lineReceived(line)
/Users/jknight/Tw/trunk/twisted/web2/channel/http.py(142)
lineReceived()
-> self.processRequest()
/Users/jknight/Tw/trunk/twisted/web2/channel/http.py(384)
processRequest()
-> self.request.process()
/Users/jknight/Tw/trunk/twisted/web2/server.py(258)process()
-> d.addCallback(self._cbFinishRender)
/Users/jknight/Tw/trunk/twisted/internet/defer.py(191)addCallback()
-> callbackKeywords=kw)
/Users/jknight/Tw/trunk/twisted/internet/defer.py(182)addCallbacks()
-> self._runCallbacks()
/Users/jknight/Tw/trunk/twisted/internet/defer.py(307)_runCallbacks()
-> self.result = callback(self.result, *args, **kw)
/Users/jknight/Tw/trunk/twisted/web2/server.py(359)_cbFinishRender()
-> d.callback(response)
/Users/jknight/Tw/trunk/twisted/internet/defer.py(229)callback()
-> self._startRunCallbacks(result)
/Users/jknight/Tw/trunk/twisted/internet/defer.py(294)
_startRunCallbacks()
-> self._runCallbacks()
/Users/jknight/Tw/trunk/twisted/internet/defer.py(307)_runCallbacks()
-> self.result = callback(self.result, *args, **kw)
/Users/jknight/Tw/trunk/twisted/web2/http.py(419)writeResponse()
-> d = stream.StreamProducer(response.stream).beginProducing
(self.chanRequest)
/Users/jknight/Tw/trunk/twisted/web2/stream.py(725)beginProducing()
-> finishedCallback = self.finishedCallback = Deferred()
> /Users/jknight/Tw/trunk/twisted/internet/defer.py(163)__init__()
-> self.callbacks = []
James
More information about the Twisted-web
mailing list