Improve speed of Object method comparisons when applying default Fake rules
See original GitHub issueBackstory - I found my tests were not running well today. A few tests ran, and then everything stopped, while my CPU fan started to whine, so I fired up the debugger, to see what was what, and found most threads doing nothing, but this thread looked like it was doing something. Not once, but several times…
So from that symptom seems like it could be a bit of a performance hotspot in this scenario where I was intercepting the same call (out of various possible calls) over and over again.
One minor thing in the code today I can suggest changing: this test should probably be reversed
if (rule.Rule.IsApplicableTo(fakeObjectCall) && rule.HasNotBeenCalledSpecifiedNumberOfTimes())
—>
if (rule.HasNotBeenCalledSpecifiedNumberOfTimes() && rule.Rule.IsApplicableTo(fakeObjectCall))
Since doing tests on integers is fast.
Another idea of what to change would be to optimize ObjectMemberRule.IsApplicableTo
. In the scenario where its called repeatedly with the same fakeObjectCallMethod, perhaps the result of ‘ObjectMemberRule.IsApplicableTo’ could be cached on the fakeObjectCall.Method
somehow, also maybe some cheaper heuristics should be checked before calling HasSameBaseMethodAs
– does fakeObjectCall.Method.Name
start with “E”? It could be “Equals”. “T”? It could be “ToString”. “G”? It could be “GetHashCode”. None of those? None of those.
public override bool IsApplicableTo(IFakeObjectCall fakeObjectCall) =>
fakeObjectCall.Method.HasSameBaseMethodAs(EqualsMethod) ||
fakeObjectCall.Method.HasSameBaseMethodAs(ToStringMethod) ||
fakeObjectCall.Method.HasSameBaseMethodAs(GetHashCodeMethod);
public static bool HasSameBaseMethodAs(this MethodInfo first, MethodInfo second)
{
var baseOfFirst = GetBaseDefinition(first);
var baseOfSecond = GetBaseDefinition(second);
return baseOfFirst.IsSameMethodAs(baseOfSecond);
}
Original repro with:
<package id="Castle.Core" version="4.3.1" targetFramework="net472" />
<package id="FakeItEasy" version="6.2.1" targetFramework="net472" />
Stack:
mscorlib.dll!System.RuntimeType.RuntimeTypeCache.MemberInfoCache<System.Reflection.RuntimeMethodInfo>.Insert(ref System.Reflection.RuntimeMethodInfo[] list, string name, System.RuntimeType.MemberListType listType) Unknown
mscorlib.dll!System.RuntimeType.RuntimeTypeCache.MemberInfoCache<System.Reflection.RuntimeMethodInfo>.AddMethod(System.RuntimeType declaringType, System.RuntimeMethodHandleInternal method, System.RuntimeType.RuntimeTypeCache.CacheType cacheType) Unknown
mscorlib.dll!System.RuntimeType.GetMethodBase(System.RuntimeType reflectedType, System.RuntimeMethodHandleInternal methodHandle) Unknown
mscorlib.dll!System.Reflection.RuntimeMethodInfo.GetBaseDefinition() Unknown
> FakeItEasy.dll!FakeItEasy.MethodInfoExtensions.GetBaseDefinition(System.Reflection.MethodInfo method) Line 82 C#
FakeItEasy.dll!FakeItEasy.MethodInfoExtensions.HasSameBaseMethodAs(System.Reflection.MethodInfo first, System.Reflection.MethodInfo second) Line 69 C#
FakeItEasy.dll!FakeItEasy.Core.FakeManager.ObjectMemberRule.IsApplicableTo(FakeItEasy.Core.IFakeObjectCall fakeObjectCall) Line 12 C#
FakeItEasy.dll!FakeItEasy.Core.FakeManager.ApplyBestRule(FakeItEasy.Core.IInterceptedFakeObjectCall fakeObjectCall) Line 268 C#
FakeItEasy.dll!FakeItEasy.Core.FakeManager.FakeItEasy.Core.IFakeCallProcessor.Process(FakeItEasy.Core.InterceptedFakeObjectCall fakeObjectCall) Line 173 C#
FakeItEasy.dll!FakeItEasy.Creation.CastleDynamicProxy.CastleDynamicProxyGenerator.ProxyInterceptor.Intercept(Castle.DynamicProxy.IInvocation invocation) Line 193 C#
Castle.Core.dll!Castle.DynamicProxy.AbstractInvocation.Proceed() Unknown
DynamicProxyGenAssembly2!<Unknown function> Unknown
ModernHub.dll!ModernHub.Hub.PollForMessagesAsync(System.Threading.CancellationToken token, bool returnWhenEmpty) Line 178 C#
Issue Analytics
- State:
- Created 2 years ago
- Comments:9 (7 by maintainers)
Top GitHub Comments
I had some benchmarking code lying around. Ran 6.1.0 and 6.2.0 through it, timing this test
on netcoreapp2.1, because it was convenient.
This could contribute to some slowness. Less than doubling your test times, I would think, and then only if it’s all you do…
Darn. As I type, it occurs to me I should’ve benchmarked a non-
Object
method. I’ll be back.Thanks for reporting and analyzing this issue, @TimLovellSmith. Look for your name in the release notes! 🐶