Add proper per-file copyright notices/licenses and top-level license.
[bluesky.git] / microbench / workload.py
1 #!/usr/bin/python
2 #
3 # A simple benchmark for Blue Sky that will read and/or write a collection of
4 # files with a specified working set size, and measure the response time to do
5 # so.
6
7 import json, math, os, random, sys, threading, time
8
9 THREADS = 1
10
11 def percentile(rank, elements):
12     """Return the value at a fraction rank from the beginning of the list."""
13
14     # Convert a fraction [0.0, 1.0] to a fractional index in elements.
15     rank = rank * (len(elements) - 1)
16
17     # Round the index value to the nearest integer values, then interpolate.
18     prev = int(math.floor(rank))
19     next = int(math.ceil(rank))
20     frac = rank - prev
21
22     return (1.0 - frac) * elements[prev] + frac * elements[next]
23
24 class WorkerThread:
25     """Performs a mix of file system operations and records the performance."""
26
27     PARAMS = ['duration', 'write_fraction', 'wss_count', 'tot_count',
28               'filesize', 'target_ops']
29
30     def __init__(self):
31         self.stats = []
32         self.duration = 1800.0      # Seconds for which to run
33         self.write_fraction = 0.5   # Fraction of operations which are writes
34         self.wss_count = 2048       # Files in the working set
35         self.tot_count = 2048       # Total number of files created
36         self.filesize = 32 * 1024   # Size of files to work with
37         self.target_ops = 40        # Target operations/second/thread
38
39     def get_params(self):
40         params = {}
41         for p in self.PARAMS:
42             params[p] = getattr(self, p)
43         return params
44
45     def setup(self):
46         for i in range(self.tot_count):
47             filename = "file-%d" % (i,)
48             fp = open(filename, 'w')
49             fp.write('\0' * self.filesize)
50             fp.close()
51
52     def run(self):
53         stop_time = time.time() + self.duration
54
55         while True:
56             time1 = time.time()
57             if time1 >= stop_time: break
58             info = self._operation()
59             time2 = time.time()
60             self.stats.append((time1, time2 - time1, info))
61             #print self.stats[-1]
62             delay = time1 + (1.0 / self.target_ops) - time2
63             if delay > 0: time.sleep(delay)
64
65     def _operation(self):
66         """Run a single file system test (i.e., read or write a file)."""
67
68         filename = "file-%d" % (random.randrange(self.wss_count),)
69
70         if random.uniform(0.0, 1.0) < self.write_fraction:
71             fp = open(filename, 'w')
72             fp.write('\0' * self.filesize)
73             fp.close()
74             return ('write', filename)
75         else:
76             fp = open(filename, 'r')
77             fp.read()
78             fp.close()
79             return ('read', filename)
80
81 def print_distribution_stats(stats):
82     stats = sorted(stats)
83     print "  Count:", len(stats)
84     if len(stats) == 0: return
85     print "  Average:", sum(stats) / len(stats)
86     for (s, p) in [("Min", 0.0), ("Med", 0.5), ("90%", 0.9),
87                    ("95%", 0.95), ("Max", 1.0)]:
88         print "  %s: %s" % (s, percentile(p, stats))
89
90 def run_stats(stats):
91     duration = max(x[0] for x in stats) - min(x[0] for x in stats)
92     latencies = [x[1] for x in stats]
93     latencies.sort()
94     print "Experiment duration:", duration
95     print "READS:"
96     print_distribution_stats([x[1] for x in stats if x[2][0] == 'read'])
97     print "WRITES:"
98     print_distribution_stats([x[1] for x in stats if x[2][0] == 'write'])
99
100 fp = open('/tmp/results.json', 'a')
101
102 def run(filecount, writefrac, filesize):
103     workers = []
104     threads = []
105     for i in range(THREADS):
106         w = WorkerThread()
107         w.write_fraction = writefrac
108         w.wss_count = w.tot_count = filecount
109         w.filesize = filesize
110         if i == 0: w.setup()
111         t = threading.Thread(target=w.run)
112         threads.append(t)
113         workers.append(w)
114         t.start()
115
116     print json.dumps(workers[0].get_params(), indent=2)
117
118     for t in threads:
119         t.join()
120
121     results = []
122     for w in workers:
123         results += w.stats
124     results.sort()
125
126     fp.write(json.dumps(workers[0].get_params(), indent=2) + "\n\n")
127     fp.write(json.dumps(results, indent=2))
128     fp.write("\n\n")
129     run_stats(results)
130
131 if __name__ == '__main__':
132     for filesize in [32, 256, 2048]:            # KiB
133         for totsize in [256, 512, 1024]:        # MiB
134             filecount = totsize * 1024 / filesize
135             for writefrac in [0.0, 0.5]:
136                 run(filecount, writefrac, filesize * 1024)
137     fp.close()