| 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 |