/usr/share/doc/twisted-doc/benchmarks/netstringreceiver.py is in twisted-doc 13.2.0-1ubuntu1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 | # Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
from twisted.test import proto_helpers, test_protocols
import math
import time
import sys
import os
import gc
NETSTRING_PREFIX_TEMPLATE ="%d:"
NETSTRING_POSTFIX = ","
USAGE = """\
Usage: %s <number> <filename>
This script creates up to 2 ** <number> chunks with up to 2 **
<number> characters and sends them to the NetstringReceiver. The
sorted performance data for all combination is written to <filename>
afterwards.
You might want to start with a small number, maybe 10 or 12, and slowly
increase it. Stop when the performance starts to deteriorate ;-).
"""
class PerformanceTester(object):
"""
A class for testing the performance of some
"""
headers = []
lineFormat = ""
performanceData = {}
def __init__(self, filename):
"""
Initializes C{self.filename}.
If a file with this name already exists, asks if it should be
overwritten. Terminates with exit status 1, if the user does
not accept.
"""
if os.path.isfile(filename):
response = raw_input(("A file named %s exists. "
"Overwrite it (y/n)? ") % filename)
if response.lower() != "y":
print "Performance test cancelled."
sys.exit(1)
self.filename = filename
def testPerformance(self, number):
"""
Drives the execution of C{performTest} with arguments between
0 and C{number - 1}.
@param number: Defines the number of test runs to be performed.
@type number: C{int}
"""
for iteration in xrange(number):
self.performTest(iteration)
def performTest(self, iteration):
"""
Performs one test iteration. Overwrite this.
@param iteration: The iteration number. Can be used to configure
the test.
@type iteration: C{int}
@raise NotImplementedError: because this method has to be implemented
by the subclass.
"""
raise NotImplementedError
def createReport(self):
"""
Creates a file and writes a table with performance data.
The performance data are ordered by the total size of the netstrings.
In addition they show the chunk size, the number of chunks and the
time (in seconds) that elapsed while the C{NetstringReceiver}
received the netstring.
@param filename: The name of the report file that will be written.
@type filename: C{str}
"""
self.outputFile = open(self.filename, "w")
self.writeHeader()
self.writePerformanceData()
self.writeLineSeparator()
print "The report was written to %s." % self.filename
def writeHeader(self):
"""
Writes the table header for the report.
"""
self.writeLineSeparator()
self.outputFile.write("| %s |\n" % (" | ".join(self.headers),))
self.writeLineSeparator()
def writeLineSeparator(self):
"""
Writes a 'line separator' made from '+' and '-' characters.
"""
dashes = ("-" * (len(header) + 2) for header in self.headers)
self.outputFile.write("+%s+\n" % "+".join(dashes))
def writePerformanceData(self):
"""
Writes one line for each item in C{self.performanceData}.
"""
for combination, elapsed in sorted(self.performanceData.iteritems()):
totalSize, chunkSize, numberOfChunks = combination
self.outputFile.write(self.lineFormat %
(totalSize, chunkSize, numberOfChunks,
elapsed))
class NetstringPerformanceTester(PerformanceTester):
"""
A class for determining the C{NetstringReceiver.dataReceived} performance.
Instantiates a C{NetstringReceiver} and calls its
C{dataReceived()} method with different chunks sizes and numbers
of chunks. Presents a table showing the relation between input
data and time to process them.
"""
headers = ["Chunk size", "Number of chunks", "Total size",
"Time to receive" ]
lineFormat = ("| %%%dd | %%%dd | %%%dd | %%%d.4f |\n" %
tuple([len(header) for header in headers]))
def __init__(self, filename):
"""
Sets up the output file and the netstring receiver that will be
used for receiving data.
@param filename: The name of the file for storing the report.
@type filename: C{str}
"""
PerformanceTester.__init__(self, filename)
transport = proto_helpers.StringTransport()
self.netstringReceiver = test_protocols.TestNetstring()
self.netstringReceiver.makeConnection(transport)
def performTest(self, number):
"""
Tests the performance of C{NetstringReceiver.dataReceived}.
Feeds netstrings of various sizes in different chunk sizes
to a C{NetstringReceiver} and stores the elapsed time in
C{self.performanceData}.
@param number: The maximal chunks size / number of
chunks to be checked.
@type number: C{int}
"""
chunkSize = 2 ** number
numberOfChunks = chunkSize
while numberOfChunks:
self.testCombination(chunkSize, numberOfChunks)
numberOfChunks = numberOfChunks // 2
def testCombination(self, chunkSize, numberOfChunks):
"""
Tests one combination of chunk size and number of chunks.
@param chunkSize: The size of one chunk to be sent to the
C{NetstringReceiver}.
@type chunkSize: C{int}
@param numberOfChunks: The number of C{chunkSize}-sized chunks to
be sent to the C{NetstringReceiver}.
@type numberOfChunks: C{int}
"""
chunk, dataSize = self.configureCombination(chunkSize, numberOfChunks)
elapsed = self.receiveData(chunk, numberOfChunks, dataSize)
key = (chunkSize, numberOfChunks, dataSize)
self.performanceData[key] = elapsed
def configureCombination(self, chunkSize, numberOfChunks):
"""
Updates C{MAX_LENGTH} for {self.netstringReceiver} (to avoid
C{NetstringParseErrors} that might be raised if the size
exceeds the default C{MAX_LENGTH}).
Calculates and returns one 'chunk' of data and the total size
of the netstring.
@param chunkSize: The size of chunks that will be received.
@type chunkSize: C{int}
@param numberOfChunks: The number of C{chunkSize}-sized chunks
that will be received.
@type numberOfChunks: C{int}
@return: A tuple consisting of string of C{chunkSize} 'a'
characters and the size of the netstring data portion.
"""
chunk = "a" * chunkSize
dataSize = chunkSize * numberOfChunks
self.netstringReceiver.MAX_LENGTH = dataSize
numberOfDigits = math.ceil(math.log10(dataSize)) + 1
return chunk, dataSize
def receiveData(self, chunk, numberOfChunks, dataSize):
dr = self.netstringReceiver.dataReceived
now = time.time()
dr(NETSTRING_PREFIX_TEMPLATE % (dataSize,))
for idx in xrange(numberOfChunks):
dr(chunk)
dr(NETSTRING_POSTFIX)
elapsed = time.time() - now
assert self.netstringReceiver.received, "Didn't receive string!"
return elapsed
def disableGarbageCollector():
gc.disable()
print 'Disabled Garbage Collector.'
def main(number, filename):
disableGarbageCollector()
npt = NetstringPerformanceTester(filename)
npt.testPerformance(int(number))
npt.createReport()
if __name__ == "__main__":
if len(sys.argv) < 3:
print USAGE % sys.argv[0]
sys.exit(1)
main(*sys.argv[1:3])
|