1193a9a5caffb9fcc70a2740604856dc081561ef
[platform/framework/web/crosswalk.git] / src / third_party / pyftpdlib / src / test / bench.py
1 #!/usr/bin/env python
2 # $Id: bench.py 977 2012-01-22 23:05:09Z g.rodola $
3 #
4 #  pyftpdlib is released under the MIT license, reproduced below:
5 #  ======================================================================
6 #  Copyright (C) 2007-2012 Giampaolo Rodola' <g.rodola@gmail.com>
7 #
8 #                         All Rights Reserved
9 #
10 # Permission is hereby granted, free of charge, to any person
11 # obtaining a copy of this software and associated documentation
12 # files (the "Software"), to deal in the Software without
13 # restriction, including without limitation the rights to use,
14 # copy, modify, merge, publish, distribute, sublicense, and/or sell
15 # copies of the Software, and to permit persons to whom the
16 # Software is furnished to do so, subject to the following
17 # conditions:
18 #
19 # The above copyright notice and this permission notice shall be
20 # included in all copies or substantial portions of the Software.
21 #
22 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
24 # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
26 # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
27 # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
29 # OTHER DEALINGS IN THE SOFTWARE.
30 #
31 #  ======================================================================
32
33 """
34 FTP server benchmark script.
35
36 In order to run this you must have a listening FTP server with a user
37 with writing permissions configured.
38
39 Example usages:
40   python bench.py -u USER -p PASSWORD
41   python bench.py -u USER -p PASSWORD -H 127.0.0.1 -P 21
42   python bench.py -u USER -p PASSWORD -b transfer
43   python bench.py -u USER -p PASSWORD -b concurrence
44   python bench.py -u USER -p PASSWORD -b all
45   python bench.py -u USER -p PASSWORD -b concurrence -n 500
46 """
47
48 # Some benchmarks (Linux 3.0.0, Intel core duo - 3.1 Ghz).
49 #
50 # pyftpdlib 0.6.0:
51 #
52 #   STOR (client -> server)                      512.70 MB/sec
53 #   RETR (server -> client)                      689.49 MB/sec
54 #   200 concurrent clients (connect, login)        0.26 secs
55 #   200 concurrent clients (RETR 10M file)         3.24 secs
56 #   200 concurrent clients (STOR 10M file)         3.80 secs
57 #   200 concurrent clients (quit)                  0.04 secs
58 #
59 # pyftpdlib 0.7.0:
60 #
61 #   STOR (client -> server)                      508.80 MB/sec
62 #   RETR (server -> client)                     1635.14 MB/sec
63 #   200 concurrent clients (connect, login)        0.22 secs
64 #   200 concurrent clients (RETR 10M file)         2.33 secs
65 #   200 concurrent clients (STOR 10M file)         3.83 secs
66 #   200 concurrent clients (quit)                  0.02 secs
67 #
68 # proftpd 1.3.4rc2:
69 #
70 #   STOR (client -> server)                      609.22 MB/sec
71 #   RETR (server -> client)                     1313.77 MB/sec
72 #   200 concurrent clients (connect, login)        7.53 secs
73 #   200 concurrent clients (RETR 10M file)         2.76 secs
74 #   200 concurrent clients (STOR 10M file)         6.39 secs
75 #   200 concurrent clients (quit)                  0.23 secs
76
77
78 from __future__ import with_statement, division
79 import ftplib
80 import sys
81 import os
82 import atexit
83 import time
84 import optparse
85 import contextlib
86 import asyncore
87 import asynchat
88
89
90 HOST = 'localhost'
91 PORT = 21
92 USER = None
93 PASSWORD = None
94 TESTFN = "$testfile"
95 TESTFILE_SIZE = 1024 * 1024 * 100  # 100 MB
96 BUFFER_LEN = 8192
97
98
99 # http://goo.gl/6V8Rm
100 def hilite(string, ok=True, bold=False):
101     """Return an highlighted version of 'string'."""
102     attr = []
103     if ok is None:  # no color
104         pass
105     elif ok:   # green
106         attr.append('32')
107     else:   # red
108         attr.append('31')
109     if bold:
110         attr.append('1')
111     return '\x1b[%sm%s\x1b[0m' % (';'.join(attr), string)
112
113 if not sys.stdout.isatty() or os.name != 'posix':
114     def hilite(s, *args, **kwargs):
115         return s
116
117 def print_bench(what, value, unit=""):
118     s = "%s %s %s" % (hilite("%-42s" % what, ok=None, bold=0),
119                       hilite("%8.2f" % value),
120                       unit)
121     print s.strip()
122
123 # http://goo.gl/zeJZl
124 def bytes2human(n, format="%(value)i%(symbol)s"):
125     """
126     >>> bytes2human(10000)
127     '9K'
128     >>> bytes2human(100001221)
129     '95M'
130     """
131     symbols = ('B', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y')
132     prefix = {}
133     for i, s in enumerate(symbols[1:]):
134         prefix[s] = 1 << (i+1)*10
135     for symbol in reversed(symbols[1:]):
136         if n >= prefix[symbol]:
137             value = float(n) / prefix[symbol]
138             return format % locals()
139     return format % dict(symbol=symbols[0], value=n)
140
141 def timethis(what):
142     """"Utility function for making simple benchmarks (calculates time calls).
143     It can be used either as a context manager or as a decorator.
144     """
145     @contextlib.contextmanager
146     def benchmark():
147         timer = time.clock if sys.platform == "win32" else time.time
148         start = timer()
149         yield
150         stop = timer()
151         res = (stop - start)
152         print_bench(what, res, "secs")
153
154     if hasattr(what,"__call__"):
155         def timed(*args,**kwargs):
156             with benchmark():
157                 return what(*args,**kwargs)
158         return timed
159     else:
160         return benchmark()
161
162 def connect():
163     """Connect to FTP server, login and return an ftplib.FTP instance."""
164     ftp = ftplib.FTP()
165     ftp.connect(HOST, PORT)
166     ftp.login(USER, PASSWORD)
167     return ftp
168
169 def retr(ftp):
170     """Same as ftplib's retrbinary() but discard the received data."""
171     ftp.voidcmd('TYPE I')
172     conn = ftp.transfercmd("RETR " + TESTFN)
173     recv_bytes = 0
174     while 1:
175         data = conn.recv(BUFFER_LEN)
176         if not data:
177             break
178         recv_bytes += len(data)
179     conn.close()
180     ftp.voidresp()
181
182 def stor(ftp, size):
183     """Same as ftplib's storbinary() but just sends dummy data
184     instead of reading it from a real file.
185     """
186     ftp.voidcmd('TYPE I')
187     conn = ftp.transfercmd("STOR " + TESTFN)
188     chunk = 'x' * BUFFER_LEN
189     total_sent = 0
190     while 1:
191         sent = conn.send(chunk)
192         total_sent += sent
193         if total_sent >= size:
194             break
195     conn.close()
196     ftp.voidresp()
197
198 def bytes_per_second(ftp, retr=True):
199     """Return the number of bytes transmitted in 1 second."""
200     stop_at = time.time() + 1.0
201     bytes = 0
202     if retr:
203         def request_file():
204             ftp.voidcmd('TYPE I')
205             conn = ftp.transfercmd("retr " + TESTFN)
206             return conn
207
208         conn = request_file()
209         while stop_at > time.time():
210             chunk = conn.recv(BUFFER_LEN)
211             if not chunk:
212                 a = time.time()
213                 while conn.recv(BUFFER_LEN):
214                     break
215                 conn.close()
216                 ftp.voidresp()
217                 conn = request_file()
218                 stop_at += time.time() - a
219             bytes += len(chunk)
220         conn.close()
221         try:
222             ftp.voidresp()
223         except (ftplib.error_temp, ftplib.error_perm):
224             pass
225     else:
226         ftp.voidcmd('TYPE I')
227         conn = ftp.transfercmd("STOR " + TESTFN)
228         chunk = 'x' * BUFFER_LEN
229         stop_at = time.time() + 1
230         while stop_at > time.time():
231             bytes += conn.send(chunk)
232         conn.close()
233         ftp.voidresp()
234
235     ftp.quit()
236     return bytes
237
238 def cleanup():
239     ftp = connect()
240     try:
241         ftp.delete(TESTFN)
242     except (ftplib.error_perm, ftplib.error_temp):
243         pass
244     ftp.quit()
245
246
247 class AsyncReader(asyncore.dispatcher):
248     """Just read data from a connected socket, asynchronously."""
249
250     def __init__(self, sock):
251         asyncore.dispatcher.__init__(self, sock)
252
253     def writable(self):
254         return False
255
256     def handle_read(self):
257         chunk = self.socket.recv(BUFFER_LEN)
258         if not chunk:
259             self.close()
260
261     def handle_close(self):
262         self.close()
263
264     def handle_error(self):
265         raise
266
267 class AsyncWriter(asynchat.async_chat):
268     """Just write dummy data to a connected socket, asynchronously."""
269     class ChunkProducer:
270         def __init__(self, size):
271             self.size = size
272             self.sent = 0
273             self.chunk = 'x' * BUFFER_LEN
274
275         def more(self):
276             if self.sent >= self.size:
277                 return ''
278             self.sent += len(self.chunk)
279             return self.chunk
280
281     def __init__(self, sock, size):
282         asynchat.async_chat.__init__(self, sock)
283         self.push_with_producer(self.ChunkProducer(size))
284         self.close_when_done()
285
286     def handle_error(self):
287         raise
288
289 class AsyncQuit(asynchat.async_chat):
290
291     def __init__(self, sock):
292         asynchat.async_chat.__init__(self, sock)
293         self.in_buffer = []
294         self.set_terminator('\r\n')
295         self.push('QUIT\r\n')
296
297     def collect_incoming_data(self, data):
298         self.in_buffer.append(data)
299
300     def found_terminator(self):
301         self.handle_close()
302
303     def handle_error(self):
304         raise
305
306 class OptFormatter(optparse.IndentedHelpFormatter):
307
308     def format_epilog(self, s):
309         return s.lstrip()
310
311     def format_option(self, option):
312         result = []
313         opts = self.option_strings[option]
314         result.append('  %s\n' % opts)
315         if option.help:
316             help_text = '     %s\n\n' % self.expand_default(option)
317             result.append(help_text)
318         return ''.join(result)
319
320 def main():
321     global HOST, PORT, USER, PASSWORD
322     USAGE = "%s -u USERNAME -p PASSWORD [-H] [-P] [-b] [-n]" % __file__
323     parser = optparse.OptionParser(usage=USAGE,
324                                    epilog=__doc__[__doc__.find('Example'):],
325                                    formatter=OptFormatter())
326     parser.add_option('-u', '--user', dest='user', help='username')
327     parser.add_option('-p', '--pass', dest='password', help='password')
328     parser.add_option('-H', '--host', dest='host', default=HOST, help='hostname')
329     parser.add_option('-P', '--port', dest='port', default=PORT, help='port')
330     parser.add_option('-b', '--benchmark', dest='benchmark', default='transfer',
331                       help="benchmark type ('transfer', 'concurrence', 'all')")
332     parser.add_option('-n', '--clients', dest='clients', default=200, type="int",
333                       help="number of concurrent clients used by 'concurrence' "
334                            "benchmark")
335     options, args = parser.parse_args()
336     if not options.user or not options.password:
337         sys.exit(USAGE)
338     else:
339         USER = options.user
340         PASSWORD = options.password
341         HOST = options.host
342         PORT = options.port
343
344     def bench_stor():
345         bytes = bytes_per_second(connect(), retr=False)
346         print_bench("STOR (client -> server)",
347                     round(bytes / 1024.0 / 1024.0, 2), "MB/sec")
348
349     def bench_retr():
350         bytes = bytes_per_second(connect(), retr=True)
351         print_bench("RETR (server -> client)",
352                     round(bytes / 1024.0 / 1024.0, 2), "MB/sec")
353
354     def bench_multi():
355         howmany = options.clients
356         FILE_SIZE = 1024 * 1024 * 10  # 10MB
357
358         def bench_multi_connect():
359             with timethis("%i concurrent clients (connect, login)" % howmany):
360                 clients = []
361                 for x in range(howmany):
362                     clients.append(connect())
363             return clients
364
365         def bench_multi_retr(clients):
366             stor(clients[0], FILE_SIZE)
367             for ftp in clients:
368                 ftp.voidcmd('TYPE I')
369                 conn = ftp.transfercmd("RETR " + TESTFN)
370                 AsyncReader(conn)
371             with timethis("%s concurrent clients (RETR %s file)" \
372                           % (howmany, bytes2human(FILE_SIZE))):
373                 asyncore.loop(use_poll=True)
374             for ftp in clients:
375                 ftp.voidresp()
376
377         def bench_multi_stor(clients):
378             for ftp in clients:
379                 ftp.voidcmd('TYPE I')
380                 conn = ftp.transfercmd("STOR " + TESTFN)
381                 AsyncWriter(conn, 1024 * 1024 * 5)
382             with timethis("%s concurrent clients (STOR %s file)" \
383                           % (howmany, bytes2human(FILE_SIZE))):
384                 asyncore.loop(use_poll=True)
385             for ftp in clients:
386                 ftp.voidresp()
387
388         def bench_multi_quit(clients):
389             for ftp in clients:
390                 AsyncQuit(ftp.sock)
391             with timethis("%i concurrent clients (quit)" % howmany):
392                 asyncore.loop(use_poll=True)
393
394         clients = bench_multi_connect()
395         bench_multi_retr(clients)
396         bench_multi_stor(clients)
397         bench_multi_quit(clients)
398
399     # before starting make sure we have write permissions
400     ftp = connect()
401     conn = ftp.transfercmd("STOR " + TESTFN)
402     conn.close()
403     ftp.voidresp()
404     ftp.delete(TESTFN)
405     ftp.quit()
406     atexit.register(cleanup)
407
408     # start benchmark
409     if options.benchmark == 'transfer':
410         bench_stor()
411         bench_retr()
412     elif options.benchmark == 'concurrence':
413         bench_multi()
414     elif options.benchmark == 'all':
415         bench_stor()
416         bench_retr()
417         bench_multi()
418     else:
419         sys.exit("invalid 'benchmark' parameter %r" % options.benchmark)
420
421 if __name__ == '__main__':
422     main()