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

Performance issue with large interfaces #1350

Closed
rauhs opened this issue Jul 10, 2023 · 5 comments · Fixed by #1351
Closed

Performance issue with large interfaces #1350

rauhs opened this issue Jul 10, 2023 · 5 comments · Fixed by #1351

Comments

@rauhs
Copy link
Contributor

rauhs commented Jul 10, 2023

Hi,

this is basically #1128. Not sure how to re-open it.

Though, it's making Moq unusable for some tests. For us, we have a interface with over 500 slots (GetInterfaceMap) which is taking all the time.

Here is a quick PerfView trace of the runtime:

Name                                                                                                                                                    	Inc %	      Inc	Exc %	Exc	Fold	                             When	      First	       Last
 castle.core!Castle.DynamicProxy.AbstractInvocation.Proceed()                                                                                           	 98.7	   13,601	  0.0	  0	   0	 09999999999999999999999999999999	 10,774.284	 24,810.652
+ moq!Moq.CastleProxyFactory+Interceptor.Intercept(class Castle.DynamicProxy.IInvocation)                                                               	 98.7	   13,601	  0.0	  1	   0	 99999o99999999999999999999999999	 10,774.284	 24,810.652
 + moq!Moq.Mock.Moq.IInterceptor.Intercept(class Moq.Invocation)                                                                                        	 98.7	   13,597	  0.0	  0	   0	 99999o99999999999999999999999999	 10,774.284	 24,810.652
 |+ moq!FindAndExecuteMatchingSetup.Handle                                                                                                              	 98.7	   13,594	  0.0	  1	   0	 o9999999999999999999999999999999	 10,774.284	 24,810.652
 ||+ moq!SetupCollection.FindLast                                                                                                                       	 98.6	   13,585	  0.0	  0	   0	 o9999999999999999999999999999999	 10,774.284	 24,810.652
 |||+ moq!Moq.Setup.Matches(class Moq.Invocation)                                                                                                       	 98.6	   13,585	  0.0	  0	   0	 o9999999999999999999999999999999	 10,774.284	 24,810.652
 ||| + moq!Moq.MethodExpectation.IsMatch(class Moq.Invocation)                                                                                          	 98.6	   13,584	  0.0	  0	   0	 o9999999999999999999999999999999	 10,774.284	 24,810.652
 ||| |+ moq!Moq.MethodExpectation.IsOverride(class Moq.Invocation)                                                                                      	 98.6	   13,583	  0.0	  0	   0	 o9999999999999999999999999999999	 10,774.284	 24,810.652
 ||| ||+ moq!Moq.Invocation.get_MethodImplementation()                                                                                                  	 97.1	   13,387	  0.0	  0	   0	 o9899999999989999898999989999999	 10,776.284	 24,810.652
 ||| |||+ moq!Extensions.GetImplementingMethod                                                                                                          	 97.1	   13,387	  0.0	  0	   0	 o9899999999989999898999989999999	 10,776.284	 24,810.652
 ||| ||| + mscorlib.ni!RuntimeType.GetInterfaceMap                                                                                                      	 97.0	   13,368	  0.1	 15	   0	 o9899999999989899898999989999999	 10,776.284	 24,810.652
 ||| ||| |+ mscorlib.ni!RuntimeType.GetMethodBase                                                                                                       	 49.2	    6,773	  0.3	 35	   0	 o5444554444444454444444545445454	 10,776.284	 24,810.652
 ||| ||| |+ mscorlib.ni!DomainNeutralILStubClass.IL_STUB_PInvoke(System.RuntimeTypeHandle, System.RuntimeTypeHandle, System.RuntimeMethodHandleInternal)	 44.8	    6,177	  0.2	 27	   0	 o4444444434444344444443434444344	 10,787.290	 24,808.652
Name

The fix is pretty simple:

// Moq.Extensions:
		private static readonly ConcurrentDictionary<Tuple<Type, Type>, InterfaceMapping> mappingsCache = new ();

		private static InterfaceMapping GetInterfaceMap(Type type, Type interfaceType)
		{
			return mappingsCache.GetOrAdd(Tuple.Create(type, interfaceType), tuple => tuple.Item1.GetInterfaceMap(tuple.Item2));
		}

This makes the above run in a few ms as opposed to 14 seconds.

@stakx
Copy link
Contributor

stakx commented Jul 20, 2023

Thanks for posting a possible solution. Before we apply it, could we look at a test case that demonstrates the problem? Could you post a minimally complete repro code example?

@rauhs
Copy link
Contributor Author

rauhs commented Jul 21, 2023

@stakx Reproducer is at:

https://gist.github.com/rauhs/4cbe672e26dd6727e84f7b96c68dcf1f

Without optimization: 24seconds

With opt: 7ms.

(even with "only" 50 properties we're at 1s of runtime vs 5ms, so huge performance penalty even for non-gigantic interfaces)

@rauhs
Copy link
Contributor Author

rauhs commented Jul 31, 2023

Is there anything we can do to expedite this?

rauhs added a commit to rauhs/moq that referenced this issue Aug 6, 2023
Fixes devlooped#1350.

Revert "Improve performance for mocking interfaces: Cache GetInterfaceMap"

This reverts commit 7b8a5a04b5beb97d24ef9ef6e02e6ccd278795db.

Improve performance for mocking interfaces: Cache GetInterfaceMap

Fixes devlooped#1350.
rauhs pushed a commit to rauhs/moq that referenced this issue Aug 6, 2023
rauhs pushed a commit to rauhs/moq that referenced this issue Aug 8, 2023
rauhs pushed a commit to rauhs/moq that referenced this issue Aug 9, 2023
rauhs pushed a commit to rauhs/moq that referenced this issue Aug 9, 2023
@kzu kzu closed this as completed in #1351 Aug 16, 2023
@aleksandr-grechko
Copy link

aleksandr-grechko commented Nov 28, 2023

@kzu Could you release new version of Moq with this fix? It would allow us to significantly decrease time of unit tests execution for our project.

@kzu
Copy link
Member

kzu commented Nov 28, 2023

Done! https://github.com/devlooped/moq/releases/tag/v4.20.70

@devlooped devlooped locked and limited conversation to collaborators Sep 4, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants