8000 Merge pull request #24 from Martin-Molinero/performance-qc2925-improv… · saaib/pythonnet@d047597 · GitHub
[go: up one dir, main page]

Skip to content

Commit d047597

Browse files
authored
Merge pull request QuantConnect#24 from Martin-Molinero/performance-qc2925-improve-indicator-benchmark
Improve Indicator Performance Benchmark
2 parents daddc68 + ab0d1b3 commit d047597

File tree

3 files changed

+117
-13
lines changed

3 files changed

+117
-13
lines changed

src/runtime/interop.cs

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
using System;
22
using System.Collections;
3-
using System.Collections.Specialized;
3+
using System.Collections.Generic;
44
using System.Runtime.InteropServices;
55
using System.Reflection;
66
using System.Text;
@@ -88,8 +88,7 @@ static ObjectOffset()
8888

8989
public static int magic(IntPtr ob)
9090
{
91-
if ((Runtime.PyObject_TypeCheck(ob, Exceptions.BaseException) ||
92-
(Runtime.PyType_Check(ob) && Runtime.PyType_IsSubtype(ob, Exceptions.BaseException))))
91+
if (IsException(ob))
9392
{
9493
return ExceptionOffset.ob_data;
9594
}
@@ -98,8 +97,7 @@ public static int magic(IntPtr ob)
9897

9998
public static int DictOffset(IntPtr ob)
10099
{
101-
if ((Runtime.PyObject_TypeCheck(ob, Exceptions.BaseException) ||
102-
(Runtime.PyType_Check(ob) && Runtime.PyType_IsSubtype(ob, Exceptions.BaseException))))
100+
if (IsException(ob))
103101
{
104102
return ExceptionOffset.ob_dict;
105103
}
@@ -108,8 +106,7 @@ public static int DictOffset(IntPtr ob)
108106

109107
public static int Size(IntPtr ob)
110108
{
111-
if ((Runtime.PyObject_TypeCheck(ob, Exceptions.BaseException) ||
112-
(Runtime.PyType_Check(ob) && Runtime.PyType_IsSubtype(ob, Exceptions.BaseException))))
109+
if (IsException(ob))
113110
{
114111
return ExceptionOffset.Size();
115112
}
@@ -128,6 +125,21 @@ public static int Size(IntPtr ob)
128125
public static int ob_type;
129126
private static int ob_dict;
130127
private static int ob_data;
128+
private static readonly Dictionary<IntPtr, bool> ExceptionTypeCache = new Dictionary<IntPtr, bool>();
129+
130+
private static bool IsException(IntPtr pyObject)
131+
{
132+
bool res;
133+
var type = Runtime.PyObject_TYPE(pyObject);
134+
if (!ExceptionTypeCache.TryGetValue(type, out res))
135+
{
136+
res = Runtime.PyObjectType_TypeCheck(type, Exceptions.BaseException)
137+
|| Runtime.PyObjectType_TypeCheck(type, Runtime.PyTypeType)
138+
&& Runtime.PyType_IsSubtype(pyObject, Exceptions.BaseException);
139+
ExceptionTypeCache.Add(type, res);
140+
}
141+
return res;
142+
}
131143
}
132144

133145
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]

src/runtime/propertyobject.cs

Lines changed: 90 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Linq.Expressions;
23
using System.Reflection;
34
using System.Security.Permissions;
45

@@ -12,6 +13,10 @@ internal class PropertyObject : ExtensionType
1213
private PropertyInfo info;
1314
private MethodInfo getter;
1415
private MethodInfo setter;
16+
private bool getterCacheFailed;
17+
private bool setterCacheFailed;
18+
private Func<object, object> getterCache;
19+
private Action<object, object> setterCache;
1520

1621
[StrongNameIdentityPermission(SecurityAction.Assert)]
1722
public PropertyObject(PropertyInfo md)
@@ -67,7 +72,21 @@ public static IntPtr tp_descr_get(IntPtr ds, IntPtr ob, IntPtr tp)
6772

6873
try
6974
{
70-
result = self.info.GetValue(co.inst, null);
75+
if (self.getterCache == null && !self.getterCacheFailed)
76+
{
77+
// if the getter is not public 'GetGetMethod' will not find it
78+
// but calling 'GetValue' will work, so for backwards compatibility
79+
// we will use it instead
80+
self.getterCache = BuildGetter(self.info);
81+
if (self.getterCache == null)
82+
{
83+
self.getterCacheFailed = true;
84+
}
85+
}
86+
87+
result = self.getterCacheFailed ?
88+
self.info.GetValue(co.inst, null)
89+
: self.getterCache(co.inst);
7190
return Converter.ToPython(result, self.info.PropertyType);
7291
}
7392
catch (Exception e)
@@ -81,7 +100,6 @@ public static IntPtr tp_descr_get(IntPtr ds, IntPtr ob, IntPtr tp)
81100
}
82101
}
83102

