QNTM-5137: VM Method resolution performance improvement#8755
QNTM-5137: VM Method resolution performance improvement#8755saintentropy merged 23 commits intoDynamoDS:masterfrom
Conversation
| int cartIndex = ri.CartesianIndex; | ||
|
|
||
| //We determine if the input paramaters are homogenious to set the final function endpoint once | ||
| if (cartIndex == 0) |
There was a problem hiding this comment.
What is this check for?
There was a problem hiding this comment.
@aparajit-pratap This is so you only check the homogeneity before you get into the recursive calls of ExecWithRISlowPath(). You could call these at different CartesianIndex values but you start to loose some of the performance improvement with multiple nested calls to AreParametersHomogeneous(). I will double check though... Right now I make the assumption that you only get the benefit of the predetermined finalFunctionEndPoint if you pass AreParametersHomogeneous() with all the parameters.
| } | ||
|
|
||
| //Add single sample parameter to pass for evalutation in SelectFinalFep | ||
| finalFormalParameters.Add(flatParameters[0]); |
There was a problem hiding this comment.
should this not be finalFormalParameters.Add(flatParameters[j]);
There was a problem hiding this comment.
At this point we don't care about which item we select. The loop before has determined that all the flatParameters are the same so there I select the first item as a sample type
|
@saintentropy this looks good. I have a few comments? Have you tried running the tests after your change? |
| throw new ReplicationCaseNotCurrentlySupported(Resources.AlgorithmNotSupported); | ||
| } | ||
|
|
||
| //We determine if the input paramaters are homogenious to set the final function endpoint once |
| if(homogenious) | ||
| finalFuntionEndPoint = SelectFinalFep(c, functionEndPoint, finalFormalParameters, stackFrame, runtimeCore); | ||
| } | ||
|
|
There was a problem hiding this comment.
Can this block of changes go before:
if (replicationInstructions.Count == 0)
{
return ExecWithZeroRI(functionEndPoint, c, formalParameters, stackFrame, runtimeCore, previousTraceData, newTraceData, finalFuntionEndPoint);
}
There was a problem hiding this comment.
I think not for the same reason I mentioned before.... I was assuming that we wanted to validate the function input parameters are the same types (homogeneous) with the initial call of ExecWithRISlowPath() vs at every nested invocation. That leaves some situations where you could have a partial optimization uncovered (ie they drop back to determining the Function endpoint with every call of the replication) but this may be simpler.
| if (formalParameter.IsArray) | ||
| { | ||
| DSArray array = runtimeCore.Heap.ToHeapObject<DSArray>(formalParameter); | ||
| StackValue[] flatParameters = array.Values.ToArray(); |
There was a problem hiding this comment.
Why not use var? LHS types are simply unnecessary.
| } | ||
| } | ||
|
|
||
| //Add single sample parameter to pass for evalutation in SelectFinalFep |
There was a problem hiding this comment.
evalutation is spelled wrong twice in this file
|
Is there a task around validating this performance improvement and merging this at some point? |
|
@mjkkirschner Any thoughts on what scenarios would be good to validate performance. |
|
I'm still getting up to speed on the VM but this change seems like it has bounded risk and large value, so I'm in favor of prioritizing whatever we need to do to increase our confidence and get it merged. Things I would like to see:
|
|
@Dewb which Dynamo performance tests are you referring to in the Revit test suite? |
| /// Determine if the formalParameters are homogeneous and initialize a flat list of final formalParameters | ||
| /// </summary> | ||
| /// <returns>true if the formalParameters are homogeneous</returns> | ||
| private static bool AreParametersHomogeneous(List<StackValue> formalParameters, RuntimeCore runtimeCore, List<StackValue> finalFormalParameters) |
There was a problem hiding this comment.
feels weird to me that this has side effects on a list you need to initialize and pass in to this method... what about just returning a tuple?
| public Symbol[] TryGetSymbols(string name, Func<Symbol, bool> predicate) | ||
| { | ||
| string symbolName = name.Split('.').Last(); | ||
| var index = name.LastIndexOf('.'); |
There was a problem hiding this comment.
whats with this change - I might have missed it in the PR description?
There was a problem hiding this comment.
Ah sorry... just faster implementation. Split finds all the dots and LastIndexOf moves from the back
| if (cartIndex == 0) | ||
| { | ||
| List<StackValue> finalFormalParameters = new List<StackValue>(); | ||
| var homogeneous = AreParametersHomogeneous(formalParameters, runtimeCore, finalFormalParameters); |
There was a problem hiding this comment.
this has a worst case cost of 2*n - and I don't know much about the VM - so a question - by the time we are making a function call here - have we already marshaled this data?
| } | ||
| catch(Exception e) | ||
| { | ||
| Console.WriteLine(e.ToString()); |
There was a problem hiding this comment.
Why is this no longer required?
There was a problem hiding this comment.
I guess I should say it is very very annoying now that it pushes the error to the console every single time you open a DYN file (json)
There was a problem hiding this comment.
Oh, good point!
There was a problem hiding this comment.
@QilongTang are you okay with this being removed - was there a reason we wanted this?
There was a problem hiding this comment.
@mjkkirschner It was for diagnosing old corrupted XML, I can see it less useful now. Fine with this change
| SingleRunTraceData previousTraceData, | ||
| SingleRunTraceData newTraceData) | ||
| SingleRunTraceData newTraceData, | ||
| FunctionEndPoint finalFuntionEndPoint = null) |
There was a problem hiding this comment.
Typo: should be finalFunctionEndpoint
| return ret; | ||
| } | ||
|
|
||
| private List<double> TrimRange(IEnumerable<double> values, double stdDev, int multiplyer = 1) |
There was a problem hiding this comment.
typo: multiplier :)
|
@saintentropy just curious what the graphs look like in the tests? Could you share a screenshot? |
|
@aparajit-pratap Hey this is the graph I am using for performance. This is the graph with homogeneous types (all |
|
The perf validation test of homogenous vs heterogeneous is usually like this This is relative to my machine but in general I see homogeneous running in about 80% of the heterogenous times. With the check disabled (ie the previous code) I get ~ 192ms. |
| @@ -1876,8 +1882,11 @@ private StackValue ExecWithZeroRI(List<FunctionEndPoint> functionEndPoint, Conte | |||
| /// Determine if the formalParameters are homogeneous and initialize a flat list of final formalParameters | |||
| /// </summary> | |||
| /// <returns>true if the formalParameters are homogeneous</returns> | |||
There was a problem hiding this comment.
@saintentropy out dated comment now that you return tuples
| //@PERF: Todo add a fast path here for the case where we have a homogenious array so we can directly dispatch | ||
| FunctionEndPoint finalFep = SelectFinalFep(c, functionEndPoint, formalParameters, stackFrame, runtimeCore); | ||
| if(finalFep == null) | ||
| finalFep = SelectFinalFep(c, functionEndPoint, formalParameters, stackFrame, runtimeCore); |
| namespace Dynamo.Tests | ||
| { | ||
| [TestFixture] | ||
| class HomogeneousListTests : DynamoModelTestBase |
There was a problem hiding this comment.
since it was important to use this class can you note it?
|
|
||
| protected override void GetLibrariesToPreload(List<string> libraries) | ||
| { | ||
| libraries.Add("ProtoGeometry.dll"); |
There was a problem hiding this comment.
here as well - if this difference in time required loading some number of libs, lets note them.
| return ret; | ||
| } | ||
|
|
||
| private List<double> TrimRange(IEnumerable<double> values, double stdDev, int multiplier = 1) |
There was a problem hiding this comment.
comment on what this does?
|
@saintentropy LGTM - just a few comment issues. |
|
I will run the Jenkins self service a few more times and then merge |
|
Ok tests running well on self service CI. Will merge now |

Purpose
The purpose of this PR is to add an optimization to the VM related to Method Resolution. Currently the VM determines the
FunctionEndPointfor every specific call to the node as determined by the input parameters and replication strategy. This ensures that varied inputs can be dispatched correctly for each specific call. TheSelectFinalFep()method (aka select final function endpoint) is relatively fast but is often called repeatedly with the same input parameters. This PR adds a mechanism to validate that the input parameters for the node are of the same type (ie homogeneous) which allows theSelectFinalFep()to be called once and the resultingFunctionEndPointto be cached. In general most user call Dynamo nodes with homogeneous inputs which allows this optimization to improve throughput of the VM in most user scenarios. In terms of performance optimization, the new methodAreParametersHomogeneous()is linearly bound to the number of inputs parameters vs calls toFunctionEndPointwhich are linearly bound to the input parameter combinations determined by replication. Additionally, the type comparison methods utilized inAreParametersHomogeneous()are much faster (2 orders of magnitude) then the method resolution logic required inSelectFinalFep()and includes short-curcuit logic in-case input parameters are found to be dissimilar. In my profiling I found that this optimization can improve VM throughput by 12 to 20 percent for graph topologies that have lots of homogenous lists.QNTM-5137
Declarations
Check these if you believe they are true
*.resxfilesReviewers
@aparajit-pratap @pboyer
FYIs
@ramramps @mjkkirschner