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