1
# Copyright (c) 2006 Twisted Matrix Laboratories.
2
# See LICENSE for details.
5
Benchmarks comparing the write performance of a "normal" Protocol instance
6
and an instance of a Protocol class which has had L{twisted.conch.mixin}'s
7
L{BufferingMixin<twisted.conch.mixin.BufferingMixin>} mixed in to perform
8
Nagle-like write coalescing.
11
from sys import stdout
12
from pprint import pprint
15
from twisted.python.usage import Options
16
from twisted.python.log import startLogging
18
from twisted.internet.protocol import ServerFactory, Protocol, ClientCreator
19
from twisted.internet.defer import Deferred
20
from twisted.internet import reactor
22
from twisted.conch.mixin import BufferingMixin
25
class BufferingBenchmark(Options):
27
Options for configuring the execution parameters of a benchmark run.
32
'Work multiplier (bigger takes longer, might resist noise better)')]
34
def postOptions(self):
35
self['scale'] = int(self['scale'])
39
class ServerProtocol(Protocol):
41
A silent protocol which only waits for a particular amount of input and
42
then fires a Deferred.
44
def __init__(self, expected, finished):
45
self.expected = expected
46
self.finished = finished
49
def dataReceived(self, bytes):
50
self.expected -= len(bytes)
51
if self.expected == 0:
52
finished, self.finished = self.finished, None
53
finished.callback(None)
57
class BufferingProtocol(Protocol, BufferingMixin):
59
A protocol which uses the buffering mixin to provide a write method.
64
class UnbufferingProtocol(Protocol):
66
A protocol which provides a naive write method which simply passes through
70
def connectionMade(self):
72
Bind write to the transport's write method and flush to a no-op
73
function in order to provide the same API as is provided by
76
self.write = self.transport.write
77
self.flush = lambda: None
81
def _write(proto, byteCount):
85
for i in range(byteCount):
91
def _benchmark(byteCount, clientProtocol):
94
def cbFinished(ignored):
95
result[u'disconnected'] = time()
96
result[u'duration'] = result[u'disconnected'] - result[u'connected']
98
finished.addCallback(cbFinished)
101
f.protocol = lambda: ServerProtocol(byteCount, finished)
102
server = reactor.listenTCP(0, f)
104
f2 = ClientCreator(reactor, clientProtocol)
105
proto = f2.connectTCP('127.0.0.1', server.getHost().port)
106
def connected(proto):
107
result[u'connected'] = time()
109
proto.addCallback(connected)
110
proto.addCallback(_write, byteCount)
115
def _benchmarkBuffered(byteCount):
116
return _benchmark(byteCount, BufferingProtocol)
120
def _benchmarkUnbuffered(byteCount):
121
return _benchmark(byteCount, UnbufferingProtocol)
125
def benchmark(scale=1):
127
Benchmark and return information regarding the relative performance of a
128
protocol which does not use the buffering mixin and a protocol which
132
@param scale: A multipler to the amount of work to perform
134
@return: A Deferred which will fire with a dictionary mapping each of
135
the two unicode strings C{u'buffered'} and C{u'unbuffered'} to
136
dictionaries describing the performance of a protocol of each type.
137
These value dictionaries will map the unicode strings C{u'connected'}
138
and C{u'disconnected'} to the times at which each of those events
139
occurred and C{u'duration'} two the difference between these two values.
145
bufferedDeferred = _benchmarkBuffered(byteCount * scale)
146
def didBuffered(bufferedResult):
147
overallResult[u'buffered'] = bufferedResult
148
unbufferedDeferred = _benchmarkUnbuffered(byteCount * scale)
149
def didUnbuffered(unbufferedResult):
150
overallResult[u'unbuffered'] = unbufferedResult
152
unbufferedDeferred.addCallback(didUnbuffered)
153
return unbufferedDeferred
154
bufferedDeferred.addCallback(didBuffered)
155
return bufferedDeferred
161
Perform a single benchmark run, starting and stopping the reactor and
162
logging system as necessary.
166
options = BufferingBenchmark()
167
options.parseOptions(args)
169
d = benchmark(options['scale'])
170
def cbBenchmark(result):
172
def ebBenchmark(err):
173
print err.getTraceback()
174
d.addCallbacks(cbBenchmark, ebBenchmark)
175
def stopReactor(ign):
177
d.addBoth(stopReactor)
181
if __name__ == '__main__':