Skip to content
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

Reduce use of reflection in SetupAllProperties #549

Merged
merged 1 commit into from
Dec 10, 2017

Conversation

stakx
Copy link
Contributor

@stakx stakx commented Dec 10, 2017

Excessive reflection in SetupAllProperties contributes to Mock.Of<T>'s bad performance (#547).

This PR reduces use of reflection in this method by adding instances of specialized MethodCall types directly to mock.Setups, instead of installing setups by making calls to mock.SetupProperty(m => m.Property, initialValue) or mock.SetupGet(m => m.Property).Returns(value) through reflection.

foreach (var property in properties)
{
var expression = GetPropertyExpression(mockType, property);
object initialValue = GetInitialValue(mock, mockedTypesStack, property);
object value = GetInitialValue(mock, mockedTypesStack, property);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Renamed because this variable no longer just holds the initial value; it acts as the backing field for the set up property. It gets captured by both setups' getter / setter lambdas.

/// Only use this constructor when you know that the specified <paramref name="method"/> has no `out` parameters,
/// and when you want to avoid the <see cref="MatcherFactory"/>-related overhead of the other constructor overload.
/// </remarks>
public MethodCall(Mock mock, Condition condition, Expression originalExpression, MethodInfo method, IMatcher[] argumentMatchers)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

For the type of setups we want to install (i. e. setups that implement property getters and setters), we know in advance what kinds of matchers we'll need. The existing constructor would however force us to build an It.IsAny<T>() expression, which would then be put through the (slow!) MatcherFactory.CreateMatcher factory method. This new constructor allows us to skip these two unnecessary steps.

if (mocked != null)
{
SetupAllProperties(mocked.Mock, mockedTypesStack);
}

mock.Setups.Add(new PropertyGetterMethodCall(mock, expression, property.GetGetMethod(true), () => value));
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The above LINQ query only returns properties that are readable, so we don't need to check property.CanRead.

@stakx stakx merged commit d30e61d into devlooped:develop Dec 10, 2017
@stakx stakx deleted the setupallproperties branch December 10, 2017 16:48
@kzu
Copy link
Member

kzu commented Dec 28, 2017

Glad to see this! Perf. should be comparable with v5, see the just-added PropertyBehavior :)

It's added wholesale to every loose mock by default so SetupAllProperties shouldn't be needed anymore even!

@stakx
Copy link
Contributor Author

stakx commented Dec 28, 2017

@kzu - should something similar be done in Moq 4? Auto setting up property behavior for get-settable properties of loose mocks seems entirely feasible; we could modify the interception pipeline such that when a property is queried or set for the first time, a property setup is created on the fly. (This might be perceived as a breaking change, though.)

@stakx
Copy link
Contributor Author

stakx commented Dec 28, 2017

Btw., regarding performance: Moq 4 performance is now back to what it was around version 4.2 (despite being more thread-safe and feature complete). I am expecting that it will get even faster once the remaining filed bugs are removed--essentially because fixing them will entail reducing expression tree compilation and doing plain expression tree rewriting instead, which is orders of magnitude faster. In particular, perf will greatly benefit on x64. I've prototyped the necessary changes, but it'll be quite difficult to pull this off without causing any minor breaking changes.

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.

None yet

2 participants