Skip to content

QNTM-5137: VM Method resolution performance improvement#8755

Merged
saintentropy merged 23 commits intoDynamoDS:masterfrom
saintentropy:VMPerfImprovement
Sep 14, 2018
Merged

QNTM-5137: VM Method resolution performance improvement#8755
saintentropy merged 23 commits intoDynamoDS:masterfrom
saintentropy:VMPerfImprovement

Conversation

@aparajit-pratap
Copy link
Contributor

@aparajit-pratap aparajit-pratap commented Apr 16, 2018

Purpose

The purpose of this PR is to add an optimization to the VM related to Method Resolution. Currently the VM determines the FunctionEndPoint for 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. The SelectFinalFep() 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 the SelectFinalFep() to be called once and the resulting FunctionEndPoint to 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 method AreParametersHomogeneous() is linearly bound to the number of inputs parameters vs calls to FunctionEndPoint which are linearly bound to the input parameter combinations determined by replication. Additionally, the type comparison methods utilized in AreParametersHomogeneous() are much faster (2 orders of magnitude) then the method resolution logic required in SelectFinalFep() 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

  • The code base is in a better state after this PR
  • Is documented according to the standards
  • The level of testing this PR includes is appropriate
  • User facing strings, if any, are extracted into *.resx files
  • All tests pass using the self-service CI.
  • Snapshot of UI changes, if any.
  • Changes to the API follow Semantic Versioning, and are documented in the API Changes document.

Reviewers

@aparajit-pratap @pboyer

FYIs

@ramramps @mjkkirschner

@aparajit-pratap aparajit-pratap added the DNM Do not merge. For PRs. label Apr 16, 2018
int cartIndex = ri.CartesianIndex;

//We determine if the input paramaters are homogenious to set the final function endpoint once
if (cartIndex == 0)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this check for?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@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]);
Copy link
Contributor Author

@aparajit-pratap aparajit-pratap Apr 16, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this not be finalFormalParameters.Add(flatParameters[j]);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@aparajit-pratap
Copy link
Contributor Author

@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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spelling(homogeneous)

if(homogenious)
finalFuntionEndPoint = SelectFinalFep(c, functionEndPoint, finalFormalParameters, stackFrame, runtimeCore);
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this block of changes go before:

if (replicationInstructions.Count == 0)
{
               return ExecWithZeroRI(functionEndPoint, c, formalParameters, stackFrame, runtimeCore, previousTraceData, newTraceData, finalFuntionEndPoint);
 }

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me look.

Copy link
Contributor

@saintentropy saintentropy Apr 18, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@saintentropy saintentropy changed the title [DNM] Vm perf improvement [DNM] WIP VMperf improvement Apr 16, 2018
@saintentropy saintentropy changed the title [DNM] WIP VMperf improvement [DNM] WIP VM Method Resoultion performance improvement Apr 18, 2018
@pboyer pboyer changed the title [DNM] WIP VM Method Resoultion performance improvement [DNM] WIP VM Method resolution performance improvement Apr 18, 2018
if (formalParameter.IsArray)
{
DSArray array = runtimeCore.Heap.ToHeapObject<DSArray>(formalParameter);
StackValue[] flatParameters = array.Values.ToArray();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use var? LHS types are simply unnecessary.

}
}

//Add single sample parameter to pass for evalutation in SelectFinalFep
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

evalutation is spelled wrong twice in this file

@mjkkirschner
Copy link
Member

Is there a task around validating this performance improvement and merging this at some point?

@saintentropy saintentropy changed the title [DNM] WIP VM Method resolution performance improvement [DNM] VM Method resolution performance improvement Aug 20, 2018
@saintentropy
Copy link
Contributor

@mjkkirschner Any thoughts on what scenarios would be good to validate performance.

@saintentropy saintentropy removed the DNM Do not merge. For PRs. label Aug 21, 2018
@saintentropy saintentropy changed the title [DNM] VM Method resolution performance improvement QNTM-5137: VM Method resolution performance improvement Aug 21, 2018
@Dewb
Copy link
Contributor

Dewb commented Aug 21, 2018

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:

  • A new unit test specifically for correct resolution behavior in a variety of homogenous and heterogenous replication scenarios
  • Performance test suite results before and after this change (I know the Revit test suite has Dynamo performance tests, I'm not clear on whether they're in the Dynamo CI pipeline yet.)

@aparajit-pratap
Copy link
Contributor Author

@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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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('.');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whats with this change - I might have missed it in the PR description?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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());
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this no longer required?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, good point!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@QilongTang are you okay with this being removed - was there a reason we wanted this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mjkkirschner It was for diagnosing old corrupted XML, I can see it less useful now. Fine with this change

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Thanks

SingleRunTraceData previousTraceData,
SingleRunTraceData newTraceData)
SingleRunTraceData newTraceData,
FunctionEndPoint finalFuntionEndPoint = null)
Copy link
Contributor Author

@aparajit-pratap aparajit-pratap Sep 13, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: should be finalFunctionEndpoint

return ret;
}

private List<double> TrimRange(IEnumerable<double> values, double stdDev, int multiplyer = 1)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo: multiplier :)

@aparajit-pratap
Copy link
Contributor Author

@saintentropy just curious what the graphs look like in the tests? Could you share a screenshot?

@saintentropy
Copy link
Contributor

image

@aparajit-pratap Hey this is the graph I am using for performance. This is the graph with homogeneous types (all int). I alter either the first insert or the last insert to a double for the heterogeneous test files

@saintentropy
Copy link
Contributor

saintentropy commented Sep 13, 2018

The perf validation test of homogenous vs heterogeneous is usually like this

Homogeneous average execution: 161.113782608696ms
Heterogeneous first item average execution: 200.779774468085ms
Heterogeneous last item average execution: 195.1759125ms

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>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mind using braces?

namespace Dynamo.Tests
{
[TestFixture]
class HomogeneousListTests : DynamoModelTestBase
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since it was important to use this class can you note it?


protected override void GetLibrariesToPreload(List<string> libraries)
{
libraries.Add("ProtoGeometry.dll");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comment on what this does?

@mjkkirschner
Copy link
Member

@saintentropy LGTM - just a few comment issues.

@saintentropy
Copy link
Contributor

I will run the Jenkins self service a few more times and then merge

@saintentropy
Copy link
Contributor

Ok tests running well on self service CI. Will merge now

@saintentropy saintentropy merged commit 57a33ae into DynamoDS:master Sep 14, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants