-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
Special-case Enumerable.SequenceEqual for byte[] #48287
Conversation
stephentoub
commented
Feb 15, 2021
Method | Toolchain | Count | Mean | Ratio |
---|---|---|---|---|
SequenceEquals | \master\corerun.exe | 1 | 44.769 ns | 1.00 |
SequenceEquals | \pr\corerun.exe | 1 | 7.577 ns | 0.17 |
SequenceEquals | \master\corerun.exe | 100 | 622.219 ns | 1.00 |
SequenceEquals | \pr\corerun.exe | 100 | 9.766 ns | 0.02 |
SequenceEquals | \master\corerun.exe | 1000000 | 5,882,686.440 ns | 1.000 |
SequenceEquals | \pr\corerun.exe | 1000000 | 32,118.099 ns | 0.005 |
Tagging subscribers to this area: @eiriktsarpalis Issue Details[Params(1, 100, 1_000_000)]
public int Count { get; set; }
private byte[] _arr1, _arr2;
[GlobalSetup]
public void Setup()
{
_arr1 = RandomNumberGenerator.GetBytes(Count);
_arr2 = (byte[])_arr1.Clone();
}
[Benchmark]
public bool SequenceEquals() => _arr1.SequenceEqual(_arr2);
|
It optimizes |
That's not necessary. As noted in the code comments, we could use ROS.SequenceEqual after doing a JIT- eliminated value type and assignable from check, but I don't see a good way to convince the C# compiler to let us call it due to the generic constraint. |
A possible fix I mentioned: EgorBo@6a91bf3 |
I don't want that level of unsafe code in LINQ :-) If this is a problem worth solving, I'd rather add a non-constrained variant to MemoryExtensions, e.g. an overload that takes a nullable comparer. |
How common is using |
Would a new overload be worth adding to cover all arrays? Or would that drop too many where they are passed as public static bool SequenceEqual<TSource>(this TSource[] first, TSource[] second) where TSource : IEquatable<TSource>
=> ((ReadOnlySpan<TSource>)firstArr).SequenceEqual(secondArr); |
I'd rather add an ROS-based overload to MemoryExtensions that takes a comparer rather than being constrained and then light up with that here. |
We've hit this (and probably are still hitting this) in dotnet/runtime, in particular in our tests. In the past we've had some innocuous looking tests that did
I would like to expand this to use MemoryExtensions for any array, but that's not feasible right now. So, yes, with this there's an inconsistency between byte[] and other types, but there's already an inconsistency between Enumerable.SequenceEqual and MemoryExtensions.SequenceEqual, and this ends up removing that inconsistency for at least one type that, in my experience, has been the most problematic, at least in our uses. |
I opened #48304. |
If you have a |
My statement was that I'd like to be able to write: if (first is TSource[] firstArray && second is TSource[] secondArray)
{
return ((ReadOnlySpan<TSource>)firstArray).SequenceEqual(secondArray); // or with a `, comparer` argument
} in order to work for any array, not just byte[], but that's not possible today as here TSource is unconstrained and the T generic parameter to MemoryExtensions.SequenceEqual is constrained with |
@eiriktsarpalis, would you like me to close this PR or merge it? Either way, we can update the code if the discussed API is added. |
From my perspective it would be preferable if we solved this in the general case, special casing on the element type feels odd to me. |
Agreed. If we do that, we'll replace this, obviously. But if we don't end up doing that (or don't end up doing that in .NET 6), your preference would then be to do nothing here at all? If so, we can close this. |
I don't have a strong opinion, if it is expected to improve our test performance then it makes perfect sense to merge. |
Ok. |