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

Mock.Of<T>() is much slower than new Mock<T> (up to several orders of magnitude) #547

Closed
stakx opened this issue Dec 8, 2017 · 5 comments

Comments

@stakx
Copy link
Contributor

stakx commented Dec 8, 2017

No description provided.

@stakx stakx added this to the v4.9.0 milestone Dec 8, 2017
@stakx stakx changed the title Speed up Mock.Of<T>() by making SetupProperties lazy Mock.Of<T>() is much slower than new Mock<T> (up to several orders of magnitude) Dec 10, 2017
@informatorius
Copy link
Contributor

I added a lambda compile cache to Moq and Mock.Of() performance improves.
#188

@stakx stakx removed this from the v4.9.0 milestone Feb 26, 2018
@informatorius
Copy link
Contributor

informatorius commented Feb 27, 2018

Is my understanding right that Mock.Of() equals

var mock = new Mock<Interface>();
mock.SetupAllProperties();
return mock.Object; 

If true can we just replace the slow code inside Mock.Of by these faster code lines?

@stakx
Copy link
Contributor Author

stakx commented Feb 27, 2018

@informatorius - I'd have to check whether the current implementation is equivalent to what you've shown above, but it seems plausible. Of course this optimisation won't be possible for the parameterized method overload of Mock.Of. I'll get back to you about that.

@stakx
Copy link
Contributor Author

stakx commented Feb 27, 2018

@informatorius - Yes, as far as I can tell by stepping through Moq internals, the code you've shown is what Mock.Of<T>() ends up doing once you take away the whole IQueryable provider machinery.

I've run a quick and dirty BenchmarkDotNet test:

[Benchmark(Baseline = true)]
public IFoo MockOfT_as_is()
{
	return Mock.Of<IFoo>();
}

[Benchmark]
public IFoo MockOfT_simplified()
{
	var mock = new Mock<IFoo>();
	mock.SetupAllProperties();
	return mock.Object;
}

public interface IFoo
{
	IFoo AnotherFoo { get; }
	string SomeString { get; }
	Task<string> GetSomeStringAsync();
}

with this result:

BenchmarkDotNet=v0.10.12, OS=Windows 10 Redstone 3 [1709, Fall Creators Update] (10.0.16299.192)
Intel Core i5-8250U CPU 1.60GHz (Kaby Lake R), 1 CPU, 8 logical cores and 4 physical cores
Frequency=1757812 Hz, Resolution=568.8891 ns, Timer=TSC
  [Host]     : .NET Framework 4.7 (CLR 4.0.30319.42000), 32bit LegacyJIT-v4.7.2600.0
  DefaultJob : .NET Framework 4.7 (CLR 4.0.30319.42000), 32bit LegacyJIT-v4.7.2600.0


             Method |      Mean |     Error |    StdDev | Scaled |
------------------- |----------:|----------:|----------:|-------:|
      MockOfT_as_is | 338.44 us | 1.3457 us | 1.0506 us |   1.00 |
 MockOfT_simplified |  20.95 us | 0.0533 us | 0.0472 us |   0.06 |

So the new implementation might speed up Mock.Of<T>() by something between 1 and 2 orders of magnitude. (The exact speed-up depends on what type you're mocking because the amount of work SetupAllProperties does depends on the number and types of properties it finds.)

Would you like to submit a PR that replaces the current implementation of Mock.Of<T>() with the code you've shown above?

If so, please take the following into account:

  • Please include a comment in the new implementation explaining that the new code is an optimization / simplification of the current code (return Mocks.CreateMockQuery<T>().First<T>()). It seems important to me to keep a reference to the old code, so that it remains easy to see the parallel to the other, parameterized method overload Mock.Of<T>(predicate).

  • Add a new entry to CHANGELOG.md describing your change and what it aims for. Put your entry under a Changed heading.

@stakx
Copy link
Contributor Author

stakx commented Mar 2, 2018

Closing this issue as the performance of Mock.Of<T>() has now been taken care of. Thank you, @informatorius.

There's still the other method overload, Mock.Of<T>(Func<T, bool>) to deal with. Improving its performance would likely require some heavy refactoring regarding how Moq processes setups and LINQ setup expressions. This will probably deserve its own dedicated issue.

@stakx stakx closed this as completed Mar 2, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants