Add proper per-file copyright notices/licenses and top-level license.
[bluesky.git] / cloudbench / rangetest.py
1 #!/usr/bin/python
2 #
3 # Copyright (C) 2010  The Regents of the University of California
4 # Written by Michael Vrable <mvrable@cs.ucsd.edu>
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 # 1. Redistributions of source code must retain the above copyright
10 #    notice, this list of conditions and the following disclaimer.
11 # 2. Redistributions in binary form must reproduce the above copyright
12 #    notice, this list of conditions and the following disclaimer in the
13 #    documentation and/or other materials provided with the distribution.
14 # 3. Neither the name of the University nor the names of its contributors
15 #    may be used to endorse or promote products derived from this software
16 #    without specific prior written permission.
17 #
18 # THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
19 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 # ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 # SUCH DAMAGE.
29
30 # Run a series of simple test requests against S3 for gathering some basic
31 # performance numbers.
32
33 import boto, time
34 from boto.s3.connection import SubdomainCallingFormat
35 from boto.s3.key import Key
36 import sys, threading, time, Queue
37 import azure
38
39 BUCKET_NAME = 'mvrable-benchmark'
40 SIZES = [(1 << s) for s in range(12, 23)]
41
42 class S3TestConnection:
43     def __init__(self):
44         self.conn = boto.connect_s3(is_secure=False,
45                                     calling_format=SubdomainCallingFormat())
46         self.bucket = self.conn.get_bucket(BUCKET_NAME)
47
48     def put_object(self, name, size):
49         buf = 'A' * size
50         k = Key(self.bucket, name)
51         start_time = time.time()
52         k.set_contents_from_string(buf)
53         #print "%s: %f" % (name, time.time() - start_time)
54
55     def get_object(self, name, byterange=None):
56         k = Key(self.bucket, name)
57         headers = {}
58         if byterange is not None:
59             headers['Range'] = 'bytes=%s-%s' % byterange
60         start_time = time.time()
61         buf = k.get_contents_as_string(headers=headers)
62         duration = time.time() - start_time
63         return (duration, len(buf))
64
65 def parallel_get(name, connections, delay1=0.0):
66     #print "Get: %s x %d" % (name, len(connections))
67     threads = []
68     q = Queue.Queue()
69     def launcher(c, name, result_queue):
70         result_queue.put(c.get_object(name))
71     for i in range(len(connections)):
72         c = connections[i]
73         threads.append(threading.Thread(target=launcher, args=(c, name, q)))
74     for i in range(len(threads)):
75         threads[i].start()
76     for t in threads: t.join()
77     res = []
78     while not q.empty():
79         res.append(q.get())
80
81     if len(res) == len(connections):
82         return res
83
84 def parallel_multiget(names, connections, repeat=1):
85     requests = Queue.Queue()
86     results = [[threading.Lock(), None] for n in names]
87     for i in range(len(names)):
88         for _ in range(repeat):
89             requests.put((names[i], results[i]))
90
91     threads = []
92     def launcher(c, requests):
93         while True:
94             try:
95                 (n, r) = requests.get(block=False)
96                 # Possible data race here but it should be harmless
97                 if r[1] is None:
98                     res = c.get_object(n)
99                     r[0].acquire()
100                     if r[1] is None: r[1] = time.time()
101                     r[0].release()
102                 requests.task_done()
103             except Queue.Empty:
104                 return
105     for i in range(len(connections)):
106         c = connections[i]
107         threads.append(threading.Thread(target=launcher, args=(c, requests)))
108     start_time = time.time()
109     for i in range(len(threads)):
110         threads[i].start()
111     requests.join()
112
113     return max(x[1] for x in results) - start_time
114
115 def run_test(size, threads, num, logfile=sys.stdout, delay=1.0):
116     connections = [S3TestConnection() for _ in range(threads)]
117     for i in range(num):
118         print "    ...test", i
119         res = parallel_get('file-%d-%d' % (size, i), connections)
120         if res is not None:
121             logfile.write(str(min(res)) + "\n")
122         if delay > 0:
123             time.sleep(delay)
124
125 # Ranges are specified as a start and a length.  Fractional values are
126 # multiplied by the total size of the object.  Negative start values measure
127 # from the end of the object.
128 TESTRANGES = [[None, None],                 # Cold read, then hot read
129               [None, (0, 256)],             # Cold read, hot partial read
130               [(0, 256), None],             # Cold partial, hot full
131               [(-256, 256), None],          # Cold partial end, hot full
132               [(0, 256), (0, 256)],         # Repeated range
133               [(0, 256), (256, 256)],       # Consecutive ranges
134               [(0, 256), (-256, 256)],      # Discontiguous ranges
135               [(-256, 256), (0, 256)]]      # Discontiguous ranges
136
137 connection = S3TestConnection()
138 logfile = open('multifetch-simulation.data', 'a')
139 RANGE = 100
140 for s in SIZES:
141     for i in range(RANGE):
142         name = 'file-%d-%d' % (s, i)
143         for r in TESTRANGES[i * len(TESTRANGES) // RANGE]:
144             if r is not None:
145                 (x, y) = r
146                 if abs(x) < 1: x = int(x * s)
147                 if abs(y) < 1: x = int(y * s)
148                 if x < 0: x += s
149                 r = (x, x + y - 1)
150             t = connection.get_object(name, r)
151             print "%s %s: %s" % (name, r or "", t)
152
153 sys.exit(0)