nova/vendor/Twisted-10.0.0/doc/conch/benchmarks/buffering_mixin.py
2010-05-27 23:05:26 -07:00

183 lines
4.9 KiB
Python
Executable File

# Copyright (c) 2006 Twisted Matrix Laboratories.
# See LICENSE for details.
"""
Benchmarks comparing the write performance of a "normal" Protocol instance
and an instance of a Protocol class which has had L{twisted.conch.mixin}'s
L{BufferingMixin<twisted.conch.mixin.BufferingMixin>} mixed in to perform
Nagle-like write coalescing.
"""
from sys import stdout
from pprint import pprint
from time import time
from twisted.python.usage import Options
from twisted.python.log import startLogging
from twisted.internet.protocol import ServerFactory, Protocol, ClientCreator
from twisted.internet.defer import Deferred
from twisted.internet import reactor
from twisted.conch.mixin import BufferingMixin
class BufferingBenchmark(Options):
"""
Options for configuring the execution parameters of a benchmark run.
"""
optParameters = [
('scale', 's', '1',
'Work multiplier (bigger takes longer, might resist noise better)')]
def postOptions(self):
self['scale'] = int(self['scale'])
class ServerProtocol(Protocol):
"""
A silent protocol which only waits for a particular amount of input and
then fires a Deferred.
"""
def __init__(self, expected, finished):
self.expected = expected
self.finished = finished
def dataReceived(self, bytes):
self.expected -= len(bytes)
if self.expected == 0:
finished, self.finished = self.finished, None
finished.callback(None)
class BufferingProtocol(Protocol, BufferingMixin):
"""
A protocol which uses the buffering mixin to provide a write method.
"""
class UnbufferingProtocol(Protocol):
"""
A protocol which provides a naive write method which simply passes through
to the transport.
"""
def connectionMade(self):
"""
Bind write to the transport's write method and flush to a no-op
function in order to provide the same API as is provided by
BufferingProtocol.
"""
self.write = self.transport.write
self.flush = lambda: None
def _write(proto, byteCount):
write = proto.write
flush = proto.flush
for i in range(byteCount):
write('x')
flush()
def _benchmark(byteCount, clientProtocol):
result = {}
finished = Deferred()
def cbFinished(ignored):
result[u'disconnected'] = time()
result[u'duration'] = result[u'disconnected'] - result[u'connected']
return result
finished.addCallback(cbFinished)
f = ServerFactory()
f.protocol = lambda: ServerProtocol(byteCount, finished)
server = reactor.listenTCP(0, f)
f2 = ClientCreator(reactor, clientProtocol)
proto = f2.connectTCP('127.0.0.1', server.getHost().port)
def connected(proto):
result[u'connected'] = time()
return proto
proto.addCallback(connected)
proto.addCallback(_write, byteCount)
return finished
def _benchmarkBuffered(byteCount):
return _benchmark(byteCount, BufferingProtocol)
def _benchmarkUnbuffered(byteCount):
return _benchmark(byteCount, UnbufferingProtocol)
def benchmark(scale=1):
"""
Benchmark and return information regarding the relative performance of a
protocol which does not use the buffering mixin and a protocol which
does.
@type scale: C{int}
@param scale: A multipler to the amount of work to perform
@return: A Deferred which will fire with a dictionary mapping each of
the two unicode strings C{u'buffered'} and C{u'unbuffered'} to
dictionaries describing the performance of a protocol of each type.
These value dictionaries will map the unicode strings C{u'connected'}
and C{u'disconnected'} to the times at which each of those events
occurred and C{u'duration'} two the difference between these two values.
"""
overallResult = {}
byteCount = 1024
bufferedDeferred = _benchmarkBuffered(byteCount * scale)
def didBuffered(bufferedResult):
overallResult[u'buffered'] = bufferedResult
unbufferedDeferred = _benchmarkUnbuffered(byteCount * scale)
def didUnbuffered(unbufferedResult):
overallResult[u'unbuffered'] = unbufferedResult
return overallResult
unbufferedDeferred.addCallback(didUnbuffered)
return unbufferedDeferred
bufferedDeferred.addCallback(didBuffered)
return bufferedDeferred
def main(args=None):
"""
Perform a single benchmark run, starting and stopping the reactor and
logging system as necessary.
"""
startLogging(stdout)
options = BufferingBenchmark()
options.parseOptions(args)
d = benchmark(options['scale'])
def cbBenchmark(result):
pprint(result)
def ebBenchmark(err):
print err.getTraceback()
d.addCallbacks(cbBenchmark, ebBenchmark)
def stopReactor(ign):
reactor.stop()
d.addBoth(stopReactor)
reactor.run()
if __name__ == '__main__':
main()