question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Improve speed of Object method comparisons when applying default Fake rules

See original GitHub issue

Backstory - 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:closed
  • Created 2 years ago
  • Comments:9 (7 by maintainers)

github_iconTop GitHub Comments

1reaction
blairconradcommented, Jun 24, 2021

I had some benchmarking code lying around. Ran 6.1.0 and 6.2.0 through it, timing this test

fake.Equals(this);

on netcoreapp2.1, because it was convenient.

Method Job NuGetReferences Mean Error StdDev Ratio RatioSD
Equals 6.1.0 FakeItEasy 6.1.0 5.262 us 0.4512 us 0.2360 us 1.00 0.00
Equals 6.2.0 FakeItEasy 6.2.0 10.134 us 0.4143 us 0.2465 us 1.93 0.13

image

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.

0reactions
blairconradcommented, Sep 1, 2021

Thanks for reporting and analyzing this issue, @TimLovellSmith. Look for your name in the release notes! 🐶

Read more comments on GitHub >

github_iconTop Results From Across the Web

Comparing Java objects with equals() and hashcode()
Rules for object comparison. When a hashcode() comparison returns false , the equals() method must also return false.
Read more >
Object.Equals Method (System)
The object to compare with the current object. Returns. Boolean. true if the specified object is equal to the current object; otherwise, false...
Read more >
C# Object Equality in .NET
Explore ways to override the default concepts of object equality, ... If they do, it returns true; otherwise, it returns false.
Read more >
Why do I need to override the equals and hashCode ...
Since they don't override equals() and hashCode() unlike String class, equals() will return false when you compare two different objects even ...
Read more >
Comparison in Python is Not as Simple as You May Think
The simple rule is that if you want to know whether object A and B are the same “car” , then use ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found