84-
85103
/// <summary>
86104
/// Descriptor __set__ implementation. This method sets the value of
87105
/// a property based on the given Python value. The Python value must
@@ -132,7 +150,27 @@ public static IntPtr tp_descr_get(IntPtr ds, IntPtr ob, IntPtr tp)
132150
Exceptions.RaiseTypeError("invalid target");
133151
return -1;
134152
}
135-
self.info.SetValue(co.inst, newval, null);
153+
154+
if (self.setterCache == null && !self.setterCacheFailed)
155+
{
156+
// if the setter is not public 'GetSetMethod' will not find it
157+
// but calling 'SetValue' will work, so for backwards compatibility
158+
// we will use it instead
159+
self.setterCache = BuildSetter(self.info);
160+
if (self.setterCache == null)
161+
{
162+
self.setterCacheFailed = true;
163+
}
164+
}
165+
166+
if (self.setterCacheFailed)
167+
{
168+
self.info.SetValue(co.inst, newval, null);
169+
}
170+
else
171+
{
172+
self.setterCache(co.inst, newval);
173+
}
136174
}
137175
else
138176
{
@@ -160,5 +198,54 @@ public static IntPtr tp_repr(IntPtr ob)
160198
var self = (PropertyObject)GetManagedObject(ob);
161199
return Runtime.PyString_FromString($"<property '{self.info.Name}'>");
162200
}
201+
202+
private static Func<object, object> BuildGetter(PropertyInfo propertyInfo)
203+
{
204+
var methodInfo = propertyInfo.GetGetMethod();
205+
if (methodInfo == null)
206+
{
207+
// if the getter is not public 'GetGetMethod' will not find it
208+
return null;
209+
}
210+
var obj = Expression.Parameter(typeof(object), "o");
211+
// Require because we will know at runtime the declaring type
212+
// so 'obj' is declared as typeof(object)
213+
var instance = Expression.Convert(obj, methodInfo.DeclaringType);
214+
215+
var expressionCall = Expression.Call(instance, methodInfo);
216+
217+
return Expression.Lambda<Func<object, object>>(
218+
Expression.Convert(expressionCall, typeof(object)),
219+
obj).Compile();
220+
}
221+
222+
private static Action<object, object> BuildSetter(PropertyInfo propertyInfo)
223+
{
224+
var methodInfo = propertyInfo.GetSetMethod();
225+
if (methodInfo == null)
226+
{
227+
// if the setter is not public 'GetSetMethod' will not find it
228+
return null;
229+
}
230+
var obj = Expression.Parameter(typeof(object), "o");
231+
// Require because we will know at runtime the declaring type
232+
// so 'obj' is declared as typeof(object)
233+
var instance = Expression.Convert(obj, methodInfo.DeclaringType);
234+
235+
var parameters = methodInfo.GetParameters();
236+
if (parameters.Length != 1)
237+
{
238+
return null;
239+
}
240+
var value = Expression.Parameter(typeof(object));
241+
var argument = Expression.Convert(value, parameters[0].ParameterType);
242+
243+
var expressionCall = Expression.Call(instance, methodInfo, argument);
244+
245+
return Expression.Lambda<Action<object, object>>(
246+
expressionCall,
247+
obj,
248+
value).Compile();
249+
}
163250
}
164251
}

src/runtime/runtime.cs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ public static IntPtr GetProcAddress(IntPtr dllHandle, string name)
104104
public class Runtime
105105
{
106106
// C# compiler copies constants to the assemblies that references this library.
107-
// We needs to replace all public constants to static readonly fields to allow
107+
// We needs to replace all public constants to static readonly fields to allow
108108
// binary substitution of different Python.Runtime.dll builds in a target application.
109109

110110
public static int UCS => _UCS;
@@ -130,7 +130,7 @@ public class Runtime
130130
#endif
131131

132132
// C# compiler copies constants to the assemblies that references this library.
133-
// We needs to replace all public constants to static readonly fields to allow
133+
// We needs to replace all public constants to static readonly fields to allow
134134
// binary substitution of different Python.Runtime.dll builds in a target application.
135135

136136
public static string pyversion => _pyversion;
@@ -173,7 +173,7 @@ public class Runtime
173173
#endif
174174

175175
// C# compiler copies constants to the assemblies that references this library.
176-
// We needs to replace all public constants to static readonly fields to allow
176+
// We needs to replace all public constants to static readonly fields to allow
177177
// binary substitution of different Python.Runtime.dll builds in a target application.
178178

179179
public static readonly string PythonDLL = _PythonDll;
@@ -1565,6 +1565,11 @@ internal static bool PyObject_TypeCheck(IntPtr ob, IntPtr tp)
15651565
return (t == tp) || PyType_IsSubtype(t, tp);
15661566
}
15671567

1568+
internal static bool PyObjectType_TypeCheck(IntPtr type, IntPtr tp)
1569+
{
1570+
return (type == tp) || PyType_IsSubtype(type, tp);
1571+
}
1572+
15681573
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
15691574
internal static extern IntPtr PyType_GenericNew(IntPtr type, IntPtr args, IntPtr kw);
15701575

0 commit comments

Comments
 (0)
0