OLD | NEW |
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 #ifndef VM_HASH_TABLE_H_ | 5 #ifndef VM_HASH_TABLE_H_ |
6 #define VM_HASH_TABLE_H_ | 6 #define VM_HASH_TABLE_H_ |
7 | 7 |
8 // Temporarily used when sorting the indices in EnumIndexHashTable. | 8 // Temporarily used when sorting the indices in EnumIndexHashTable. |
9 // TODO(koda): Remove these dependencies before using in production. | 9 // TODO(koda): Remove these dependencies before using in production. |
10 #include <map> | 10 #include <map> |
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
130 intptr_t num_entries = num_occupied + 1; | 130 intptr_t num_entries = num_occupied + 1; |
131 return kFirstKeyIndex + (kEntrySize * num_entries); | 131 return kFirstKeyIndex + (kEntrySize * num_entries); |
132 } | 132 } |
133 | 133 |
134 // Initializes an empty table. | 134 // Initializes an empty table. |
135 void Initialize() const { | 135 void Initialize() const { |
136 ASSERT(data_->Length() >= ArrayLengthForNumOccupied(0)); | 136 ASSERT(data_->Length() >= ArrayLengthForNumOccupied(0)); |
137 smi_handle_ = Smi::New(0); | 137 smi_handle_ = Smi::New(0); |
138 data_->SetAt(kOccupiedEntriesIndex, smi_handle_); | 138 data_->SetAt(kOccupiedEntriesIndex, smi_handle_); |
139 data_->SetAt(kDeletedEntriesIndex, smi_handle_); | 139 data_->SetAt(kDeletedEntriesIndex, smi_handle_); |
| 140 |
| 141 NOT_IN_PRODUCT( |
| 142 data_->SetAt(kNumGrowsIndex, smi_handle_); |
| 143 data_->SetAt(kNumLT5LookupsIndex, smi_handle_); |
| 144 data_->SetAt(kNumLT25LookupsIndex, smi_handle_); |
| 145 data_->SetAt(kNumGT25LookupsIndex, smi_handle_); |
| 146 ) // !PRODUCT |
| 147 |
140 for (intptr_t i = kHeaderSize; i < data_->Length(); ++i) { | 148 for (intptr_t i = kHeaderSize; i < data_->Length(); ++i) { |
141 data_->SetAt(i, Object::sentinel()); | 149 data_->SetAt(i, Object::sentinel()); |
142 } | 150 } |
143 } | 151 } |
144 | 152 |
145 // Returns whether 'key' matches any key in the table. | 153 // Returns whether 'key' matches any key in the table. |
146 template<typename Key> | 154 template<typename Key> |
147 bool ContainsKey(const Key& key) const { | 155 bool ContainsKey(const Key& key) const { |
148 return FindKey(key) != -1; | 156 return FindKey(key) != -1; |
149 } | 157 } |
150 | 158 |
151 // Returns the entry that matches 'key', or -1 if none exists. | 159 // Returns the entry that matches 'key', or -1 if none exists. |
152 template<typename Key> | 160 template<typename Key> |
153 intptr_t FindKey(const Key& key) const { | 161 intptr_t FindKey(const Key& key) const { |
154 const intptr_t num_entries = NumEntries(); | 162 const intptr_t num_entries = NumEntries(); |
155 ASSERT(NumOccupied() < num_entries); | 163 ASSERT(NumOccupied() < num_entries); |
156 // TODO(koda): Add salt. | 164 // TODO(koda): Add salt. |
| 165 NOT_IN_PRODUCT(intptr_t collisions = 0;) |
157 uword hash = KeyTraits::Hash(key); | 166 uword hash = KeyTraits::Hash(key); |
158 intptr_t probe = hash % num_entries; | 167 intptr_t probe = hash % num_entries; |
159 // TODO(koda): Consider quadratic probing. | 168 // TODO(koda): Consider quadratic probing. |
160 while (true) { | 169 while (true) { |
161 if (IsUnused(probe)) { | 170 if (IsUnused(probe)) { |
| 171 NOT_IN_PRODUCT(UpdateCollisions(collisions);) |
162 return -1; | 172 return -1; |
163 } else if (!IsDeleted(probe)) { | 173 } else if (!IsDeleted(probe)) { |
164 key_handle_ = GetKey(probe); | 174 key_handle_ = GetKey(probe); |
165 if (KeyTraits::IsMatch(key, key_handle_)) { | 175 if (KeyTraits::IsMatch(key, key_handle_)) { |
| 176 NOT_IN_PRODUCT(UpdateCollisions(collisions);) |
166 return probe; | 177 return probe; |
167 } | 178 } |
| 179 NOT_IN_PRODUCT(collisions += 1;) |
168 } | 180 } |
169 // Advance probe. | 181 // Advance probe. |
170 probe++; | 182 probe++; |
171 probe = (probe == num_entries) ? 0 : probe; | 183 probe = (probe == num_entries) ? 0 : probe; |
172 } | 184 } |
173 UNREACHABLE(); | 185 UNREACHABLE(); |
174 return -1; | 186 return -1; |
175 } | 187 } |
176 | 188 |
177 // Sets *entry to either: | 189 // Sets *entry to either: |
178 // - an occupied entry matching 'key', and returns true, or | 190 // - an occupied entry matching 'key', and returns true, or |
179 // - an unused/deleted entry where a matching key may be inserted, | 191 // - an unused/deleted entry where a matching key may be inserted, |
180 // and returns false. | 192 // and returns false. |
181 template<typename Key> | 193 template<typename Key> |
182 bool FindKeyOrDeletedOrUnused(const Key& key, intptr_t* entry) const { | 194 bool FindKeyOrDeletedOrUnused(const Key& key, intptr_t* entry) const { |
183 const intptr_t num_entries = NumEntries(); | 195 const intptr_t num_entries = NumEntries(); |
184 ASSERT(entry != NULL); | 196 ASSERT(entry != NULL); |
185 ASSERT(NumOccupied() < num_entries); | 197 ASSERT(NumOccupied() < num_entries); |
| 198 NOT_IN_PRODUCT(intptr_t collisions = 0;) |
186 uword hash = KeyTraits::Hash(key); | 199 uword hash = KeyTraits::Hash(key); |
187 intptr_t probe = hash % num_entries; | 200 intptr_t probe = hash % num_entries; |
188 intptr_t deleted = -1; | 201 intptr_t deleted = -1; |
189 // TODO(koda): Consider quadratic probing. | 202 // TODO(koda): Consider quadratic probing. |
190 while (true) { | 203 while (true) { |
191 if (IsUnused(probe)) { | 204 if (IsUnused(probe)) { |
192 *entry = (deleted != -1) ? deleted : probe; | 205 *entry = (deleted != -1) ? deleted : probe; |
| 206 NOT_IN_PRODUCT(UpdateCollisions(collisions);) |
193 return false; | 207 return false; |
194 } else if (IsDeleted(probe)) { | 208 } else if (IsDeleted(probe)) { |
195 if (deleted == -1) { | 209 if (deleted == -1) { |
196 deleted = probe; | 210 deleted = probe; |
197 } | 211 } |
198 } else { | 212 } else { |
199 key_handle_ = GetKey(probe); | 213 key_handle_ = GetKey(probe); |
200 if (KeyTraits::IsMatch(key, key_handle_)) { | 214 if (KeyTraits::IsMatch(key, key_handle_)) { |
201 *entry = probe; | 215 *entry = probe; |
| 216 NOT_IN_PRODUCT(UpdateCollisions(collisions);) |
202 return true; | 217 return true; |
203 } | 218 } |
| 219 NOT_IN_PRODUCT(collisions += 1;) |
204 } | 220 } |
205 // Advance probe. | 221 // Advance probe. |
206 probe++; | 222 probe++; |
207 probe = (probe == num_entries) ? 0 : probe; | 223 probe = (probe == num_entries) ? 0 : probe; |
208 } | 224 } |
209 UNREACHABLE(); | 225 UNREACHABLE(); |
210 return false; | 226 return false; |
211 } | 227 } |
212 | 228 |
213 // Sets the key of a previously unoccupied entry. This must not be the last | 229 // Sets the key of a previously unoccupied entry. This must not be the last |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
272 intptr_t NumDeleted() const { | 288 intptr_t NumDeleted() const { |
273 return GetSmiValueAt(kDeletedEntriesIndex); | 289 return GetSmiValueAt(kDeletedEntriesIndex); |
274 } | 290 } |
275 Object& KeyHandle() const { | 291 Object& KeyHandle() const { |
276 return key_handle_; | 292 return key_handle_; |
277 } | 293 } |
278 Smi& SmiHandle() const { | 294 Smi& SmiHandle() const { |
279 return smi_handle_; | 295 return smi_handle_; |
280 } | 296 } |
281 | 297 |
| 298 NOT_IN_PRODUCT( |
| 299 intptr_t NumGrows() const { |
| 300 return GetSmiValueAt(kNumGrowsIndex); |
| 301 } |
| 302 intptr_t NumLT5Collisions() const { |
| 303 return GetSmiValueAt(kNumLT5LookupsIndex); |
| 304 } |
| 305 intptr_t NumLT25Collisions() const { |
| 306 return GetSmiValueAt(kNumLT25LookupsIndex); |
| 307 } |
| 308 intptr_t NumGT25Collisions() const { |
| 309 return GetSmiValueAt(kNumGT25LookupsIndex); |
| 310 } |
| 311 void UpdateGrowth() const { |
| 312 AdjustSmiValueAt(kNumGrowsIndex, 1); |
| 313 } |
| 314 void UpdateCollisions(intptr_t collisions) const { |
| 315 if (data_->raw()->IsVMHeapObject()) { |
| 316 return; |
| 317 } |
| 318 if (collisions < 5) { |
| 319 AdjustSmiValueAt(kNumLT5LookupsIndex, 1); |
| 320 } else if (collisions < 25) { |
| 321 AdjustSmiValueAt(kNumLT25LookupsIndex, 1); |
| 322 } else { |
| 323 AdjustSmiValueAt(kNumGT25LookupsIndex, 1); |
| 324 } |
| 325 } |
| 326 void PrintStats() const { |
| 327 if (!KeyTraits::ReportStats()) { |
| 328 return; |
| 329 } |
| 330 OS::Print("Stats for %s table :\n" |
| 331 " Size of table = %" Pd ",Number of Occupied entries = %" Pd "\n" |
| 332 " Number of Grows = %" Pd "\n" |
| 333 " Number of look ups with < 5 collisions = %" Pd "\n" |
| 334 " Number of look ups with < 25 collisions = %" Pd "\n" |
| 335 " Number of look ups with > 25 collisions = %" Pd "\n", |
| 336 KeyTraits::Name(), |
| 337 NumEntries(), NumOccupied(), |
| 338 NumGrows(), |
| 339 NumLT5Collisions(), NumLT25Collisions(), NumGT25Collisions()); |
| 340 } |
| 341 ) // !PRODUCT |
| 342 |
282 protected: | 343 protected: |
283 static const intptr_t kOccupiedEntriesIndex = 0; | 344 static const intptr_t kOccupiedEntriesIndex = 0; |
284 static const intptr_t kDeletedEntriesIndex = 1; | 345 static const intptr_t kDeletedEntriesIndex = 1; |
| 346 #if defined(PRODUCT) |
285 static const intptr_t kHeaderSize = kDeletedEntriesIndex + 1; | 347 static const intptr_t kHeaderSize = kDeletedEntriesIndex + 1; |
| 348 #else |
| 349 static const intptr_t kNumGrowsIndex = 2; |
| 350 static const intptr_t kNumLT5LookupsIndex = 3; |
| 351 static const intptr_t kNumLT25LookupsIndex = 4; |
| 352 static const intptr_t kNumGT25LookupsIndex = 5; |
| 353 static const intptr_t kHeaderSize = kNumGT25LookupsIndex + 1; |
| 354 #endif |
286 static const intptr_t kMetaDataIndex = kHeaderSize; | 355 static const intptr_t kMetaDataIndex = kHeaderSize; |
287 static const intptr_t kFirstKeyIndex = kHeaderSize + kMetaDataSize; | 356 static const intptr_t kFirstKeyIndex = kHeaderSize + kMetaDataSize; |
288 static const intptr_t kEntrySize = 1 + kPayloadSize; | 357 static const intptr_t kEntrySize = 1 + kPayloadSize; |
289 | 358 |
290 intptr_t KeyIndex(intptr_t entry) const { | 359 intptr_t KeyIndex(intptr_t entry) const { |
291 ASSERT(0 <= entry && entry < NumEntries()); | 360 ASSERT(0 <= entry && entry < NumEntries()); |
292 return kFirstKeyIndex + (kEntrySize * entry); | 361 return kFirstKeyIndex + (kEntrySize * entry); |
293 } | 362 } |
294 | 363 |
295 intptr_t PayloadIndex(intptr_t entry, intptr_t component) const { | 364 intptr_t PayloadIndex(intptr_t entry, intptr_t component) const { |
(...skipping 188 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
484 if (low <= current && current < high) { | 553 if (low <= current && current < high) { |
485 return; | 554 return; |
486 } | 555 } |
487 double target = (low + high) / 2.0; | 556 double target = (low + high) / 2.0; |
488 intptr_t new_capacity = (1 + table.NumOccupied()) / target; | 557 intptr_t new_capacity = (1 + table.NumOccupied()) / target; |
489 Table new_table(New<Table>( | 558 Table new_table(New<Table>( |
490 new_capacity, | 559 new_capacity, |
491 table.data_->IsOld() ? Heap::kOld : Heap::kNew)); | 560 table.data_->IsOld() ? Heap::kOld : Heap::kNew)); |
492 Copy(table, new_table); | 561 Copy(table, new_table); |
493 *table.data_ = new_table.Release().raw(); | 562 *table.data_ = new_table.Release().raw(); |
| 563 NOT_IN_PRODUCT(table.UpdateGrowth(); table.PrintStats();) |
494 } | 564 } |
495 | 565 |
496 // Serializes a table by concatenating its entries as an array. | 566 // Serializes a table by concatenating its entries as an array. |
497 template<typename Table> | 567 template<typename Table> |
498 static RawArray* ToArray(const Table& table, bool include_payload) { | 568 static RawArray* ToArray(const Table& table, bool include_payload) { |
499 const intptr_t entry_size = include_payload ? (1 + Table::kPayloadSize) : 1; | 569 const intptr_t entry_size = include_payload ? (1 + Table::kPayloadSize) : 1; |
500 Array& result = Array::Handle(Array::New(table.NumOccupied() * entry_size)); | 570 Array& result = Array::Handle(Array::New(table.NumOccupied() * entry_size)); |
501 typename Table::Iterator it(&table); | 571 typename Table::Iterator it(&table); |
502 Object& obj = Object::Handle(); | 572 Object& obj = Object::Handle(); |
503 intptr_t result_index = 0; | 573 intptr_t result_index = 0; |
(...skipping 207 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
711 class EnumIndexHashSet : public HashSet<EnumIndexHashTable<KeyTraits, 0> > { | 781 class EnumIndexHashSet : public HashSet<EnumIndexHashTable<KeyTraits, 0> > { |
712 public: | 782 public: |
713 typedef HashSet<EnumIndexHashTable<KeyTraits, 0> > BaseSet; | 783 typedef HashSet<EnumIndexHashTable<KeyTraits, 0> > BaseSet; |
714 explicit EnumIndexHashSet(RawArray* data) : BaseSet(data) {} | 784 explicit EnumIndexHashSet(RawArray* data) : BaseSet(data) {} |
715 EnumIndexHashSet(Zone* zone, RawArray* data) : BaseSet(zone, data) {} | 785 EnumIndexHashSet(Zone* zone, RawArray* data) : BaseSet(zone, data) {} |
716 }; | 786 }; |
717 | 787 |
718 } // namespace dart | 788 } // namespace dart |
719 | 789 |
720 #endif // VM_HASH_TABLE_H_ | 790 #endif // VM_HASH_TABLE_H_ |
OLD | NEW |