3 # Copyright (C) 2010 The Regents of the University of California
4 # Written by Michael Vrable <mvrable@cs.ucsd.edu>
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
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.
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
30 # Run a series of simple test requests against S3 for gathering some basic
31 # performance numbers.
34 from boto.s3.connection import SubdomainCallingFormat
35 from boto.s3.key import Key
36 import sys, threading, time, Queue
39 BUCKET_NAME = 'mvrable-benchmark'
40 SIZES = [(1 << s) for s in range(12, 23)]
42 class S3TestConnection:
44 self.conn = boto.connect_s3(is_secure=False,
45 calling_format=SubdomainCallingFormat())
46 self.bucket = self.conn.get_bucket(BUCKET_NAME)
48 def put_object(self, name, 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)
55 def get_object(self, name, byterange=None):
56 k = Key(self.bucket, name)
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))
65 def parallel_get(name, connections, delay1=0.0):
66 #print "Get: %s x %d" % (name, len(connections))
69 def launcher(c, name, result_queue):
70 result_queue.put(c.get_object(name))
71 for i in range(len(connections)):
73 threads.append(threading.Thread(target=launcher, args=(c, name, q)))
74 for i in range(len(threads)):
76 for t in threads: t.join()
81 if len(res) == len(connections):
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]))
92 def launcher(c, requests):
95 (n, r) = requests.get(block=False)
96 # Possible data race here but it should be harmless
100 if r[1] is None: r[1] = time.time()
105 for i in range(len(connections)):
107 threads.append(threading.Thread(target=launcher, args=(c, requests)))
108 start_time = time.time()
109 for i in range(len(threads)):
113 return max(x[1] for x in results) - start_time
115 def run_test(size, threads, num, logfile=sys.stdout, delay=1.0):
116 connections = [S3TestConnection() for _ in range(threads)]
119 res = parallel_get('file-%d-%d' % (size, i), connections)
121 logfile.write(str(min(res)) + "\n")
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
137 connection = S3TestConnection()
138 logfile = open('multifetch-simulation.data', 'a')
141 for i in range(RANGE):
142 name = 'file-%d-%d' % (s, i)
143 for r in TESTRANGES[i * len(TESTRANGES) // RANGE]:
146 if abs(x) < 1: x = int(x * s)
147 if abs(y) < 1: x = int(y * s)
150 t = connection.get_object(name, r)
151 print "%s %s: %s" % (name, r or "", t)