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