-
-
Notifications
You must be signed in to change notification settings - Fork 813
Moq 64bit bad performance #188
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
Comments
I found .net 4.6 RyuJIT improves performance by 20% |
This is the whole trace if I did it right. The MessageBox.Show was just a trigger for me take the profiler snapshot. As there is no CastleProxy code here it is a Moq only problem. I believe it is the LambdaCompiler.Compile from .Net which is slower in 64bit My TestCode defines an Interface with 40 methods and creates an InterfaceMock using Moq.
|
Not sure there's anything we can do to make LambdaExpression.Compile any |
I opened a Microsoft Connect issue |
I found the compiled lambda expressions can be cached. Now the unit tests execution time in my scenario is half the time! Can you implement a similar lambda compile cache mechanism or include Xtensive.Core? |
Including that component isn't likely to happen. Maybe you'd like to take on the challenge to submit a PR with that feature? |
Before we start optimizing, we should probably set up some kind of benchmarking (doesn't have to be part of the build process / CI, but we should have some kind of strategy) so we can actually measure the effect of any changes we make; see #388. |
I implemented lambda expression compile cache. It is quite easy and performance increases for the Mock.Of() calls. Moq needs a static class LambdaCompileCache for extension method lambda.CachedCompile(); Then all calls to lambda.Compile() in Moq code (6) must be replaced by lambda.CachedCompile() The Moq ExpressionComparer needs to be modified so that GetHashCode() returns not expression.GetHashCode() but expression.ToString().GetHashCode() namespace Moq
{
internal static class LambdaCompileCache
{
public static readonly IDictionary<Expression, object> Dictionary = new ConcurrentDictionary<Expression, object>(ExpressionEqualityComparer.Instance);
public static int HitCounter;
public static int MissCounter;
public static T CachedCompile<T>(this Expression<T> lambda)
{
T lambdaCompile;
object obj;
bool found = Dictionary.TryGetValue(lambda, out obj);
if (found)
{
HitCounter++;
lambdaCompile = (T)obj;
}
else
{
MissCounter++;
lambdaCompile = lambda.Compile();
Dictionary.Add(lambda, lambdaCompile);
}
return lambdaCompile;
}
}
internal class ExpressionEqualityComparer : IEqualityComparer<Expression>
{
public static readonly ExpressionEqualityComparer Instance = new ExpressionEqualityComparer();
private ExpressionEqualityComparer()
{
// Singleton
}
public bool Equals(Expression x, Expression y)
{
return ExpressionComparer.Default.Equals(x, y);
}
public int GetHashCode(Expression obj)
{
var str = obj.ToString();
return str.GetHashCode();
}
}
} |
@informatorius, thanks for working on this! Could you share some details on how you measured the performance improvement, and give an idea of how much performance is improved in a typical testing scenario? I guess if the performance gain is sufficiently big, we could incorporate this into Moq, however before you submit a PR I'd like to mention a few points:
There's probably more, but that's the couple points that came to mind while looking at your above code. If you'd like to submit a PR for this, I'd be happy if you could take the above points into account. :-) |
Good points! Most performance improve I see with lots of Mock.Of<> maybe because it has the most setups internally. One test run with 1000 production unit tests with many Mock.Of<> showed an improvement from runtime 3min to 2min (all with 1 thread sequentially) using Moq 4.8.1. So this is 30%. I see the same when I setup an IMyInterface with 50 properties and methods of type IAnotherInterface and do Mock.Of<> or SetupAllProperties() with DefaultValue=DefaultValue.Mock then setup time goes from 900ms to 600 ms (including testrunner start). But that may not be a typical scenario but it is how our unit tests work. I will now try to run all production unittests (10000) with cachedcompile moq to see if any tests fail and so breaks moq and how performance changes. Then I make pull request and we can discuss changes there. |
@informatorius - Thank you! It would be great if you could share the results of running your 10K production unit tests against your lambda compiler cache. |
I got no performance improvement when using new Mock instead of Mock.Of. So maybe if Mock.Of can be tweaked more then LambdaCachedCompile is not necessary. |
@informatorius - I have a few ideas about how to improve performance further. For example, it would be entirely feasible to reduce the amount of (slow) lambda compilation going on simply by re-designing how Moq processes setups. I originally planned to do this kind of re-design work for Moq 4.9.0, but since it's a fairly large effort, I have postponed this milestone until after Moq 5 is released. My feeling is that Moq 5 will have better performance than Moq 4 right from the start, so it might be better for people to adopt Moq 5 as soon as possible instead of waiting for a faster Moq 4. We'll see. :-) |
Closing this dormant issue, but marking it as "unresolved" so it can be easily found again. Please see #642 for details. If you'd like to pick this up and work on it, please post here briefly and we'll see what we can do! |
Since I build all targets in 64bit instead of 32bit, the unit test duration has doubled because Moq is 50% slower in 64bit. Can you confirm this problem?
Any ideas how to enhance performance in 64bit?
The text was updated successfully, but these errors were encountered: