Skip to content

Commit 556baf9

Browse files
committed
Optimize generic MethodInfo for Func<TResult>
1 parent ef12e13 commit 556baf9

File tree

15 files changed

+273
-70
lines changed

15 files changed

+273
-70
lines changed
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
#nullable enable
6+
7+
using System;
8+
using System.Linq.Expressions;
9+
using System.Reflection;
10+
using Microsoft.ML.Runtime;
11+
12+
namespace Microsoft.ML.Internal.Utilities
13+
{
14+
/// <summary>
15+
/// Represents the <see cref="MethodInfo"/> for a generic function corresponding to <see cref="Func{TResult}"/>,
16+
/// with the following characteristics:
17+
///
18+
/// <list type="bullet">
19+
/// <item><description>The method is an instance method on an object of type <typeparamref name="TTarget"/>.</description></item>
20+
/// <item><description>One generic type argument.</description></item>
21+
/// <item><description>A return value of <typeparamref name="TResult"/>.</description></item>
22+
/// </list>
23+
/// </summary>
24+
/// <typeparam name="TTarget">The type of the receiver of the instance method.</typeparam>
25+
/// <typeparam name="TResult">The type of the return value of the method.</typeparam>
26+
internal sealed class FuncInstanceMethodInfo1<TTarget, TResult> : FuncMethodInfo1<TResult>
27+
where TTarget : class
28+
{
29+
private static readonly string _targetTypeCheckMessage = $"Should have a target type of '{typeof(TTarget)}'";
30+
31+
public FuncInstanceMethodInfo1(Func<TResult> function)
32+
: this(function.Method)
33+
{
34+
}
35+
36+
private FuncInstanceMethodInfo1(MethodInfo methodInfo)
37+
: base(methodInfo)
38+
{
39+
Contracts.CheckParam(!GenericMethodDefinition.IsStatic, nameof(methodInfo), "Should be an instance method");
40+
Contracts.CheckParam(GenericMethodDefinition.DeclaringType == typeof(TTarget), nameof(methodInfo), _targetTypeCheckMessage);
41+
}
42+
43+
/// <summary>
44+
/// Creates a <see cref="FuncInstanceMethodInfo1{TTarget, TResult}"/> representing the <see cref="MethodInfo"/> for
45+
/// a generic instance method. This helper method allows the instance to be created prior to the creation of any
46+
/// instances of the target type. The following example shows the creation of an instance representing the
47+
/// <see cref="object.GetHashCode"/> method:
48+
///
49+
/// <code>
50+
/// FuncInstanceMethodInfo1&lt;object, int&gt;.Create(obj => obj.GetHashCode)
51+
/// </code>
52+
/// </summary>
53+
/// <param name="expression">The expression which creates the delegate for an instance of the target type.</param>
54+
/// <returns>A <see cref="FuncInstanceMethodInfo1{TTarget, TResult}"/> representing the <see cref="MethodInfo"/>
55+
/// for the generic instance method.</returns>
56+
public static FuncInstanceMethodInfo1<TTarget, TResult> Create(Expression<Func<TTarget, Func<TResult>>> expression)
57+
{
58+
if (!(expression is { Body: UnaryExpression { Operand: MethodCallExpression methodCallExpression } }))
59+
{
60+
throw Contracts.ExceptParam(nameof(expression), "Unexpected expression form");
61+
}
62+
63+
// Verify that we are calling MethodInfo.CreateDelegate(Type, object)
64+
Contracts.CheckParam(methodCallExpression.Method.DeclaringType == typeof(MethodInfo), nameof(expression), "Unexpected expression form");
65+
Contracts.CheckParam(methodCallExpression.Method.Name == nameof(MethodInfo.CreateDelegate), nameof(expression), "Unexpected expression form");
66+
Contracts.CheckParam(methodCallExpression.Method.GetParameters().Length == 2, nameof(expression), "Unexpected expression form");
67+
Contracts.CheckParam(methodCallExpression.Method.GetParameters()[0].ParameterType == typeof(Type), nameof(expression), "Unexpected expression form");
68+
Contracts.CheckParam(methodCallExpression.Method.GetParameters()[1].ParameterType == typeof(object), nameof(expression), "Unexpected expression form");
69+
70+
// Verify that we are creating a delegate of type Func<TRet>
71+
Contracts.CheckParam(methodCallExpression.Arguments.Count == 2, nameof(expression), "Unexpected expression form");
72+
Contracts.CheckParam(methodCallExpression.Arguments[0] is ConstantExpression, nameof(expression), "Unexpected expression form");
73+
Contracts.CheckParam(((ConstantExpression)methodCallExpression.Arguments[0]).Type == typeof(Type), nameof(expression), "Unexpected expression form");
74+
Contracts.CheckParam((Type)((ConstantExpression)methodCallExpression.Arguments[0]).Value == typeof(Func<TResult>), nameof(expression), "Unexpected expression form");
75+
Contracts.CheckParam(methodCallExpression.Arguments[1] is ParameterExpression, nameof(expression), "Unexpected expression form");
76+
Contracts.CheckParam(methodCallExpression.Arguments[1] == expression.Parameters[0], nameof(expression), "Unexpected expression form");
77+
78+
// Check the MethodInfo
79+
Contracts.CheckParam(methodCallExpression.Object is ConstantExpression, nameof(expression), "Unexpected expression form");
80+
Contracts.CheckParam(((ConstantExpression)methodCallExpression.Object).Type == typeof(MethodInfo), nameof(expression), "Unexpected expression form");
81+
82+
var methodInfo = (MethodInfo)((ConstantExpression)methodCallExpression.Object).Value;
83+
Contracts.CheckParam(expression.Body is UnaryExpression, nameof(expression), "Unexpected expression form");
84+
Contracts.CheckParam(((UnaryExpression)expression.Body).Operand is MethodCallExpression, nameof(expression), "Unexpected expression form");
85+
86+
return new FuncInstanceMethodInfo1<TTarget, TResult>(methodInfo);
87+
}
88+
}
89+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
#nullable enable
6+
7+
using System;
8+
using System.Collections.Immutable;
9+
using System.Reflection;
10+
using Microsoft.ML.Runtime;
11+
12+
namespace Microsoft.ML.Internal.Utilities
13+
{
14+
/// <summary>
15+
/// Represents the <see cref="MethodInfo"/> for a generic function corresponding to <see cref="Func{TResult}"/>,
16+
/// with the following characteristics:
17+
///
18+
/// <list type="bullet">
19+
/// <item><description>One generic type argument.</description></item>
20+
/// <item><description>A return value of <typeparamref name="TResult"/>.</description></item>
21+
/// </list>
22+
/// </summary>
23+
/// <typeparam name="TResult">The type of the return value of the method.</typeparam>
24+
internal abstract class FuncMethodInfo1<TResult> : FuncMethodInfo<TResult>
25+
{
26+
private ImmutableDictionary<Type, MethodInfo> _instanceMethodInfo;
27+
28+
private protected FuncMethodInfo1(MethodInfo methodInfo)
29+
: base(methodInfo)
30+
{
31+
_instanceMethodInfo = ImmutableDictionary<Type, MethodInfo>.Empty;
32+
33+
Contracts.CheckParam(GenericMethodDefinition.GetGenericArguments().Length == 1, nameof(methodInfo),
34+
"Should have exactly one generic type parameter but does not");
35+
}
36+
37+
public MethodInfo MakeGenericMethod(Type typeArg1)
38+
{
39+
return ImmutableInterlocked.GetOrAdd(
40+
ref _instanceMethodInfo,
41+
typeArg1,
42+
(typeArg, methodInfo) => methodInfo.MakeGenericMethod(typeArg),
43+
GenericMethodDefinition);
44+
}
45+
}
46+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
#nullable enable
6+
7+
using System.Reflection;
8+
using Microsoft.ML.Runtime;
9+
10+
namespace Microsoft.ML.Internal.Utilities
11+
{
12+
internal abstract class FuncMethodInfo<TResult>
13+
{
14+
private protected FuncMethodInfo(MethodInfo methodInfo)
15+
{
16+
Contracts.CheckValue(methodInfo, nameof(methodInfo));
17+
Contracts.CheckParam(methodInfo.IsGenericMethod, nameof(methodInfo), "Should be generic but is not");
18+
19+
GenericMethodDefinition = methodInfo.GetGenericMethodDefinition();
20+
Contracts.CheckParam(GenericMethodDefinition.ReturnType == typeof(TResult), nameof(methodInfo), "Cannot be generic on return type");
21+
}
22+
23+
protected MethodInfo GenericMethodDefinition { get; }
24+
}
25+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
#nullable enable
6+
7+
using System;
8+
using System.Reflection;
9+
using Microsoft.ML.Runtime;
10+
11+
namespace Microsoft.ML.Internal.Utilities
12+
{
13+
/// <summary>
14+
/// Represents the <see cref="MethodInfo"/> for a generic function corresponding to <see cref="Func{TResult}"/>,
15+
/// with the following characteristics:
16+
///
17+
/// <list type="bullet">
18+
/// <item><description>The method is static.</description></item>
19+
/// <item><description>One generic type argument.</description></item>
20+
/// <item><description>A return value of <typeparamref name="TResult"/>.</description></item>
21+
/// </list>
22+
/// </summary>
23+
/// <typeparam name="TResult">The type of the return value of the method.</typeparam>
24+
internal sealed class FuncStaticMethodInfo1<TResult> : FuncMethodInfo1<TResult>
25+
{
26+
public FuncStaticMethodInfo1(Func<TResult> function)
27+
: base(function.Method)
28+
{
29+
Contracts.CheckParam(GenericMethodDefinition.IsStatic, nameof(function), "Should be a static method");
30+
}
31+
}
32+
}

src/Microsoft.ML.Core/Utilities/Utils.cs

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -988,20 +988,32 @@ private static MethodInfo MarshalInvokeCheckAndCreate<TRet>(Type[] genArgs, Dele
988988
/// Because it is strongly typed, this can only be applied to methods whose return type
989989
/// is known at compile time, that is, that do not depend on the type parameter of the method itself.
990990
/// </summary>
991-
/// <typeparam name="TRet">The return value</typeparam>
991+
/// <typeparam name="TTarget">The type of the receiver of the instance method.</typeparam>
992+
/// <typeparam name="TResult">The type of the return value of the method.</typeparam>
992993
/// <param name="func">A delegate that should be a generic method with a single type parameter.
993994
/// The generic method definition will be extracted, then a new method will be created with the
994995
/// given type parameter, then the method will be invoked.</param>
996+
/// <param name="target">The target of the invocation.</param>
995997
/// <param name="genArg">The new type parameter for the generic method</param>
996998
/// <returns>The return value of the invoked function</returns>
997-
public static TRet MarshalInvoke<TRet>(Func<TRet> func, Type genArg)
999+
public static TResult MarshalInvoke<TTarget, TResult>(FuncInstanceMethodInfo1<TTarget, TResult> func, TTarget target, Type genArg)
1000+
where TTarget : class
9981001
{
999-
var meth = MarshalInvokeCheckAndCreate<TRet>(genArg, func);
1000-
return (TRet)meth.Invoke(func.Target, null);
1002+
var meth = func.MakeGenericMethod(genArg);
1003+
return (TResult)meth.Invoke(target, null);
1004+
}
1005+
1006+
/// <summary>
1007+
/// A static version of <see cref="MarshalInvoke{TTarget, TResult}(FuncInstanceMethodInfo1{TTarget, TResult}, TTarget, Type)"/>.
1008+
/// </summary>
1009+
public static TResult MarshalInvoke<TResult>(FuncStaticMethodInfo1<TResult> func, Type genArg)
1010+
{
1011+
var meth = func.MakeGenericMethod(genArg);
1012+
return (TResult)meth.Invoke(null, null);
10011013
}
10021014

10031015
/// <summary>
1004-
/// A one-argument version of <see cref="MarshalInvoke{TRet}"/>.
1016+
/// A one-argument version of <see cref="MarshalInvoke{TTarget, TResult}(FuncInstanceMethodInfo1{TTarget, TResult}, TTarget, Type)"/>.
10051017
/// </summary>
10061018
public static TRet MarshalInvoke<TArg1, TRet>(Func<TArg1, TRet> func, Type genArg, TArg1 arg1)
10071019
{
@@ -1010,7 +1022,7 @@ public static TRet MarshalInvoke<TArg1, TRet>(Func<TArg1, TRet> func, Type genAr
10101022
}
10111023

10121024
/// <summary>
1013-
/// A two-argument version of <see cref="MarshalInvoke{TRet}"/>.
1025+
/// A two-argument version of <see cref="MarshalInvoke{TTarget, TResult}(FuncInstanceMethodInfo1{TTarget, TResult}, TTarget, Type)"/>.
10141026
/// </summary>
10151027
public static TRet MarshalInvoke<TArg1, TArg2, TRet>(Func<TArg1, TArg2, TRet> func, Type genArg, TArg1 arg1, TArg2 arg2)
10161028
{
@@ -1019,7 +1031,7 @@ public static TRet MarshalInvoke<TArg1, TArg2, TRet>(Func<TArg1, TArg2, TRet> fu
10191031
}
10201032

10211033
/// <summary>
1022-
/// A three-argument version of <see cref="MarshalInvoke{TRet}"/>.
1034+
/// A three-argument version of <see cref="MarshalInvoke{TTarget, TResult}(FuncInstanceMethodInfo1{TTarget, TResult}, TTarget, Type)"/>.
10231035
/// </summary>
10241036
public static TRet MarshalInvoke<TArg1, TArg2, TArg3, TRet>(Func<TArg1, TArg2, TArg3, TRet> func, Type genArg,
10251037
TArg1 arg1, TArg2 arg2, TArg3 arg3)
@@ -1029,7 +1041,7 @@ public static TRet MarshalInvoke<TArg1, TArg2, TArg3, TRet>(Func<TArg1, TArg2, T
10291041
}
10301042

10311043
/// <summary>
1032-
/// A four-argument version of <see cref="MarshalInvoke{TRet}"/>.
1044+
/// A four-argument version of <see cref="MarshalInvoke{TTarget, TResult}(FuncInstanceMethodInfo1{TTarget, TResult}, TTarget, Type)"/>.
10331045
/// </summary>
10341046
public static TRet MarshalInvoke<TArg1, TArg2, TArg3, TArg4, TRet>(Func<TArg1, TArg2, TArg3, TArg4, TRet> func,
10351047
Type genArg, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4)
@@ -1039,7 +1051,7 @@ public static TRet MarshalInvoke<TArg1, TArg2, TArg3, TArg4, TRet>(Func<TArg1, T
10391051
}
10401052

10411053
/// <summary>
1042-
/// A five-argument version of <see cref="MarshalInvoke{TRet}"/>.
1054+
/// A five-argument version of <see cref="MarshalInvoke{TTarget, TResult}(FuncInstanceMethodInfo1{TTarget, TResult}, TTarget, Type)"/>.
10431055
/// </summary>
10441056
public static TRet MarshalInvoke<TArg1, TArg2, TArg3, TArg4, TArg5, TRet>(Func<TArg1, TArg2, TArg3, TArg4, TArg5, TRet> func,
10451057
Type genArg, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4, TArg5 arg5)
@@ -1049,7 +1061,7 @@ public static TRet MarshalInvoke<TArg1, TArg2, TArg3, TArg4, TArg5, TRet>(Func<T
10491061
}
10501062

10511063
/// <summary>
1052-
/// A six-argument version of <see cref="MarshalInvoke{TRet}"/>.
1064+
/// A six-argument version of <see cref="MarshalInvoke{TTarget, TResult}(FuncInstanceMethodInfo1{TTarget, TResult}, TTarget, Type)"/>.
10531065
/// </summary>
10541066
public static TRet MarshalInvoke<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TRet>(Func<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TRet> func,
10551067
Type genArg, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4, TArg5 arg5, TArg6 arg6)
@@ -1059,7 +1071,7 @@ public static TRet MarshalInvoke<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TRet>
10591071
}
10601072

10611073
/// <summary>
1062-
/// A seven-argument version of <see cref="MarshalInvoke{TRet}"/>.
1074+
/// A seven-argument version of <see cref="MarshalInvoke{TTarget, TResult}(FuncInstanceMethodInfo1{TTarget, TResult}, TTarget, Type)"/>.
10631075
/// </summary>
10641076
public static TRet MarshalInvoke<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TRet>(Func<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TRet> func,
10651077
Type genArg, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4, TArg5 arg5, TArg6 arg6, TArg7 arg7)
@@ -1069,7 +1081,7 @@ public static TRet MarshalInvoke<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7
10691081
}
10701082

10711083
/// <summary>
1072-
/// An eight-argument version of <see cref="MarshalInvoke{TRet}"/>.
1084+
/// An eight-argument version of <see cref="MarshalInvoke{TTarget, TResult}(FuncInstanceMethodInfo1{TTarget, TResult}, TTarget, Type)"/>.
10731085
/// </summary>
10741086
public static TRet MarshalInvoke<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TRet>(Func<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TRet> func,
10751087
Type genArg, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4, TArg5 arg5, TArg6 arg6, TArg7 arg7, TArg8 arg8)
@@ -1079,7 +1091,7 @@ public static TRet MarshalInvoke<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7
10791091
}
10801092

10811093
/// <summary>
1082-
/// A nine-argument version of <see cref="MarshalInvoke{TRet}"/>.
1094+
/// A nine-argument version of <see cref="MarshalInvoke{TTarget, TResult}(FuncInstanceMethodInfo1{TTarget, TResult}, TTarget, Type)"/>.
10831095
/// </summary>
10841096
public static TRet MarshalInvoke<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TRet>(
10851097
Func<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TRet> func,
@@ -1090,7 +1102,7 @@ public static TRet MarshalInvoke<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7
10901102
}
10911103

10921104
/// <summary>
1093-
/// A ten-argument version of <see cref="MarshalInvoke{TRet}"/>.
1105+
/// A ten-argument version of <see cref="MarshalInvoke{TTarget, TResult}(FuncInstanceMethodInfo1{TTarget, TResult}, TTarget, Type)"/>.
10941106
/// </summary>
10951107
public static TRet MarshalInvoke<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TArg10, TRet>(
10961108
Func<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TArg10, TRet> func,
@@ -1101,7 +1113,7 @@ public static TRet MarshalInvoke<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7
11011113
}
11021114

11031115
/// <summary>
1104-
/// A 1 argument and n type version of <see cref="MarshalInvoke{TRet}"/>.
1116+
/// A 1 argument and n type version of <see cref="MarshalInvoke{TTarget, TResult}(FuncInstanceMethodInfo1{TTarget, TResult}, TTarget, Type)"/>.
11051117
/// </summary>
11061118
public static TRet MarshalInvoke<TArg1, TRet>(
11071119
Func<TArg1, TRet> func,
@@ -1112,7 +1124,7 @@ public static TRet MarshalInvoke<TArg1, TRet>(
11121124
}
11131125

11141126
/// <summary>
1115-
/// A 2 argument and n type version of <see cref="MarshalInvoke{TRet}"/>.
1127+
/// A 2 argument and n type version of <see cref="MarshalInvoke{TTarget, TResult}(FuncInstanceMethodInfo1{TTarget, TResult}, TTarget, Type)"/>.
11161128
/// </summary>
11171129
public static TRet MarshalInvoke<TArg1, TArg2, TRet>(
11181130
Func<TArg1, TArg2, TRet> func,
@@ -1147,7 +1159,7 @@ private static MethodInfo MarshalActionInvokeCheckAndCreate(Type[] typeArguments
11471159
}
11481160

11491161
/// <summary>
1150-
/// This is akin to <see cref="MarshalInvoke{TRet}(Func{TRet}, Type)"/>, except applied to
1162+
/// This is akin to <see cref="MarshalInvoke{TTarget, TResult}(FuncInstanceMethodInfo1{TTarget, TResult}, TTarget, Type)"/>, except applied to
11511163
/// <see cref="Action"/> instead of <see cref="Func{TRet}"/>.
11521164
/// </summary>
11531165
/// <param name="act">A delegate that should be a generic method with a single type parameter.

src/Microsoft.ML.Data/DataLoadSave/Binary/BinaryLoader.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1240,6 +1240,9 @@ public DataViewRowCursor[] GetRowCursorSet(IEnumerable<DataViewSchema.Column> co
12401240

12411241
private sealed class Cursor : RootCursorBase
12421242
{
1243+
private static readonly FuncInstanceMethodInfo1<Cursor, Delegate> _noRowGetterMethodInfo
1244+
= FuncInstanceMethodInfo1<Cursor, Delegate>.Create(target => target.NoRowGetter<int>);
1245+
12431246
private readonly BinaryLoader _parent;
12441247
private readonly int[] _colToActivesIndex;
12451248
private readonly TableOfContentsEntry[] _actives;
@@ -2071,7 +2074,7 @@ public override ValueGetter<TValue> GetGetter<TValue>(DataViewSchema.Column colu
20712074
/// a delegate that simply always throws.
20722075
/// </summary>
20732076
private Delegate GetNoRowGetter(DataViewType type)
2074-
=> Utils.MarshalInvoke(NoRowGetter<int>, type.RawType);
2077+
=> Utils.MarshalInvoke(_noRowGetterMethodInfo, this, type.RawType);
20752078

20762079
private Delegate NoRowGetter<T>()
20772080
{

0 commit comments

Comments
 (0)