• 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 import java.util.Collection;
26 import java.util.Collections;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.concurrent.ConcurrentHashMap;
30 import java.util.concurrent.ConcurrentSkipListMap;
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 {
44       @Override
create(Collection<Element> keys)45       Map<Element, Element> create(Collection<Element> keys) {
46         Map<Element, Element> map = Maps.newHashMap();
47         for (Element element : keys) {
48           map.put(element, element);
49         }
50         return map;
51       }
52     },
53     LinkedHM {
54       @Override
create(Collection<Element> keys)55       Map<Element, Element> create(Collection<Element> keys) {
56         Map<Element, Element> map = Maps.newLinkedHashMap();
57         for (Element element : keys) {
58           map.put(element, element);
59         }
60         return map;
61       }
62     },
63     UnmodHM {
64       @Override
create(Collection<Element> keys)65       Map<Element, Element> create(Collection<Element> keys) {
66         return Collections.unmodifiableMap(Hash.create(keys));
67       }
68     },
69     SyncHM {
70       @Override
create(Collection<Element> keys)71       Map<Element, Element> create(Collection<Element> keys) {
72         return Collections.synchronizedMap(Hash.create(keys));
73       }
74     },
75     Tree {
76       @Override
create(Collection<Element> keys)77       Map<Element, Element> create(Collection<Element> keys) {
78         Map<Element, Element> map = Maps.newTreeMap();
79         for (Element element : keys) {
80           map.put(element, element);
81         }
82         return map;
83       }
84     },
85     SkipList {
86       @Override
create(Collection<Element> keys)87       Map<Element, Element> create(Collection<Element> keys) {
88         Map<Element, Element> map = new ConcurrentSkipListMap<>();
89         for (Element element : keys) {
90           map.put(element, element);
91         }
92         return map;
93       }
94     },
95     ConcurrentHM1 {
96       @Override
create(Collection<Element> keys)97       Map<Element, Element> create(Collection<Element> keys) {
98         Map<Element, Element> map = new ConcurrentHashMap<>(keys.size(), 0.75f, 1);
99         for (Element element : keys) {
100           map.put(element, element);
101         }
102         return map;
103       }
104     },
105     ConcurrentHM16 {
106       @Override
create(Collection<Element> keys)107       Map<Element, Element> create(Collection<Element> keys) {
108         Map<Element, Element> map = new ConcurrentHashMap<>(keys.size(), 0.75f, 16);
109         for (Element element : keys) {
110           map.put(element, element);
111         }
112         return map;
113       }
114     },
115     MapMaker1 {
116       @Override
create(Collection<Element> keys)117       Map<Element, Element> create(Collection<Element> keys) {
118         Map<Element, Element> map = new MapMaker().concurrencyLevel(1).makeMap();
119         for (Element element : keys) {
120           map.put(element, element);
121         }
122         return map;
123       }
124     },
125     MapMaker16 {
126       @Override
create(Collection<Element> keys)127       Map<Element, Element> create(Collection<Element> keys) {
128         Map<Element, Element> map = new MapMaker().concurrencyLevel(16).makeMap();
129         for (Element element : keys) {
130           map.put(element, element);
131         }
132         return map;
133       }
134     },
135     Immutable {
136       @Override
create(Collection<Element> keys)137       Map<Element, Element> create(Collection<Element> keys) {
138         ImmutableMap.Builder<Element, Element> builder = ImmutableMap.builder();
139         for (Element element : keys) {
140           builder.put(element, element);
141         }
142         return builder.buildOrThrow();
143       }
144     },
145     ImmutableSorted {
146       @Override
create(Collection<Element> keys)147       Map<Element, Element> create(Collection<Element> keys) {
148         ImmutableSortedMap.Builder<Element, Element> builder = ImmutableSortedMap.naturalOrder();
149         for (Element element : keys) {
150           builder.put(element, element);
151         }
152         return builder.build();
153       }
154     };
155 
create(Collection<Element> contents)156     abstract Map<Element, Element> create(Collection<Element> contents);
157   }
158 
159   @Param({"5", "50", "500", "5000", "50000"})
160   private int size;
161 
162   // TODO: look at exact (==) hits vs. equals() hits?
163   @Param("0.9")
164   private double hitRate;
165 
166   @Param("true")
167   private boolean isUserTypeFast;
168 
169   // "" means no fixed seed
170   @Param("")
171   private SpecialRandom random;
172 
173   @Param("false")
174   private boolean sortedData;
175 
176   // the following must be set during setUp
177   private Element[] queries;
178   private Map<Element, Element> mapToTest;
179 
180   private Collection<Element> values;
181 
182   @BeforeExperiment
setUp()183   void setUp() {
184     CollectionBenchmarkSampleData sampleData =
185         new CollectionBenchmarkSampleData(isUserTypeFast, random, hitRate, size);
186 
187     if (sortedData) {
188       List<Element> valueList = newArrayList(sampleData.getValuesInSet());
189       Collections.sort(valueList);
190       values = valueList;
191     } else {
192       values = sampleData.getValuesInSet();
193     }
194     this.mapToTest = impl.create(values);
195     this.queries = sampleData.getQueries();
196   }
197 
198   @Benchmark
get(int reps)199   boolean get(int reps) {
200     // Paranoia: acting on hearsay that accessing fields might be slow
201     // Should write a benchmark to test that!
202     Map<Element, Element> map = mapToTest;
203     Element[] queries = this.queries;
204 
205     // Allows us to use & instead of %, acting on hearsay that division
206     // operators (/%) are disproportionately expensive; should test this too!
207     int mask = queries.length - 1;
208 
209     boolean dummy = false;
210     for (int i = 0; i < reps; i++) {
211       dummy ^= map.get(queries[i & mask]) != null;
212     }
213     return dummy;
214   }
215 
216   @Benchmark
createAndPopulate(int reps)217   int createAndPopulate(int reps) {
218     int dummy = 0;
219     for (int i = 0; i < reps; i++) {
220       dummy += impl.create(values).size();
221     }
222     return dummy;
223   }
224 
225   @Benchmark
createPopulateAndRemove(int reps)226   boolean createPopulateAndRemove(int reps) {
227     boolean dummy = false;
228     for (int i = 1; i < reps; i++) {
229       Map<Element, Element> map = impl.create(values);
230       for (Element value : values) {
231         dummy |= map.remove(value) == null;
232       }
233     }
234     return dummy;
235   }
236 
237   @Benchmark
iterateWithEntrySet(int reps)238   boolean iterateWithEntrySet(int reps) {
239     Map<Element, Element> map = mapToTest;
240 
241     boolean dummy = false;
242     for (int i = 0; i < reps; i++) {
243       for (Map.Entry<Element, Element> entry : map.entrySet()) {
244         dummy ^= entry.getKey() != entry.getValue();
245       }
246     }
247     return dummy;
248   }
249 
250   @Benchmark
iterateWithKeySetAndGet(int reps)251   boolean iterateWithKeySetAndGet(int reps) {
252     Map<Element, Element> map = mapToTest;
253 
254     boolean dummy = false;
255     for (int i = 0; i < reps; i++) {
256       for (Element key : map.keySet()) {
257         Element value = map.get(key);
258         dummy ^= key != value;
259       }
260     }
261     return dummy;
262   }
263 
264   @Benchmark
iterateValuesAndGet(int reps)265   boolean iterateValuesAndGet(int reps) {
266     Map<Element, Element> map = mapToTest;
267 
268     boolean dummy = false;
269     for (int i = 0; i < reps; i++) {
270       for (Element key : map.values()) {
271         // This normally wouldn't make sense, but because our keys are our values it kind of does
272         Element value = map.get(key);
273         dummy ^= key != value;
274       }
275     }
276     return dummy;
277   }
278 }
279