ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/test/loops/MapMicroBenchmark.java
Revision: 1.1
Committed: Wed Jul 1 11:24:49 2009 UTC (14 years, 10 months ago) by dl
Branch: MAIN
Log Message:
Initial checkin

File Contents

# Content
1 /*
2 * Written by Doug Lea with assistance from members of JCP JSR-166
3 * Expert Group and released to the public domain, as explained at
4 * http://creativecommons.org/licenses/publicdomain
5 */
6
7 import java.util.*;
8 import java.math.*;
9
10 /**
11 * A micro-benchmark with key types and operation mixes roughly
12 * corresponding to some real programs.
13 *
14 * The main results are a table of approximate nanoseconds per
15 * element-operation (averaged across get, put etc) for each type,
16 * across a range of map sizes.
17 *
18 * The program includes a bunch of microbenchmarking safeguards that
19 * might underestimate typical performance. For example, by using many
20 * different key types and exercising them in warmups it disables most
21 * dynamic type specialization. Some test classes, like Float and
22 * BigDecimal are included not because they are commonly used as keys,
23 * but because they can be problematic for some map implementations.
24 *
25 * By default, it creates and inserts in order dense numerical keys
26 * and searches for keys in scrambled order. Use "r" as second arg to
27 * instead use random numerical values, and "s" as third arg to search
28 * in insertion order.
29 */
30 public class MapMicroBenchmark {
31 static Class mapClass;
32 static boolean randomSearches = true;
33 static boolean randomKeys = false;
34
35 static final long NANOS_PER_JOB = 8L * 1000L*1000L*1000L;
36
37 // map operations per item per iteration -- change if job.work changed
38 static final int OPS_PER_ITER = 11;
39 static final int MIN_ITERS_PER_TEST = 2;
40 static final int MAX_ITERS_PER_TEST = 1000000;
41
42 // sizes are at halfway points for HashMap default resizes
43 static final int firstSize = 36;
44 static final int sizeStep = 4; // each size 4X last
45 static final int nsizes = 8;
46 static final int[] sizes = new int[nsizes];
47
48 static final class Missing {} // class for guaranteed non-matches
49 static final Object MISSING = new Missing();
50
51 static Map newMap() {
52 try {
53 return (Map)mapClass.newInstance();
54 } catch(Exception e) {
55 throw new RuntimeException("Can't instantiate " + mapClass + ": " + e);
56 }
57 }
58
59 public static void main(String[] args) throws Throwable {
60 if (args.length == 0) {
61 System.out.println("Usage: java MapMicroBenchmark className [r|s]keys [r|s]searches");
62 return;
63 }
64
65 mapClass = Class.forName(args[0]);
66
67 if (args.length > 1) {
68 if (args[1].startsWith("s"))
69 randomKeys = false;
70 else if (args[1].startsWith("r"))
71 randomKeys = true;
72 }
73 if (args.length > 2) {
74 if (args[2].startsWith("s"))
75 randomSearches = false;
76 else if (args[2].startsWith("r"))
77 randomSearches = true;
78 }
79
80 System.out.print("Class " + mapClass.getName());
81 if (randomKeys)
82 System.out.print(" random keys");
83 else
84 System.out.print(" sequential keys");
85 if (randomSearches)
86 System.out.print(" randomized searches");
87 else
88 System.out.print(" sequential searches");
89
90 System.out.println();
91
92 int n = firstSize;
93 for (int i = 0; i < nsizes - 1; ++i) {
94 sizes[i] = n;
95 n *= sizeStep;
96 }
97 sizes[nsizes - 1] = n;
98
99 Object[] ss = new Object[n];
100 Object[] os = new Object[n];
101 Object[] is = new Object[n];
102 Object[] ls = new Object[n];
103 Object[] fs = new Object[n];
104 Object[] ds = new Object[n];
105 Object[] bs = new Object[n];
106 Object[] es = new Object[n];
107
108 // To guarantee uniqueness, use xorshift for "random" versions
109 int j = randomKeys? 1234567 : 0;
110 for (int i = 0; i < n; i++) {
111 ss[i] = String.valueOf(j);
112 os[i] = new Object();
113 is[i] = Integer.valueOf(j);
114 ls[i] = Long.valueOf((long)j);
115 fs[i] = Float.valueOf((float)i); // can't use random for float
116 ds[i] = Double.valueOf((double)j);
117 bs[i] = BigInteger.valueOf(j);
118 es[i] = BigDecimal.valueOf(j);
119 j = randomKeys? xorshift(j) : j + 1;
120 }
121
122 List<Job> list = new ArrayList<Job>();
123 list.add(new Job("BigDecimal", es));
124 list.add(new Job("BigInteger", bs));
125 list.add(new Job("String ", ss));
126 list.add(new Job("Double ", ds));
127 list.add(new Job("Float ", fs));
128 list.add(new Job("Long ", ls));
129 list.add(new Job("Integer ", is));
130 list.add(new Job("Object ", os));
131
132 Job[] jobs = list.toArray(new Job[0]);
133 warmup(jobs);
134 warmup(jobs);
135 time(jobs);
136 }
137
138 static void runWork(Job[] jobs, int maxIters) throws Throwable {
139 for (int k = 0; k < nsizes; ++k) {
140 int len = sizes[k];
141 for (int i = 0; i < jobs.length; i++) {
142 jobs[i].setup(len);
143 Thread.sleep(50);
144 jobs[i].nanos[k] = jobs[i].work(len, maxIters);
145 System.out.print(".");
146 }
147 }
148 System.out.println();
149 }
150
151 static void warmup(Job[] jobs) throws Throwable {
152 System.out.print("warm up");
153 runWork(jobs, MIN_ITERS_PER_TEST);
154 long ck = jobs[0].checkSum;
155 for (int i = 1; i < jobs.length; i++) {
156 if (jobs[i].checkSum != ck)
157 throw new Error("CheckSum");
158 }
159 }
160
161 static void time(Job[] jobs) throws Throwable {
162 System.out.print("running");
163 runWork(jobs, MAX_ITERS_PER_TEST);
164
165 System.out.print("Type/Size:");
166 for (int k = 0; k < nsizes; ++k)
167 System.out.printf("%8d", sizes[k]);
168 System.out.println();
169
170 long[] aves = new long[nsizes];
171 int njobs = jobs.length;
172
173 for (int i = 0; i < njobs; i++) {
174 System.out.print(jobs[i].name);
175 for (int k = 0; k < nsizes; ++k) {
176 long nanos = jobs[i].nanos[k];
177 System.out.printf("%8d", nanos);
178 aves[k] += nanos;
179 }
180 System.out.println();
181 }
182
183 System.out.println();
184 System.out.print("average ");
185 for (int k = 0; k < nsizes; ++k)
186 System.out.printf("%8d", (aves[k] / njobs));
187 System.out.println();
188 }
189
190
191 static final class Job {
192 final String name;
193 long[] nanos = new long[nsizes];
194 final Object[] items;
195 final Object[] dups;
196 Object[] reordered;
197 volatile long checkSum;
198 volatile int lastSum;
199 Job(String name, Object[] items) {
200 this.name = name;
201 this.items = items;
202 if (randomSearches)
203 this.dups = new Object[items.length];
204 else
205 this.dups = items;
206 }
207
208 public void setup(int len) {
209 if (randomSearches) {
210 System.arraycopy(items, 0, dups, 0, len);
211 shuffle(dups, len);
212 reordered = dups;
213 }
214 else
215 reordered = items;
216 }
217
218 public long work(int len, int maxIters) {
219 Object[] ins = items;
220 Object[] keys = reordered;
221
222 if (ins.length < len || keys.length < len)
223 throw new Error(name);
224 int half = len / 2;
225 Class eclass = ins[0].getClass();
226 int sum = lastSum;
227 long startTime = System.nanoTime();
228 long elapsed;
229 Map m = newMap();
230 int j = 0;
231 for (;;) {
232 for (int i = 0; i < half; ++i) {
233 Object x = ins[i];
234 if (m.put(x, x) == null)
235 ++sum;
236 }
237 checkSum += sum ^ (sum << 1); // help avoid loop merging
238 for (int i = 0; i < len; ++i) {
239 Object x = keys[i];
240 Object v = m.get(x);
241 if (v != null && v.getClass() == eclass) // touch v
242 sum += 2;
243 }
244 checkSum += sum ^ (sum << 2);
245 for (int i = half; i < len; ++i) {
246 Object x = ins[i];
247 if (m.put(x, x) == null)
248 ++sum;
249 }
250 checkSum += sum ^ (sum << 3);
251 for (Object e : m.keySet()) {
252 if (e.getClass() == eclass)
253 ++sum;
254 }
255 checkSum += sum ^ (sum << 4);
256 for (Object e : m.values()) {
257 if (e.getClass() == eclass)
258 ++sum;
259 }
260 checkSum += sum ^ (sum << 5);
261 for (int i = len - 1; i >= 0; --i) {
262 Object x = keys[i];
263 Object v = m.get(x);
264 if (v != null && v.getClass() == eclass)
265 ++sum;
266 }
267 checkSum += sum ^ (sum << 6);
268 for (int i = 0; i < len; ++i) {
269 Object x = ins[i];
270 Object v = m.get(x);
271 if (v != null && v.getClass() == eclass)
272 ++sum;
273 }
274 checkSum += sum ^ (sum << 7);
275 for (int i = 0; i < len; ++i) {
276 Object x = keys[i];
277 Object v = ins[i];
278 if (m.put(x, v) == x)
279 ++sum;
280 }
281 checkSum += sum ^ (sum << 8);
282 for (int i = 0; i < len; ++i) {
283 Object x = keys[i];
284 Object v = ins[i];
285 if (v.equals(m.get(x)))
286 ++sum;
287 }
288 checkSum += sum ^ (sum << 9);
289 for (int i = len - 1; i >= 0; --i) {
290 Object x = ins[i];
291 if (m.get(x) != MISSING)
292 ++sum;
293 }
294 checkSum += sum ^ (sum << 10);
295 for (int i = len - 1; i >= 0; --i) {
296 Object x = keys[i];
297 Object v = ins[i];
298 if (v.equals(m.get(x)))
299 ++sum;
300 }
301 checkSum += sum ^ (sum << 11);
302 if ((j & 1) == 1) { // lower remove rate
303 for (int i = 0; i < len; ++i) {
304 Object x = keys[i];
305 if (m.remove(x) != null)
306 ++sum;
307 }
308 }
309 else {
310 m.clear();
311 sum += len;
312 }
313 checkSum += sum ^ (sum << 12);
314
315 elapsed = System.nanoTime() - startTime;
316 ++j;
317 if (j >= MIN_ITERS_PER_TEST &&
318 (j >= maxIters || elapsed >= NANOS_PER_JOB))
319 break;
320 }
321 long ops = ((long)j) * len * OPS_PER_ITER;
322 if (sum != lastSum + (int)ops)
323 throw new Error(name);
324 lastSum = sum;
325 return elapsed / ops;
326 }
327
328 }
329
330 static final int xorshift(int seed) {
331 seed ^= seed << 1;
332 seed ^= seed >>> 3;
333 seed ^= seed << 10;
334 return seed;
335 }
336
337
338 static final Random rng = new Random(3152688);
339
340 static void shuffle(Object[] a, int size) {
341 for (int i= size; i>1; i--) {
342 Object t = a[i-1];
343 int r = rng.nextInt(i);
344 a[i-1] = a[r];
345 a[r] = t;
346 }
347 }
348
349 }
350