ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/test/loops/MapMicroBenchmark.java
Revision: 1.15
Committed: Thu Dec 18 18:43:22 2014 UTC (9 years, 5 months ago) by jsr166
Branch: MAIN
Changes since 1.14: +3 -3 lines
Log Message:
fix some [rawtypes] warnings

File Contents

# User Rev Content
1 dl 1.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 jsr166 1.10 * http://creativecommons.org/publicdomain/zero/1.0/
5 dl 1.1 */
6    
7     import java.util.*;
8 dl 1.3 import java.io.*;
9 dl 1.1 import java.math.*;
10    
11     /**
12     * A micro-benchmark with key types and operation mixes roughly
13     * corresponding to some real programs.
14 jsr166 1.4 *
15 dl 1.1 * The main results are a table of approximate nanoseconds per
16     * element-operation (averaged across get, put etc) for each type,
17 dl 1.2 * across a range of map sizes. It also includes category "Mixed"
18     * that includes elements of multiple types including those with
19     * identical hash codes.
20 dl 1.1 *
21     * The program includes a bunch of microbenchmarking safeguards that
22     * might underestimate typical performance. For example, by using many
23     * different key types and exercising them in warmups it disables most
24     * dynamic type specialization. Some test classes, like Float and
25     * BigDecimal are included not because they are commonly used as keys,
26     * but because they can be problematic for some map implementations.
27 jsr166 1.4 *
28 dl 1.1 * By default, it creates and inserts in order dense numerical keys
29 dl 1.3 * and searches for keys in scrambled order. Use "s" as second arg to
30     * instead insert and search in unscrambled order.
31     *
32     * For String keys, the program tries to use file "testwords.txt", which
33     * is best used with real words. We can't check in this file, but you
34 jsr166 1.5 * can create one from a real dictionary (1 line per word) and then run
35 dl 1.3 * linux "shuf" to randomize entries. If no file exists, it uses
36     * String.valueOf(i) for element i.
37 dl 1.1 */
38     public class MapMicroBenchmark {
39 dl 1.3 static final String wordFile = "testwords.txt";
40    
41 jsr166 1.15 static Class<?> mapClass;
42 dl 1.1 static boolean randomSearches = true;
43    
44 dl 1.2 // Nanoseconds per run
45     static final long NANOS_PER_JOB = 6L * 1000L*1000L*1000L;
46     static final long NANOS_PER_WARMUP = 100L*1000L*1000L;
47 dl 1.1
48     // map operations per item per iteration -- change if job.work changed
49     static final int OPS_PER_ITER = 11;
50 dl 1.3 static final int MIN_ITERS_PER_TEST = 3; // must be > 1
51 dl 1.2 static final int MAX_ITERS_PER_TEST = 1000000; // avoid runaway
52 dl 1.1
53     // sizes are at halfway points for HashMap default resizes
54 dl 1.2 static final int firstSize = 9;
55 dl 1.1 static final int sizeStep = 4; // each size 4X last
56 dl 1.2 static final int nsizes = 9;
57 dl 1.1 static final int[] sizes = new int[nsizes];
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 jsr166 1.4
65 dl 1.1 mapClass = Class.forName(args[0]);
66    
67     if (args.length > 1) {
68     if (args[1].startsWith("s"))
69 dl 1.3 randomSearches = false;
70 dl 1.1 else if (args[1].startsWith("r"))
71     randomSearches = true;
72     }
73    
74     System.out.print("Class " + mapClass.getName());
75     if (randomSearches)
76     System.out.print(" randomized searches");
77     else
78     System.out.print(" sequential searches");
79    
80     System.out.println();
81    
82     int n = firstSize;
83     for (int i = 0; i < nsizes - 1; ++i) {
84     sizes[i] = n;
85     n *= sizeStep;
86     }
87     sizes[nsizes - 1] = n;
88    
89 dl 1.3 int njobs = 10;
90     Job[] jobs = new Job[njobs];
91 dl 1.2
92     Object[] os = new Object[n];
93 dl 1.3 for (int i = 0; i < n; i++) os[i] = new Object();
94     jobs[0] = new Job("Object ", os, Object.class);
95    
96 dl 1.1 Object[] ss = new Object[n];
97 dl 1.3 initStringKeys(ss, n);
98     jobs[1] = new Job("String ", ss, String.class);
99    
100 dl 1.1 Object[] is = new Object[n];
101 dl 1.3 for (int i = 0; i < n; i++) is[i] = Integer.valueOf(i);
102     jobs[2] = new Job("Integer ", is, Integer.class);
103    
104 dl 1.1 Object[] ls = new Object[n];
105 jsr166 1.8 for (int i = 0; i < n; i++) ls[i] = Long.valueOf((long) i);
106 dl 1.3 jobs[3] = new Job("Long ", ls, Long.class);
107    
108 dl 1.1 Object[] fs = new Object[n];
109 jsr166 1.8 for (int i = 0; i < n; i++) fs[i] = Float.valueOf((float) i);
110 dl 1.3 jobs[4] = new Job("Float ", fs, Float.class);
111    
112 dl 1.1 Object[] ds = new Object[n];
113 jsr166 1.8 for (int i = 0; i < n; i++) ds[i] = Double.valueOf((double) i);
114 dl 1.3 jobs[5] = new Job("Double ", ds, Double.class);
115    
116 dl 1.1 Object[] bs = new Object[n];
117 dl 1.3 long b = -n; // include some negatives
118     for (int i = 0; i < n; i++) bs[i] = BigInteger.valueOf(b += 3);
119     jobs[6] = new Job("BigInteger", bs, BigInteger.class);
120    
121 dl 1.1 Object[] es = new Object[n];
122 dl 1.3 long d = Integer.MAX_VALUE; // include crummy codes
123     for (int i = 0; i < n; i++) es[i] = BigDecimal.valueOf(d += 65536);
124     jobs[7] = new Job("BigDecimal", es, BigDecimal.class);
125 dl 1.2
126 dl 1.3 Object[] rs = new Object[n];
127     for (int i = 0; i < n; i++) rs[i] = new RandomInt();
128     jobs[8] = new Job("RandomInt ", rs, RandomInt.class);
129 dl 1.1
130 dl 1.3 Object[] ms = new Object[n];
131     for (int i = 0; i < n; i += 2) {
132     int r = rng.nextInt(njobs - 1);
133     ms[i] = jobs[r].items[i];
134     // include some that will have same hash but not .equal
135     if (++r >= njobs - 1) r = 0;
136     ms[i+1] = jobs[r].items[i];
137 dl 1.2 }
138 dl 1.3 jobs[9] = new Job("Mixed ", ms, Object.class);
139     Job mixed = jobs[9];
140 dl 1.2
141 dl 1.3 warmup1(mixed);
142 dl 1.2 warmup2(jobs);
143 dl 1.3 warmup1(mixed);
144 dl 1.2 warmup3(jobs);
145     Thread.sleep(500);
146 jsr166 1.9 time(jobs);
147 dl 1.1 }
148    
149 dl 1.2 static void runWork(Job[] jobs, int minIters, int maxIters, long timeLimit) throws Throwable {
150 jsr166 1.12 for (int k = 0; k < nsizes; ++k) {
151 dl 1.1 int len = sizes[k];
152     for (int i = 0; i < jobs.length; i++) {
153     Thread.sleep(50);
154 dl 1.2 jobs[i].nanos[k] = jobs[i].work(len, minIters, maxIters, timeLimit);
155 dl 1.1 System.out.print(".");
156     }
157     }
158     System.out.println();
159     }
160    
161 dl 1.2 // First warmup -- run only mixed job to discourage type specialization
162     static void warmup1(Job job) throws Throwable {
163 jsr166 1.12 for (int k = 0; k < nsizes; ++k)
164 dl 1.2 job.work(sizes[k], 1, 1, 0);
165     }
166    
167     // Second, run each once
168     static void warmup2(Job[] jobs) throws Throwable {
169 dl 1.1 System.out.print("warm up");
170 dl 1.2 runWork(jobs, 1, 1, 0);
171 dl 1.1 long ck = jobs[0].checkSum;
172 dl 1.2 for (int i = 1; i < jobs.length - 1; i++) {
173 dl 1.1 if (jobs[i].checkSum != ck)
174     throw new Error("CheckSum");
175     }
176     }
177    
178 dl 1.2 // Third: short timed runs
179     static void warmup3(Job[] jobs) throws Throwable {
180     System.out.print("warm up");
181     runWork(jobs, 1, MAX_ITERS_PER_TEST, NANOS_PER_WARMUP);
182     }
183    
184 dl 1.1 static void time(Job[] jobs) throws Throwable {
185     System.out.print("running");
186 dl 1.2 runWork(jobs, MIN_ITERS_PER_TEST, MAX_ITERS_PER_TEST, NANOS_PER_JOB);
187 dl 1.1
188     System.out.print("Type/Size:");
189     for (int k = 0; k < nsizes; ++k)
190 dl 1.2 System.out.printf("%7d", sizes[k]);
191 dl 1.1 System.out.println();
192    
193     long[] aves = new long[nsizes];
194     int njobs = jobs.length;
195    
196 jsr166 1.9 for (int i = 0; i < njobs; i++) {
197 dl 1.1 System.out.print(jobs[i].name);
198     for (int k = 0; k < nsizes; ++k) {
199     long nanos = jobs[i].nanos[k];
200 dl 1.2 System.out.printf("%7d", nanos);
201 dl 1.1 aves[k] += nanos;
202     }
203     System.out.println();
204     }
205    
206     System.out.println();
207     System.out.print("average ");
208     for (int k = 0; k < nsizes; ++k)
209 dl 1.2 System.out.printf("%7d", (aves[k] / njobs));
210     System.out.println("\n");
211 dl 1.1 }
212    
213    
214     static final class Job {
215 jsr166 1.9 final String name;
216 jsr166 1.15 final Class<?> elementClass;
217 dl 1.1 long[] nanos = new long[nsizes];
218     final Object[] items;
219 dl 1.2 Object[] searches;
220 dl 1.1 volatile long checkSum;
221     volatile int lastSum;
222 jsr166 1.15 Job(String name, Object[] items, Class<?> elementClass) {
223 dl 1.1 this.name = name;
224     this.items = items;
225 dl 1.2 this.elementClass = elementClass;
226 dl 1.1 if (randomSearches) {
227 dl 1.2 scramble(items);
228     this.searches = new Object[items.length];
229     System.arraycopy(items, 0, searches, 0, items.length);
230     scramble(searches);
231 dl 1.1 }
232     else
233 dl 1.2 this.searches = items;
234 dl 1.1 }
235    
236 dl 1.2 public long work(int len, int minIters, int maxIters, long timeLimit) {
237     Map m;
238     try {
239 jsr166 1.6 m = (Map) mapClass.newInstance();
240     } catch (Exception e) {
241 dl 1.2 throw new RuntimeException("Can't instantiate " + mapClass + ": " + e);
242     }
243 dl 1.1 Object[] ins = items;
244 dl 1.2 Object[] keys = searches;
245 dl 1.1
246     if (ins.length < len || keys.length < len)
247     throw new Error(name);
248     int half = len / 2;
249 dl 1.2 int quarter = half / 2;
250 dl 1.1 int sum = lastSum;
251     long startTime = System.nanoTime();
252     long elapsed;
253     int j = 0;
254     for (;;) {
255     for (int i = 0; i < half; ++i) {
256     Object x = ins[i];
257     if (m.put(x, x) == null)
258     ++sum;
259     }
260     checkSum += sum ^ (sum << 1); // help avoid loop merging
261 dl 1.2 sum += len - half;
262 dl 1.1 for (int i = 0; i < len; ++i) {
263     Object x = keys[i];
264 jsr166 1.4 Object v = m.get(x);
265 dl 1.2 if (elementClass.isInstance(v)) // touch v
266     ++sum;
267 dl 1.1 }
268     checkSum += sum ^ (sum << 2);
269     for (int i = half; i < len; ++i) {
270     Object x = ins[i];
271     if (m.put(x, x) == null)
272     ++sum;
273     }
274     checkSum += sum ^ (sum << 3);
275     for (Object e : m.keySet()) {
276 jsr166 1.4 if (elementClass.isInstance(e))
277 dl 1.1 ++sum;
278     }
279     checkSum += sum ^ (sum << 4);
280     for (Object e : m.values()) {
281 jsr166 1.4 if (elementClass.isInstance(e))
282 dl 1.1 ++sum;
283     }
284     checkSum += sum ^ (sum << 5);
285     for (int i = len - 1; i >= 0; --i) {
286     Object x = keys[i];
287     Object v = m.get(x);
288 dl 1.2 if (elementClass.isInstance(v))
289 dl 1.1 ++sum;
290     }
291     checkSum += sum ^ (sum << 6);
292     for (int i = 0; i < len; ++i) {
293     Object x = ins[i];
294     Object v = m.get(x);
295 dl 1.2 if (elementClass.isInstance(v))
296 dl 1.1 ++sum;
297     }
298     checkSum += sum ^ (sum << 7);
299     for (int i = 0; i < len; ++i) {
300     Object x = keys[i];
301     Object v = ins[i];
302     if (m.put(x, v) == x)
303     ++sum;
304     }
305     checkSum += sum ^ (sum << 8);
306     for (int i = 0; i < len; ++i) {
307     Object x = keys[i];
308     Object v = ins[i];
309 dl 1.2 if (v == m.get(x))
310 dl 1.1 ++sum;
311     }
312     checkSum += sum ^ (sum << 9);
313     for (int i = len - 1; i >= 0; --i) {
314     Object x = ins[i];
315 dl 1.2 Object v = m.get(x);
316     if (elementClass.isInstance(v))
317 dl 1.1 ++sum;
318     }
319     checkSum += sum ^ (sum << 10);
320     for (int i = len - 1; i >= 0; --i) {
321     Object x = keys[i];
322     Object v = ins[i];
323 dl 1.2 if (v == m.get(x))
324 dl 1.1 ++sum;
325     }
326     checkSum += sum ^ (sum << 11);
327 dl 1.2 for (int i = 0; i < quarter; ++i) {
328     Object x = keys[i];
329     if (m.remove(x) != null)
330     ++sum;
331 dl 1.1 }
332 dl 1.3 for (int i = 0; i < quarter; ++i) {
333     Object x = keys[i];
334     if (m.put(x, x) == null)
335     ++sum;
336     }
337 dl 1.11 /* // uncomment to avoid calling clear()
338     for (int i = 0; i < len; ++i) {
339     Object x = keys[i];
340     m.remove(x);
341     }
342     */
343 dl 1.2 m.clear();
344 dl 1.3 sum += len - (quarter * 2);
345 dl 1.1 checkSum += sum ^ (sum << 12);
346    
347 dl 1.3 if (j == 0 && sum != lastSum + len * OPS_PER_ITER)
348     throw new Error(name);
349 jsr166 1.4
350 dl 1.1 elapsed = System.nanoTime() - startTime;
351     ++j;
352 dl 1.2 if (j >= minIters &&
353     (j >= maxIters || elapsed >= timeLimit))
354 dl 1.1 break;
355 dl 1.3 // non-warmup - swap some keys for next insert
356 jsr166 1.4 if (minIters != 1 && randomSearches)
357 dl 1.3 shuffleSome(ins, len, len >>> 3);
358 dl 1.1 }
359 jsr166 1.8 long ops = ((long) j) * len * OPS_PER_ITER;
360 dl 1.1 lastSum = sum;
361     return elapsed / ops;
362     }
363    
364     }
365    
366    
367 dl 1.2 static final Random rng = new Random(3122688);
368    
369     // Shuffle the subarrays for each size. This doesn't fully
370     // randomize, but the remaining partial locality is arguably a bit
371     // more realistic
372     static void scramble(Object[] a) {
373     for (int k = 0; k < sizes.length; ++k) {
374 jsr166 1.6 int origin = (k == 0) ? 0 : sizes[k-1];
375 dl 1.2 for (int i = sizes[k]; i > origin + 1; i--) {
376     Object t = a[i-1];
377     int r = rng.nextInt(i - origin) + origin;
378     a[i-1] = a[r];
379     a[r] = t;
380     }
381     }
382 jsr166 1.4 }
383 dl 1.1
384 dl 1.2 // plain array shuffle
385 dl 1.1 static void shuffle(Object[] a, int size) {
386 jsr166 1.13 for (int i = size; i > 1; i--) {
387 dl 1.1 Object t = a[i-1];
388     int r = rng.nextInt(i);
389     a[i-1] = a[r];
390     a[r] = t;
391     }
392     }
393    
394 dl 1.3 // swap nswaps elements
395     static void shuffleSome(Object[] a, int size, int nswaps) {
396     for (int s = 0; s < nswaps; ++s) {
397     int i = rng.nextInt(size);
398     int r = rng.nextInt(size);
399     Object t = a[i];
400     a[i] = a[r];
401     a[r] = t;
402     }
403     }
404    
405     // Integer-like class with random hash codes
406     static final class RandomInt {
407     static int seed = 3122688;
408     static int next() { // a non-xorshift, 2^32-period RNG
409     int x = seed;
410     int lo = 16807 * (x & 0xFFFF);
411     int hi = 16807 * (x >>> 16);
412     lo += (hi & 0x7FFF) << 16;
413     if ((lo & 0x80000000) != 0) {
414     lo &= 0x7fffffff;
415     ++lo;
416     }
417     lo += hi >>> 15;
418     if (lo == 0 || (lo & 0x80000000) != 0) {
419     lo &= 0x7fffffff;
420     ++lo;
421     }
422     seed = lo;
423     return x;
424     }
425     final int value;
426     RandomInt() { value = next(); }
427     public int hashCode() { return value; }
428     public boolean equals(Object x) {
429     return (x instanceof RandomInt) && ((RandomInt)x).value == value;
430     }
431     }
432 dl 1.2
433 dl 1.3 // Read in String keys from file if possible
434 jsr166 1.4 static void initStringKeys(Object[] keys, int n) throws Exception {
435 dl 1.3 FileInputStream fr = null;
436     try {
437     fr = new FileInputStream(wordFile);
438     } catch (IOException ex) {
439     System.out.println("No word file. Using String.valueOf(i)");
440     for (int i = 0; i < n; i++)
441     keys[i] = String.valueOf(i);
442     return;
443     }
444    
445     BufferedInputStream in = new BufferedInputStream(fr);
446     int k = 0;
447 jsr166 1.14 outer: while (k < n) {
448 dl 1.3 StringBuffer sb = new StringBuffer();
449     for (;;) {
450     int c = in.read();
451 jsr166 1.4 if (c < 0)
452 dl 1.3 break outer;
453 jsr166 1.8 char ch = (char) c;
454 dl 1.3 if (ch == '\n') {
455     keys[k++] = sb.toString();
456     break;
457     }
458     if (!Character.isWhitespace(ch))
459     sb.append(ch);
460     }
461     }
462     in.close();
463    
464     // fill up remaining keys with path-like compounds of previous pairs
465     int j = 0;
466     while (k < n)
467 jsr166 1.7 keys[k++] = (String) keys[j++] + "/" + (String) keys[j];
468 dl 1.3 }
469 dl 1.2
470 dl 1.1 }