-
Notifications
You must be signed in to change notification settings - Fork 731
Performance improvements made in response to profiling #1434
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
Conversation
|
Oops, I picked the changes onto an out-of-date |
13ce035 to
5e179a1
Compare
|
Would be great to have some measurement on the improvements, e.g. using BenchmarkDotNet with example data before and after these changes. |
|
That's reasonable. These changes were originally a part of #1419, in which I ran into severe performance issues when trying to work with very large DataTable objects. The two main performance killers were that:
I can't remember exactly what DataTable size I did the calculations with, I believe it was 100,000 rows, but with the changes here I observed a 60-fold increase in performance and also confirmed a performance characteristic better than O(n^2). |
|
The branch behind #1419 includes a profiling fixture based on I feel like this could be even faster, but I don't know the codebase well enough to do it. But, this is a heck of a lot better than literally half an hour to compare two large |
5e179a1 to
129d182
Compare
|
I have added a performance benchmark that sets up large object graphs with a range of sizes and passes them to I'll let it run for a bit and then show what results it has managed to achieve, then I'll run it with the performance changes. |
|
Old code: At these scales, the O(n^2) characteristic of the cyclic reference check isn't terribly obvious because the results are utterly dominated by the repeated computation of the subject (with FluentAssertions does by locating & inspecting the caller's source code). But, when I crunch the numbers further down, it can be seen to be growing noticeably faster than the number of objects. New code: Test sizes: N = 16 ; Graph size: 2583 objects With the new code, the performance proportional to the number of objects is: The average time/object for the old code, for the duration of testing I let it complete, was: |
Src/FluentAssertions/Equivalency/AssertionRuleEquivalencyStep.cs
Outdated
Show resolved
Hide resolved
Src/FluentAssertions/Equivalency/AssertionRuleEquivalencyStep.cs
Outdated
Show resolved
Hide resolved
|
I am getting unit test failures on |
879f173 to
1286511
Compare
1286511 to
da0b1bc
Compare
I've just tried on AppVeyor and locally, but both work without problems. Are you sure? Which one are failing? |
I'm not currently at the workstation where I've been doing my development. I just ran tests on another machine, and This "clean" run did show some failures inside my branch, though. Sigh. :-P |
da0b1bc to
f4145ea
Compare
|
There we go :-) |
|
I'm afraid to ask, but because of all the review comment and rework, it looks like we need to recombine the relevant changes into dedicated commits. |
|
You'd like to see it squashed into a single commit? Or should separate along logical subdivisions, if I can find them? |
|
What I would do is to regroup the relevant changes into a new set of commits. See my blog post if you need some inspiration for that. |
- Updated Benchmarks/NestedClass.cs to have two child references of type Nested instead of one. Updated Create to take a ref parameter for counting the objects, since with two recursive calls it is no longer as straightforward. Updated calls from Benchmarks/BeEquivalentTo.cs correspondingly. - Added new benchmark Benchmarks/LargeObjectGraph.cs which sets up object graphs of various sizes and benchmarks BeEquivalentTo between them. - Updated Program.cs to run the new benchmark.
…t to avoid linear lookups. Updated the Equals method in ObjectReference.cs be symmetrical. Added corresponding unit tests. Updated FluentAssertions.Specs.csproj to be strong-named. Updated its reference to Chill to version 4.1.0 and updated AssemblyA.csproj and AssemblyB.csproj to strong name their output as well. Deleted TypeExtensions.cs rfom FluentAssertions.Specs, since it can now use the version internal to FluentAssertions. Updated FluentAssertions.csproj to generate an InternalsVisibleTo attribute granting access to its internals from FluentAssertions.Specs. Reran AcceptApiChanges.ps1, since the API surface area report captures the InternalsVisibleTo attribute.
954d109 to
b7a1fe7
Compare
I saw it as being sort intrinsically a "part of" the other changes -- a change with no motivation of its own, driven by the need to unit test an internal detail. Each of the commits achieves an outcome, and that would be a commit that doesn't actually achieve an outcome of its own, but merely reworks things to allow an actual change. I could split it though if you think it would make a difference :-) |
|
Nah, don't bother. Let's wait for the review from @jnyrup |
017232c to
b7a1fe7
Compare
…ck from the end instead of from the start, removing the need to cache context values immediately. - Updated DetermineCallerIdentity.cs to search backward from the end. Added helper method IsCompilerServices to ignore async machinery. - Added a mechanism to allow some stack frames to be skipped, so that constructs that re-entrantly invoke FluentAssertions and which should find the nested call skip over the outer call. * Added method OverrideStackSearchUsingCurrentScope() to CallerIdentifier.cs, returning an IDisposable that stores a count of stack frames to skip that remains in effect until it is disposed. * Updated call sites in SelfReferencingCollectionAssertions.cs, AsyncFunctionAssertions.cs and DelegateAssertions.cs to use this mechanism.
…until it is actually needed. - Retyped Context to a Lazy<string> in AssertionScope.cs, and updated assignments to Context in EquivalencyValidator.cs and ExceptionAssertions.cs correspondingly.
…ypes that could not possibly satisfy the checks. - Updated GenericDictionaryEquivalencyStep.cs and GenericEnumerableEquivalencyStep.cs to fast-path calls where the supplied type has a TypeCode other than TypeCode.Object.
… when the supplied key is already in the list.
b7a1fe7 to
f845242
Compare
`InternalObjectExtensions` and `InternalTypeExtensions` were only public for the sake of unit testing. Since fluentassertions#1434 FluentAssertions.Specs is also strong named, so these classes can now be truly internal.
This PR makes some performance improvements based on profiling with #1419's changes, testing DataTable objects with many records:
IMPORTANT
If the contribution adds a feature or fixes a bug, please update the release notes, which are published on the website.If the contribution changes the public API the changes needs to be included by runningAcceptApiChanges.ps1/AcceptApiChanges.sh.If the contribution affects the documentation, please include your changes in this pull request so the documentation will appear on the website.