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
Performance regression in xunit.assert between 2.4.2 and 2.5.0 (then more) in Assert.Equal with byte arrays #2806
Comments
I can verify that here:
(I don't see the same variation that you do, but GC may be a good guess as to what's going on there. My test machine is an AMD R9-5900X with 64GB of RAM running Windows 11.) So yes, there's definitely a performance regression. The assertion library was overhauled for v3, and then back-ported into 2.5.0 and later. I am surprised to discover it's this much slower, because I'm pretty sure most of what I did should have improved performance rather than reduced it (for example, ensuring we stop as much double enumeration as we could). The likely answer is that In the meantime, I may have a workaround for you that actually performs a LOT better than I could ever get with a fix to
The magic here boils down to treating arrays of un-managed types as spans: https://github.com/xunit/assert.xunit/blob/3bb3309722867df337161527182683b3adf32413/EqualityAsserts.cs#L34-L66 Unfortunately, no version of the v2 asserts is available in binary form that defines You can just use the v3 assertions in your v2 project (which is possible, although I have not pushed any v3 bits to NuGet because it's not ready for public consumption--not because of the assertion library, which is identical source between v2 and v3--but because I haven't made third party runners work yet, and that includes support for Visual Studio's Test Explorer and You could also use the assertion library via source, ala:
Performance is identical to the v3, since the span path is now open to us. For more information on using assert this way, check out the README on annotations: https://github.com/xunit/assert.xunit/#annotations Currently in v2, we only define ...and as I describe all of this, it makes me want to just ship a |
That really sounds like a good idea to me - but my guess is that there'll be some interesting corner cases with the number of users xUnit has. The regression definitely isn't a blocker for me; a bit of an inconvenience, but that's all. Please don't rush to put together an interim improvement on my account :) |
Well, there are two separate issues here in my mind: (a) People have been wanting things like (b) There is a performance regression, most likely from |
I'm not sure yet if this will ship as 2.5.4 or 2.6.0; I'm thinking maybe the latter because this is a decent sized change, adding the new binaries. Either way, there is a prerelease build on feedz.io if you want to verify on your end, which would be helpful to understand if you run into any other issues. Note that by having more signatures for assertions, it is possible that you may uncover method ambiguities where none existed previously, though the major time for that was moving from 2.4.2 => 2.5.x. We have a ready-made |
Hmm, ring buffer shaved a few percentage points off the time, but still nowhere near the old performance. (I bumped it down to 5 million from 50 million because time is valuable. 😂)
Ring buffer: xunit/assert.xunit@f184355?w=1 |
Cut a huge chunk out by not running unnecessary new code to support
Conditional & caching: xunit/assert.xunit@6a94a86?w=1 |
Another big chunk, down to
Getting close enough now that if I can't find another big smoking gun, I might call it done. AsTracker improvements: xunit/assert.xunit@b3e3c93?w=1 |
Okay, this one's just embarrassing. 😂 Unused (expensive) line of code in the comparison hot path.
Shame 🔔 Shame 🔔 xunit/assert.xunit@e48a306?w=1 |
One last comparison set with 50 million again:
I got within 10%, so I think I'm happy with where it landed. Nothing else seems like an obvious candidate for fixing. Plus, in your specific example here, the newly available
I'll be pushing this as a 2.6.0 release, since it made a few behavioral changes (none of which I expect to trip people up, but better to get them thinking it's a little more major than a simple bug fix). I also technically changed the contracts of some |
Available in v2: |
That's amazing - thanks so much for all the time you've put into this :) |
Confirmed that with 2.5.4-pre.12, my actual tests (for an experimental project) go down from 2m => 21s for .NET 6, and 3m3s => 51s for .NET 4.6.2. (I believe the difference there between frameworks is in my production code, but I'll dig into that when I have more time.) Thanks again! |
Confession: retirement leaves me very few opportunities to break out the performance profiling tools in Visual Studio, so this was a bit irresistible. 😂 |
I noticed this with a unit test that compares large byte arrays. I haven't tested whether it applies to other types. (Byte arrays are probably the most common type to compare really large amounts of data.)
Repro: create a console app targeting net6.0, add a reference to xunit.assert, and replace Program.cs with:
Change the project file to vary the version of xunit.assert being tested.
Results (in version order, running
dotnet run -c Release
):So it looks like there was a big regression between 2.4.2 and 2.5.0, then it was (maybe) made slightly worse in 2.5.1, and has since stayed steady? The large variation between iterations within a given version makes me suspect there's GC going on here, but I haven't looked into that.
The text was updated successfully, but these errors were encountered: