• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 The Guava Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.common.collect;
18 
19 import static com.google.common.collect.Lists.newArrayList;
20 
21 import com.google.caliper.BeforeExperiment;
22 import com.google.caliper.Benchmark;
23 import com.google.caliper.Param;
24 import com.google.common.collect.CollectionBenchmarkSampleData.Element;
25 
26 import java.util.Collection;
27 import java.util.Collections;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.concurrent.ConcurrentHashMap;
31 
32 /**
33  * A microbenchmark that tests the performance of get() and iteration on various map
34  * implementations.  Forked from {@link SetContainsBenchmark}.
35  *
36  * @author Nicholaus Shupe
37  */
38 public class MapBenchmark {
39   @Param({"Hash", "LinkedHM", "MapMaker1", "Immutable"})
40   private Impl impl;
41 
42   public enum Impl {
43     Hash {
create(Collection<Element> keys)44       @Override Map<Element, Element> create(Collection<Element> keys) {
45         Map<Element, Element> map = Maps.newHashMap();
46         for (Element element: keys) {
47           map.put(element, element);
48         }
49         return map;
50       }
51     },
52     LinkedHM {
create(Collection<Element> keys)53       @Override Map<Element, Element> create(Collection<Element> keys) {
54         Map<Element, Element> map = Maps.newLinkedHashMap();
55         for (Element element: keys) {
56           map.put(element, element);
57         }
58         return map;
59       }
60     },
61     UnmodHM {
create(Collection<Element> keys)62       @Override Map<Element, Element> create(Collection<Element> keys) {
63         return Collections.unmodifiableMap(Hash.create(keys));
64       }
65     },
66     SyncHM {
create(Collection<Element> keys)67       @Override Map<Element, Element> create(Collection<Element> keys) {
68         return Collections.synchronizedMap(Hash.create(keys));
69       }
70     },
71     Tree {
create(Collection<Element> keys)72       @Override Map<Element, Element> create(Collection<Element> keys) {
73         Map<Element, Element> map = Maps.newTreeMap();
74         for (Element element: keys) {
75           map.put(element, element);
76         }
77         return map;
78       }
79     },
80     ConcurrentHM1 {
create(Collection<Element> keys)81       @Override Map<Element, Element> create(Collection<Element> keys) {
82         Map<Element, Element> map =
83             new ConcurrentHashMap<Element, Element>(keys.size(), 0.75f, 1);
84         for (Element element: keys) {
85           map.put(element, element);
86         }
87         return map;
88       }
89     },
90     ConcurrentHM16 {
create(Collection<Element> keys)91       @Override Map<Element, Element> create(Collection<Element> keys) {
92         Map<Element, Element> map =
93             new ConcurrentHashMap<Element, Element>(keys.size(), 0.75f, 16);
94         for (Element element: keys) {
95           map.put(element, element);
96         }
97         return map;
98       }
99     },
100     MapMaker1 {
create(Collection<Element> keys)101       @Override Map<Element, Element> create(Collection<Element> keys) {
102         Map<Element, Element> map = new MapMaker()
103             .concurrencyLevel(1)
104             .makeMap();
105         for (Element element: keys) {
106           map.put(element, element);
107         }
108         return map;
109       }
110     },
111     MapMaker16 {
create(Collection<Element> keys)112       @Override Map<Element, Element> create(Collection<Element> keys) {
113         Map<Element, Element> map = new MapMaker()
114             .concurrencyLevel(16)
115             .makeMap();
116         for (Element element: keys) {
117           map.put(element, element);
118         }
119         return map;
120       }
121     },
122     Immutable {
create(Collection<Element> keys)123       @Override Map<Element, Element> create(Collection<Element> keys) {
124         ImmutableMap.Builder<Element, Element> builder = ImmutableMap.builder();
125         for (Element element : keys) {
126           builder.put(element, element);
127         }
128         return builder.build();
129       }
130     },
131     ImmutableSorted {
create(Collection<Element> keys)132       @Override Map<Element, Element> create(Collection<Element> keys) {
133         ImmutableSortedMap.Builder<Element, Element> builder =
134             ImmutableSortedMap.naturalOrder();
135         for (Element element : keys) {
136           builder.put(element, element);
137         }
138         return builder.build();
139       }
140     };
141 
create(Collection<Element> contents)142     abstract Map<Element, Element> create(Collection<Element> contents);
143   }
144 
145   @Param({"5", "50", "500", "5000", "50000"})
146   private int size;
147 
148   // TODO: look at exact (==) hits vs. equals() hits?
149   @Param("0.9")
150   private double hitRate;
151 
152   @Param("true")
153   private boolean isUserTypeFast;
154 
155   // "" means no fixed seed
156   @Param("")
157   private SpecialRandom random;
158 
159   @Param("false")
160   private boolean sortedData;
161 
162   // the following must be set during setUp
163   private Element[] queries;
164   private Map<Element, Element> mapToTest;
165 
166   private Collection<Element> values;
167 
setUp()168   @BeforeExperiment void setUp() {
169     CollectionBenchmarkSampleData sampleData =
170         new CollectionBenchmarkSampleData(
171             isUserTypeFast, random, hitRate, size);
172 
173     if (sortedData) {
174       List<Element> valueList = newArrayList(sampleData.getValuesInSet());
175       Collections.sort(valueList);
176       values = valueList;
177     } else {
178       values = sampleData.getValuesInSet();
179     }
180     this.mapToTest = impl.create(values);
181     this.queries = sampleData.getQueries();
182   }
183 
get(int reps)184   @Benchmark boolean get(int reps) {
185     // Paranoia: acting on hearsay that accessing fields might be slow
186     // Should write a benchmark to test that!
187     Map<Element, Element> map = mapToTest;
188     Element[] queries = this.queries;
189 
190     // Allows us to use & instead of %, acting on hearsay that division
191     // operators (/%) are disproportionately expensive; should test this too!
192     int mask = queries.length - 1;
193 
194     boolean dummy = false;
195     for (int i = 0; i < reps; i++) {
196       dummy ^= map.get(queries[i & mask]) != null;
197     }
198     return dummy;
199   }
200 
createAndPopulate(int reps)201   @Benchmark int createAndPopulate(int reps) {
202     int dummy = 0;
203     for (int i = 0; i < reps; i++) {
204       dummy += impl.create(values).size();
205     }
206     return dummy;
207   }
208 
iterateWithEntrySet(int reps)209   @Benchmark boolean iterateWithEntrySet(int reps) {
210     Map<Element, Element> map = mapToTest;
211 
212     boolean dummy = false;
213     for (int i = 0; i < reps; i++) {
214       for (Map.Entry<Element, Element> entry : map.entrySet()) {
215         dummy ^= entry.getKey() != entry.getValue();
216       }
217     }
218     return dummy;
219   }
220 
iterateWithKeySetAndGet(int reps)221   @Benchmark boolean iterateWithKeySetAndGet(int reps) {
222     Map<Element, Element> map = mapToTest;
223 
224     boolean dummy = false;
225     for (int i = 0; i < reps; i++) {
226       for (Element key : map.keySet()) {
227         Element value = map.get(key);
228         dummy ^= key != value;
229       }
230     }
231     return dummy;
232 
233   }
234 }
235