Skip to content

Performance issue with large interfaces #1350

@rauhs

Description

@rauhs

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.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions