Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switch to a HashSet<T> as backing for SafeEnumerableList #16633

Merged
merged 2 commits into from
Aug 13, 2024

Conversation

halgari
Copy link
Contributor

@halgari halgari commented Aug 8, 2024

What does the pull request do?

Switches SafeEnumerableList to SafeEnumerableSet, in order to offer better performance on the .Add and .Remove methods

What is the current behavior?

As the Classes class adds and removes listeners it stores them in a SafeEnumerableList. When listeners are removed they are removed from this list. However, since the backing store for this collection is a List<T> the .Remove method becomes a O(n) scan on the items. In our application we were seeing significant pauses in the application when the number of listeners grew rather large. In our case one of the lists had about 700 listeners, so the pauses became quite large.

In addition, if it is assumed that listeners that are added recently (like those deep in a tree) will be removed first, we can see another problem: the items we remove will most likely be at the end of the collection.

What is the updated/expected behavior with this PR?

The backing store is now a HashSet. None of the public APIs have changed in behavior, although there may be slight differences in performance, similar to those expected from switching from a list to a hashset.

How was the solution implemented (if it's not obvious)?

Pretty much the same as the previous solution except for the following changes:

  • We can't do a _list[idx] inside the Enumerable class as HashSets don't support offset indexing. Instead when we create the enumerable we wrap the specialized .NET HashSet enumerator, and add our functionality to this method. This also means that we no longer need a few of the fields on Enumerable
  • We have to clone the HashSet if it is modified during enumeration. This is supported via new HashSet(_set) which has an optimized fastpath in the .NET Constructor. It is true that cloning a hashset is slower than cloning an array, but it is assumed that the "modified while enumerating" usecases are rare.

Checklist

  • Added unit tests (if possible)?
    • Existing tests were updated
  • Added XML documentation to any related classes?
    • No classes added
  • Consider submitting a PR to https://github.com/AvaloniaUI/avalonia-docs with user documentation
    • Internal class, not required

@avaloniaui-bot
Copy link

You can test this PR using the following package version. 11.2.999-cibuild0051095-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

@cla-avalonia
Copy link
Collaborator

cla-avalonia commented Aug 8, 2024

  • All contributors have signed the CLA.

@halgari
Copy link
Contributor Author

halgari commented Aug 8, 2024

@cla-avalonia agree

Copy link
Member

@MrJul MrJul left a comment

Choose a reason for hiding this comment

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

LGTM, this change makes perfect sense.
Thank you for your contribution!

@kekekeks
Copy link
Member

Note that this PR isn't safe to backport since it technically changes the enumeration order, which can have some effects on the way styles get attached/detached.

@maxkatz6 maxkatz6 added this pull request to the merge queue Aug 13, 2024
Merged via the queue into AvaloniaUI:master with commit eb5f395 Aug 13, 2024
11 checks passed
@halgari halgari deleted the safe-enumerable-hash-set branch August 14, 2024 19:12
HermanKirshin pushed a commit to JetBrains/Avalonia that referenced this pull request Oct 30, 2024
…16633)

* Switch to a set as backing for _listeners on Classes. Via the `SafeEnumerableHashSet`

* Update docs

(cherry picked from commit eb5f395)
HermanKirshin pushed a commit to JetBrains/Avalonia that referenced this pull request Nov 1, 2024
…16633)

* Switch to a set as backing for _listeners on Classes. Via the `SafeEnumerableHashSet`

* Update docs

(cherry picked from commit eb5f395)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants