• 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 import java.util.concurrent.ConcurrentSkipListMap;
32 
33 /**
34  * A microbenchmark that tests the performance of get() and iteration on various map
35  * implementations.  Forked from {@link SetContainsBenchmark}.
36  *
37  * @author Nicholaus Shupe
38  */
39 public class MapBenchmark {
40   @Param({"Hash", "LinkedHM", "MapMaker1", "Immutable"})
41   private Impl impl;
42 
43   public enum Impl {
44     Hash {
create(Collection<Element> keys)45       @Override 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 {
create(Collection<Element> keys)54       @Override Map<Element, Element> create(Collection<Element> keys) {
55         Map<Element, Element> map = Maps.newLinkedHashMap();
56         for (Element element: keys) {
57           map.put(element, element);
58         }
59         return map;
60       }
61     },
62     UnmodHM {
create(Collection<Element> keys)63       @Override Map<Element, Element> create(Collection<Element> keys) {
64         return Collections.unmodifiableMap(Hash.create(keys));
65       }
66     },
67     SyncHM {
create(Collection<Element> keys)68       @Override Map<Element, Element> create(Collection<Element> keys) {
69         return Collections.synchronizedMap(Hash.create(keys));
70       }
71     },
72     Tree {
create(Collection<Element> keys)73       @Override Map<Element, Element> create(Collection<Element> keys) {
74         Map<Element, Element> map = Maps.newTreeMap();
75         for (Element element: keys) {
76           map.put(element, element);
77         }
78         return map;
79       }
80     },
81     SkipList {
create(Collection<Element> keys)82       @Override Map<Element, Element> create(Collection<Element> keys) {
83         Map<Element, Element> map = new ConcurrentSkipListMap<Element, Element>();
84         for (Element element: keys) {
85           map.put(element, element);
86         }
87         return map;
88       }
89     },
90     ConcurrentHM1 {
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, 1);
94         for (Element element: keys) {
95           map.put(element, element);
96         }
97         return map;
98       }
99     },
100     ConcurrentHM16 {
create(Collection<Element> keys)101       @Override Map<Element, Element> create(Collection<Element> keys) {
102         Map<Element, Element> map =
103             new ConcurrentHashMap<Element, Element>(keys.size(), 0.75f, 16);
104         for (Element element: keys) {
105           map.put(element, element);
106         }
107         return map;
108       }
109     },
110     MapMaker1 {
create(Collection<Element> keys)111       @Override Map<Element, Element> create(Collection<Element> keys) {
112         Map<Element, Element> map = new MapMaker()
113             .concurrencyLevel(1)
114             .makeMap();
115         for (Element element: keys) {
116           map.put(element, element);
117         }
118         return map;
119       }
120     },
121     MapMaker16 {
create(Collection<Element> keys)122       @Override Map<Element, Element> create(Collection<Element> keys) {
123         Map<Element, Element> map = new MapMaker()
124             .concurrencyLevel(16)
125             .makeMap();
126         for (Element element: keys) {
127           map.put(element, element);
128         }
129         return map;
130       }
131     },
132     Immutable {
create(Collection<Element> keys)133       @Override Map<Element, Element> create(Collection<Element> keys) {
134         ImmutableMap.Builder<Element, Element> builder = ImmutableMap.builder();
135         for (Element element : keys) {
136           builder.put(element, element);
137         }
138         return builder.build();
139       }
140     },
141     ImmutableSorted {
create(Collection<Element> keys)142       @Override Map<Element, Element> create(Collection<Element> keys) {
143         ImmutableSortedMap.Builder<Element, Element> builder =
144             ImmutableSortedMap.naturalOrder();
145         for (Element element : keys) {
146           builder.put(element, element);
147         }
148         return builder.build();
149       }
150     };
151 
create(Collection<Element> contents)152     abstract Map<Element, Element> create(Collection<Element> contents);
153   }
154 
155   @Param({"5", "50", "500", "5000", "50000"})
156   private int size;
157 
158   // TODO: look at exact (==) hits vs. equals() hits?
159   @Param("0.9")
160   private double hitRate;
161 
162   @Param("true")
163   private boolean isUserTypeFast;
164 
165   // "" means no fixed seed
166   @Param("")
167   private SpecialRandom random;
168 
169   @Param("false")
170   private boolean sortedData;
171 
172   // the following must be set during setUp
173   private Element[] queries;
174   private Map<Element, Element> mapToTest;
175 
176   private Collection<Element> values;
177 
setUp()178   @BeforeExperiment void setUp() {
179     CollectionBenchmarkSampleData sampleData =
180         new CollectionBenchmarkSampleData(
181             isUserTypeFast, random, hitRate, size);
182 
183     if (sortedData) {
184       List<Element> valueList = newArrayList(sampleData.getValuesInSet());
185       Collections.sort(valueList);
186       values = valueList;
187     } else {
188       values = sampleData.getValuesInSet();
189     }
190     this.mapToTest = impl.create(values);
191     this.queries = sampleData.getQueries();
192   }
193 
get(int reps)194   @Benchmark boolean get(int reps) {
195     // Paranoia: acting on hearsay that accessing fields might be slow
196     // Should write a benchmark to test that!
197     Map<Element, Element> map = mapToTest;
198     Element[] queries = this.queries;
199 
200     // Allows us to use & instead of %, acting on hearsay that division
201     // operators (/%) are disproportionately expensive; should test this too!
202     int mask = queries.length - 1;
203 
204     boolean dummy = false;
205     for (int i = 0; i < reps; i++) {
206       dummy ^= map.get(queries[i & mask]) != null;
207     }
208     return dummy;
209   }
210 
createAndPopulate(int reps)211   @Benchmark int createAndPopulate(int reps) {
212     int dummy = 0;
213     for (int i = 0; i < reps; i++) {
214       dummy += impl.create(values).size();
215     }
216     return dummy;
217   }
218 
iterateWithEntrySet(int reps)219   @Benchmark boolean iterateWithEntrySet(int reps) {
220     Map<Element, Element> map = mapToTest;
221 
222     boolean dummy = false;
223     for (int i = 0; i < reps; i++) {
224       for (Map.Entry<Element, Element> entry : map.entrySet()) {
225         dummy ^= entry.getKey() != entry.getValue();
226       }
227     }
228     return dummy;
229   }
230 
iterateWithKeySetAndGet(int reps)231   @Benchmark boolean iterateWithKeySetAndGet(int reps) {
232     Map<Element, Element> map = mapToTest;
233 
234     boolean dummy = false;
235     for (int i = 0; i < reps; i++) {
236       for (Element key : map.keySet()) {
237         Element value = map.get(key);
238         dummy ^= key != value;
239       }
240     }
241     return dummy;
242 
243   }
244 }
245