-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
Improve List performance in IEnumerable.SequenceEqual #67722
Conversation
Tagging subscribers to this area: @dotnet/area-system-linq Issue DetailsCurrently IEnumerable.SequenceEqual is only vectorized when both the source and comparison enumerable are arrays. This change allows them to be either an array or a List. To be able to use the already existing The fast path of MemoryExtensions.SequenceEqual is restricted to For benchmark numbers it's pretty much as expected, it scales really well with large ranges. For this example I mixed comparing an array with a List public class SequenceEqualsListArrayBenchmark
{
[Params(1, 4, 32, 256, 1024)]
public int Length { get; set; }
private IEnumerable<int> _first;
private IEnumerable<int> _second;
[GlobalSetup]
public void Setup()
{
_first = Enumerable.Range(1, Length).ToList();
_second = Enumerable.Range(1, Length).ToArray();
}
[Benchmark]
public bool SequenceEquals() => _first.SequenceEqual(_second);
}
|
Nice! I assume this change slightly regresses e.g. List of floats (and any struct) |
Is removal of a generic constraint on a public method a breaking change? It can cause a callsite to become ambiguous where it was not before if there was a user provided extension that did not have the constraint. |
@bartonjs for that breaking change question |
The method the constraint is being removed from isn't public.
I'm more concerned this is going to regress the performance of arrays of reference types. Prior to this change, they could take the fast path that uses MemoryExtensions.SequenceEqual on spans, avoiding all enumeration and interface dispatch costs. With this change, they will be forced down the @yesmey, can you please share benchmark numbers for reference types? |
here's the benchmark results for
like you said @stephentoub the regression is high, so I'll try to see if there's another way to approach this. We can put this on hold until then |
This pull request has been automatically marked |
Currently IEnumerable.SequenceEqual is only vectorized when both the source and comparison enumerable are arrays. This change allows them to be either an array or a List.
To be able to use the already existing
TryGetSpan
I had to remove the struct constraint. I still wanted to keep the original intent as the comment suggests, so I replaced it with aType.IsValueType
condition. The method was introduced in #64624@stephentoub Please let me know what you think of this.
The fast path of MemoryExtensions.SequenceEqual is restricted to
RuntimeHelpers.IsBitwiseEquatable
, and it seem a bit too low-level and implementation coupled to use here, so I chose to keep the restriction to IsValueType for that reason.For benchmark numbers it's pretty much as expected, it scales really well with large ranges. For this example I mixed comparing an array with a List