]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/blob - src/allmydata/test/check_speed.py
8be4ca07f32228e4d4b1979b1cbba85475e594c8
[tahoe-lafs/tahoe-lafs.git] / src / allmydata / test / check_speed.py
1 #! /usr/bin/env python
2
3 import os, sys
4 from twisted.internet import reactor, defer
5 from twisted.python import log
6 from twisted.application import service
7 from foolscap.api import Tub, fireEventually
8
9 MB = 1000000
10
11 class SpeedTest:
12     DO_IMMUTABLE = True
13     DO_MUTABLE_CREATE = True
14     DO_MUTABLE = True
15
16     def __init__(self, test_client_dir):
17         #self.real_stderr = sys.stderr
18         log.startLogging(open("st.log", "a"), setStdout=False)
19         f = open(os.path.join(test_client_dir, "private", "control.furl"), "r")
20         self.control_furl = f.read().strip()
21         f.close()
22         self.base_service = service.MultiService()
23         self.failed = None
24         self.upload_times = {}
25         self.download_times = {}
26
27     def run(self):
28         print "STARTING"
29         d = fireEventually()
30         d.addCallback(lambda res: self.setUp())
31         d.addCallback(lambda res: self.do_test())
32         d.addBoth(self.tearDown)
33         def _err(err):
34             self.failed = err
35             log.err(err)
36             print err
37         d.addErrback(_err)
38         def _done(res):
39             reactor.stop()
40             return res
41         d.addBoth(_done)
42         reactor.run()
43         if self.failed:
44             print "EXCEPTION"
45             print self.failed
46             sys.exit(1)
47
48     def setUp(self):
49         self.base_service.startService()
50         self.tub = Tub()
51         self.tub.setOption("expose-remote-exception-types", False)
52         self.tub.setServiceParent(self.base_service)
53         d = self.tub.getReference(self.control_furl)
54         def _gotref(rref):
55             self.client_rref = rref
56             print "Got Client Control reference"
57             return self.stall(5)
58         d.addCallback(_gotref)
59         return d
60
61     def stall(self, delay, result=None):
62         d = defer.Deferred()
63         reactor.callLater(delay, d.callback, result)
64         return d
65
66     def record_times(self, times, key):
67         print "TIME (%s): %s up, %s down" % (key, times[0], times[1])
68         self.upload_times[key], self.download_times[key] = times
69
70     def one_test(self, res, name, count, size, mutable):
71         # values for 'mutable':
72         #   False (upload a different CHK file for each 'count')
73         #   "create" (upload different contents into a new SSK file)
74         #   "upload" (upload different contents into the same SSK file. The
75         #             time consumed does not include the creation of the file)
76         d = self.client_rref.callRemote("speed_test", count, size, mutable)
77         d.addCallback(self.record_times, name)
78         return d
79
80     def measure_rtt(self, res):
81         # use RIClient.get_nodeid() to measure the foolscap-level RTT
82         d = self.client_rref.callRemote("measure_peer_response_time")
83         def _got(res):
84             assert len(res) # need at least one peer
85             times = res.values()
86             self.total_rtt = sum(times)
87             self.average_rtt = sum(times) / len(times)
88             self.max_rtt = max(times)
89             print "num-peers: %d" % len(times)
90             print "total-RTT: %f" % self.total_rtt
91             print "average-RTT: %f" % self.average_rtt
92             print "max-RTT: %f" % self.max_rtt
93         d.addCallback(_got)
94         return d
95
96     def do_test(self):
97         print "doing test"
98         d = defer.succeed(None)
99         d.addCallback(self.one_test, "startup", 1, 1000, False) #ignore this one
100         d.addCallback(self.measure_rtt)
101
102         if self.DO_IMMUTABLE:
103             # immutable files
104             d.addCallback(self.one_test, "1x 200B", 1, 200, False)
105             d.addCallback(self.one_test, "10x 200B", 10, 200, False)
106             def _maybe_do_100x_200B(res):
107                 if self.upload_times["10x 200B"] < 5:
108                     print "10x 200B test went too fast, doing 100x 200B test"
109                     return self.one_test(None, "100x 200B", 100, 200, False)
110                 return
111             d.addCallback(_maybe_do_100x_200B)
112             d.addCallback(self.one_test, "1MB", 1, 1*MB, False)
113             d.addCallback(self.one_test, "10MB", 1, 10*MB, False)
114             def _maybe_do_100MB(res):
115                 if self.upload_times["10MB"] > 30:
116                     print "10MB test took too long, skipping 100MB test"
117                     return
118                 return self.one_test(None, "100MB", 1, 100*MB, False)
119             d.addCallback(_maybe_do_100MB)
120
121         if self.DO_MUTABLE_CREATE:
122             # mutable file creation
123             d.addCallback(self.one_test, "10x 200B SSK creation", 10, 200,
124                           "create")
125
126         if self.DO_MUTABLE:
127             # mutable file upload/download
128             d.addCallback(self.one_test, "10x 200B SSK", 10, 200, "upload")
129             def _maybe_do_100x_200B_SSK(res):
130                 if self.upload_times["10x 200B SSK"] < 5:
131                     print "10x 200B SSK test went too fast, doing 100x 200B SSK"
132                     return self.one_test(None, "100x 200B SSK", 100, 200,
133                                          "upload")
134                 return
135             d.addCallback(_maybe_do_100x_200B_SSK)
136             d.addCallback(self.one_test, "1MB SSK", 1, 1*MB, "upload")
137
138         d.addCallback(self.calculate_speeds)
139         return d
140
141     def calculate_speeds(self, res):
142         # time = A*size+B
143         # we assume that A*200bytes is negligible
144
145         if self.DO_IMMUTABLE:
146             # upload
147             if "100x 200B" in self.upload_times:
148                 B = self.upload_times["100x 200B"] / 100
149             else:
150                 B = self.upload_times["10x 200B"] / 10
151             print "upload per-file time: %.3fs" % B
152             print "upload per-file times-avg-RTT: %f" % (B / self.average_rtt)
153             print "upload per-file times-total-RTT: %f" % (B / self.total_rtt)
154             A1 = 1*MB / (self.upload_times["1MB"] - B) # in bytes per second
155             print "upload speed (1MB):", self.number(A1, "Bps")
156             A2 = 10*MB / (self.upload_times["10MB"] - B)
157             print "upload speed (10MB):", self.number(A2, "Bps")
158             if "100MB" in self.upload_times:
159                 A3 = 100*MB / (self.upload_times["100MB"] - B)
160                 print "upload speed (100MB):", self.number(A3, "Bps")
161
162             # download
163             if "100x 200B" in self.download_times:
164                 B = self.download_times["100x 200B"] / 100
165             else:
166                 B = self.download_times["10x 200B"] / 10
167             print "download per-file time: %.3fs" % B
168             print "download per-file times-avg-RTT: %f" % (B / self.average_rtt)
169             print "download per-file times-total-RTT: %f" % (B / self.total_rtt)
170             A1 = 1*MB / (self.download_times["1MB"] - B) # in bytes per second
171             print "download speed (1MB):", self.number(A1, "Bps")
172             A2 = 10*MB / (self.download_times["10MB"] - B)
173             print "download speed (10MB):", self.number(A2, "Bps")
174             if "100MB" in self.download_times:
175                 A3 = 100*MB / (self.download_times["100MB"] - B)
176                 print "download speed (100MB):", self.number(A3, "Bps")
177
178         if self.DO_MUTABLE_CREATE:
179             # SSK creation
180             B = self.upload_times["10x 200B SSK creation"] / 10
181             print "create per-file time SSK: %.3fs" % B
182
183         if self.DO_MUTABLE:
184             # upload SSK
185             if "100x 200B SSK" in self.upload_times:
186                 B = self.upload_times["100x 200B SSK"] / 100
187             else:
188                 B = self.upload_times["10x 200B SSK"] / 10
189             print "upload per-file time SSK: %.3fs" % B
190             A1 = 1*MB / (self.upload_times["1MB SSK"] - B) # in bytes per second
191             print "upload speed SSK (1MB):", self.number(A1, "Bps")
192
193             # download SSK
194             if "100x 200B SSK" in self.download_times:
195                 B = self.download_times["100x 200B SSK"] / 100
196             else:
197                 B = self.download_times["10x 200B SSK"] / 10
198             print "download per-file time SSK: %.3fs" % B
199             A1 = 1*MB / (self.download_times["1MB SSK"] - B) # in bytes per
200                                                              # second
201             print "download speed SSK (1MB):", self.number(A1, "Bps")
202
203     def number(self, value, suffix=""):
204         scaling = 1
205         if value < 1:
206             fmt = "%1.2g%s"
207         elif value < 100:
208             fmt = "%.1f%s"
209         elif value < 1000:
210             fmt = "%d%s"
211         elif value < 1e6:
212             fmt = "%.2fk%s"; scaling = 1e3
213         elif value < 1e9:
214             fmt = "%.2fM%s"; scaling = 1e6
215         elif value < 1e12:
216             fmt = "%.2fG%s"; scaling = 1e9
217         elif value < 1e15:
218             fmt = "%.2fT%s"; scaling = 1e12
219         elif value < 1e18:
220             fmt = "%.2fP%s"; scaling = 1e15
221         else:
222             fmt = "huge! %g%s"
223         return fmt % (value / scaling, suffix)
224
225     def tearDown(self, res):
226         d = self.base_service.stopService()
227         d.addCallback(lambda ignored: res)
228         return d
229
230
231 if __name__ == '__main__':
232     test_client_dir = sys.argv[1]
233     st = SpeedTest(test_client_dir)
234     st.run()