Skip to content

Use HashTable in DynamicMap and fix bug in remove#15158

Merged
alice-i-cecile merged 6 commits intobevyengine:mainfrom
SkiFire13:dynamic-map-hashtable
Sep 30, 2024
Merged

Use HashTable in DynamicMap and fix bug in remove#15158
alice-i-cecile merged 6 commits intobevyengine:mainfrom
SkiFire13:dynamic-map-hashtable

Conversation

@SkiFire13
Copy link
Copy Markdown
Contributor

Objective

  • DynamicMap currently uses an HashMap from a u64 hash to the entry index in a Vec. This is incorrect in the presence of hash collisions, so let's fix it;
  • DynamicMap::remove was also buggy, as it didn't fix up the indexes of the other elements after removal. Fix that up as well and add a regression test.

Solution

  • Use HashTable in DynamicMap to distinguish entries that have the same hash by using reflect_partial_eq, bringing it more in line with what DynamicSet does;
  • Reimplement DynamicMap::remove to properly fix up the index of moved elements after the removal.

Testing

  • A regression test was added for the DynamicMap::remove issue.

Some kinda related considerations: the use of a separate Vec for storing the entries adds some complications that I'm not sure are worth. This is mainly used to implement an efficient get_at, which is relied upon by MapIter. However both HashMap and BTreeMap implement get_at inefficiently (and cannot do so efficiently), leading to a O(N^2) complexity for iterating them. This could be removed in favor of a Box<dyn Iterator> like it's done in DynamicSet.

@ickshonpe ickshonpe added A-Reflection Runtime information about types C-Bug An unexpected or incorrect behavior S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Sep 11, 2024
Copy link
Copy Markdown
Member

@MrGVSV MrGVSV left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me! This is certainly better than just hoping there's no hash collision haha.

And like we talked about on Discord, I think that get_at should be removed (not necessarily in this PR), so I'm not too concerned about that.

@MrGVSV MrGVSV added the D-Straightforward Simple bug fixes and API improvements, docs, test and examples label Sep 11, 2024
Copy link
Copy Markdown
Contributor

@pablo-lua pablo-lua left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thumbs up, no collisions

@pablo-lua pablo-lua added S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it and removed S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Sep 24, 2024
@SkiFire13 SkiFire13 force-pushed the dynamic-map-hashtable branch from fb632a9 to e784a46 Compare September 29, 2024 13:23
@alice-i-cecile alice-i-cecile added this pull request to the merge queue Sep 30, 2024
Merged via the queue into bevyengine:main with commit 0d751e8 Sep 30, 2024
@SkiFire13 SkiFire13 deleted the dynamic-map-hashtable branch September 30, 2024 19:02
robtfm pushed a commit to robtfm/bevy that referenced this pull request Oct 4, 2024
…5158)

# Objective

- `DynamicMap` currently uses an `HashMap` from a `u64` hash to the
entry index in a `Vec`. This is incorrect in the presence of hash
collisions, so let's fix it;
- `DynamicMap::remove` was also buggy, as it didn't fix up the indexes
of the other elements after removal. Fix that up as well and add a
regression test.

## Solution

- Use `HashTable` in `DynamicMap` to distinguish entries that have the
same hash by using `reflect_partial_eq`, bringing it more in line with
what `DynamicSet` does;
- Reimplement `DynamicMap::remove` to properly fix up the index of moved
elements after the removal.

## Testing

- A regression test was added for the `DynamicMap::remove` issue.

---

Some kinda related considerations: the use of a separate `Vec` for
storing the entries adds some complications that I'm not sure are worth.
This is mainly used to implement an efficient `get_at`, which is relied
upon by `MapIter`. However both `HashMap` and `BTreeMap` implement
`get_at` inefficiently (and cannot do so efficiently), leading to a
`O(N^2)` complexity for iterating them. This could be removed in favor
of a `Box<dyn Iterator>` like it's done in `DynamicSet`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Reflection Runtime information about types C-Bug An unexpected or incorrect behavior D-Straightforward Simple bug fixes and API improvements, docs, test and examples S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it

Projects

No open projects
Status: In Progress

Development

Successfully merging this pull request may close these issues.

5 participants