-
Notifications
You must be signed in to change notification settings - Fork 635
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 trace reconciliation performance #15725
base: master
Are you sure you want to change the base?
Improve trace reconciliation performance #15725
Conversation
UI Smoke TestsTest: success. 11 passed, 0 failed. |
var orphanedSerializables = cs.GetOrphanedSerializables().ToList(); | ||
if (callsiteToOrphanMap.ContainsKey(cs.CallSiteID)) | ||
var orphanedSerializables = cs.GetOrphanedSerializables(); | ||
if (callsiteToOrphanMap.TryGetValue(cs.CallSiteID, out var serializablesForCallsite)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how does this change improve performance?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the immediate action after the ContainsKey is to mutate the value at that key, using TryGetValue avoids a redundant dictionary lookup
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
okay, seems like a pretty safe change, though under profiling does it make an actual difference here?
} | ||
else | ||
{ | ||
callsiteToOrphanMap.Add(cs.CallSiteID, orphanedSerializables); | ||
callsiteToOrphanMap.Add(cs.CallSiteID, orphanedSerializables.ToList()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so orphanedSerializables
is already a List<string>
-
Dynamo/src/Engine/ProtoCore/Lang/CallSite.cs
Line 475 in 10df94f
var result = new List<string>(); |
IList
- I assume that this calls the List constructor passing in the existing list, have you verified there is any benefit to moving this ToList
call around?
If it's really such a big performance impact you could probably change this data structure to be a dict of IList
hey @bendikberg I would like some more description in the PR about what your changes are intending and how you validated that they make a substantial improvement. Maybe not all of these changes are equally important. |
@mjkkirschner To add some more context: this is more relevant to D4R because that writes huge amounts of trace data tying in the dynamo wrapper elements to the matching Revit elements for continuous updates between Dynamo/Revit sessions. In cases where the graph stored a lot of references that are no longer valid, the first execution would be very slow. Look at the attached trace data images. The I'm not familiar with Dynamo for Civil3d but if it tracks the elements in a similar way, then this would be relevant there as well. |
I made this PR during the D4R performance profiling project. This code was originally written to be more of a research and test thing, and is in a pretty raw state. The reason for the raw state of the PR is that I assumed someone on the Dynamo team would just grab the parts of this PR they deemed relevant and implement those. The thing I was trying to do was to figure out where and why the Callsite TraceData was slowing down the first run of a graph, so I touched on a few different files related to the TraceData handling. Most of the .ToList and ContainsKey/TryGetValue-juggling can be left out of this PR or skipped entirely when they are compared to the performance gained from algorithmic optmizations. They don't cause the same type of exponentital slowdowns, but still do unecessary work. I made those changes since I figured as long as I was upgrading the TraceData performance on the whole, I would also include simple (but tiny) optimizations. I made some benchmarks for the most impactful changes: This gets slower as the number of TraceData items on a node increases (this can easily get very large for a Revit file). RecursiveFillWithNestedData change
This scales poorly with the number of nodes on a canvas.
For small input sizes it doesn't add up to many microseconds, but it scales very poorly with larger input, which is bad since users might save whatever data in their files, which will make Dynamo hang for seconds or minutes. TL;DR:This PR can be reduced to only include the algorithmic improvements that I've shown in the benchmarks. |
…zablesAndClearHistoricalTraceData return List, add fast lookup.
This has now passed on master-5 a bunch of times in a row, so thats great. I am thinking about how we might stick this behind a feature flag. |
Purpose
When loading a file with a lot of bindings stored in CallSite.SingleRunTraceData, the trace reconciliation spends a lot of time reading nested data due to list allocations. And in general it does unecessary work copying lists and iterating through IEnumerables, for .Contains-testing and the like.
These changes have a large impact on the graph execution time for graphs with a lot of orphaned serializable data, as seen in these profiling results.
Declarations
Check these if you believe they are true
*.resx
filesRelease Notes
N/A
Reviewers
(FILL ME IN) Reviewer 1 (If possible, assign the Reviewer for the PR)
@mjkkirschner
(FILL ME IN, optional) Any additional notes to reviewers or testers.
FYIs
(FILL ME IN, Optional) Names of anyone else you wish to be notified of
@dimven