FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Merge pull request #51 from jmerle/reflection-improvements · jmerle/pythonnet@62b2175 · GitHub

Repository navigation

Commit 62b2175

Browse files
Merge pull request QuantConnect#51 from jmerle/reflection-improvements
Improve property access performance
2 parents 9f31a98 + 01f018c commit 62b2175

10 files changed

Lines changed: 1288 additions & 20 deletions

‎src/embed_tests/TestPropertyAccess.cs‎

Lines changed: 1020 additions & 0 deletions
Large diffs are not rendered by default.

‎src/runtime/Properties/AssemblyInfo.cs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
using System.Runtime.CompilerServices;
33

44
[assembly: InternalsVisibleTo("Python.EmbeddingTest, PublicKey=00240000048000009400000006020000002400005253413100040000110000005ffd8f49fb44ab0641b3fd8d55e749f716e6dd901032295db641eb98ee46063cbe0d4a1d121ef0bc2af95f8a7438d7a80a3531316e6b75c2dae92fb05a99f03bf7e0c03980e1c3cfb74ba690aca2f3339ef329313bcc5dccced125a4ffdc4531dcef914602cd5878dc5fbb4d4c73ddfbc133f840231343e013762884d6143189")]
5-
[assembly: AssemblyVersion("2.0.1")]
6-
[assembly: AssemblyFileVersion("2.0.1")]
5+
[assembly: AssemblyVersion("2.0.2")]
6+
[assembly: AssemblyFileVersion("2.0.2")]

‎src/runtime/Python.Runtime.csproj‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<RootNamespace>Python.Runtime</RootNamespace>
77
<AssemblyName>Python.Runtime</AssemblyName>
88
<PackageId>QuantConnect.pythonnet</PackageId>
9-
<Version>2.0.1</Version>
9+
<Version>2.0.2</Version>
1010
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
1111
<PackageLicenseFile>LICENSE</PackageLicenseFile>
1212
<RepositoryUrl>https://github.com/pythonnet/pythonnet</RepositoryUrl>
@@ -65,5 +65,6 @@
6565
<PackageReference Include="System.Security.Permissions" Version="4.4.0" />
6666
<PackageReference Include="System.Reflection.Emit" Version="4.3.0" />
6767
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
68+
<PackageReference Include="fasterflect" Version="3.0.0" />
6869
</ItemGroup>
6970
</Project>

‎src/runtime/assemblymanager.cs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ internal static void Initialize()
7070
// lets wait until all assemblies are loaded
7171
do
7272
{
73-
if (safeCount++ > 200)
73+
if (safeCount++ > 400)
7474
{
7575
throw new TimeoutException("Timeout while waiting for assemblies to load");
7676
}

‎src/runtime/clrobject.cs‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,13 @@ internal CLRObject(object ob, IntPtr tp)
3131
pyHandle = py;
3232
inst = ob;
3333

34-
// Fix the BaseException args (and __cause__ in case of Python 3)
35-
// slot if wrapping a CLR exception
36-
Exceptions.SetArgsAndCause(py);
34+
// for performance before calling SetArgsAndCause() lets check if we are an exception
35+
if (inst is Exception)
36+
{
37+
// Fix the BaseException args (and __cause__ in case of Python 3)
38+
// slot if wrapping a CLR exception
39+
Exceptions.SetArgsAndCause(py);
40+
}
3741
}
3842

3943
protected CLRObject()

‎src/runtime/converterextensions.cs‎

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,19 @@ internal static PyObject TryEncode(object obj, Type type)
8484
if (obj == null) throw new ArgumentNullException(nameof(obj));
8585
if (type == null) throw new ArgumentNullException(nameof(type));
8686

87-
foreach (var encoder in clrToPython.GetOrAdd(type, GetEncoders))
87+
if (clrToPython.Count == 0)
88+
{
89+
return null;
90+
}
91+
92+
IPyObjectEncoder[] availableEncoders;
93+
if (!clrToPython.TryGetValue(type, out availableEncoders))
94+
{
95+
availableEncoders = GetEncoders(type);
96+
clrToPython[type] = availableEncoders;
97+
}
98+
99+
foreach (var encoder in availableEncoders)
88100
{
89101
var result = encoder.TryEncode(obj);
90102
if (result != null) return result;
@@ -93,8 +105,8 @@ internal static PyObject TryEncode(object obj, Type type)
93105
return null;
94106
}
95107

96-
static readonly ConcurrentDictionary<Type, IPyObjectEncoder[]>
97-
clrToPython = new ConcurrentDictionary<Type, IPyObjectEncoder[]>();
108+
static readonly Dictionary<Type, IPyObjectEncoder[]> clrToPython = new();
109+
98110
static IPyObjectEncoder[] GetEncoders(Type type)
99111
{
100112
lock (encoders)

‎src/runtime/fasterflectmanager.cs‎

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
using System;
2+
using System.Collections.Generic;
3+
4+
using Fasterflect;
5+
6+
namespace Python.Runtime
7+
{
8+
public static class FasterflectManager
9+
{
10+
private static Dictionary<Type, bool> _isValueTypeCache = new();
11+
private static Dictionary<string, MemberGetter> _memberGetterCache = new();
12+
private static Dictionary<string, MemberSetter> _memberSetterCache = new();
13+
14+
public static bool IsValueType(Type type)
15+
{
16+
bool isValueType;
17+
if (_isValueTypeCache.TryGetValue(type, out isValueType))
18+
{
19+
return isValueType;
20+
}
21+
22+
isValueType = type.IsValueType;
23+
_isValueTypeCache[type] = isValueType;
24+
25+
return isValueType;
26+
}
27+
28+
public static MemberGetter GetPropertyGetter(Type type, string propertyName)
29+
{
30+
var cacheKey = GetCacheKey(type, propertyName);
31+
32+
MemberGetter memberGetter;
33+
if (_memberGetterCache.TryGetValue(cacheKey, out memberGetter))
34+
{
35+
return memberGetter;
36+
}
37+
38+
memberGetter = type.DelegateForGetPropertyValue(propertyName);
39+
_memberGetterCache[cacheKey] = memberGetter;
40+
41+
return memberGetter;
42+
}
43+
44+
public static MemberSetter GetPropertySetter(Type type, string propertyName)
45+
{
46+
var cacheKey = GetCacheKey(type, propertyName);
47+
48+
MemberSetter memberSetter;
49+
if (_memberSetterCache.TryGetValue(cacheKey, out memberSetter))
50+
{
51+
return memberSetter;
52+
}
53+
54+
memberSetter = type.DelegateForSetPropertyValue(propertyName);
55+
_memberSetterCache[cacheKey] = memberSetter;
56+
57+
return memberSetter;
58+
}
59+
60+
public static MemberGetter GetFieldGetter(Type type, string fieldName)
61+
{
62+
var cacheKey = GetCacheKey(type, fieldName);
63+
64+
MemberGetter memberGetter;
65+
if (_memberGetterCache.TryGetValue(cacheKey, out memberGetter))
66+
{
67+
return memberGetter;
68+
}
69+
70+
memberGetter = type.DelegateForGetFieldValue(fieldName);
71+
_memberGetterCache[cacheKey] = memberGetter;
72+
73+
return memberGetter;
74+
}
75+
76+
public static MemberSetter GetFieldSetter(Type type, string fieldName)
77+
{
78+
var cacheKey = GetCacheKey(type, fieldName);
79+
80+
MemberSetter memberSetter;
81+
if (_memberSetterCache.TryGetValue(cacheKey, out memberSetter))
82+
{
83+
return memberSetter;
84+
}
85+
86+
memberSetter = type.DelegateForSetFieldValue(fieldName);
87+
_memberSetterCache[cacheKey] = memberSetter;
88+
89+
return memberSetter;
90+
}
91+
92+
private static string GetCacheKey(Type type, string memberName)
93+
{
94+
return $"{type} {memberName}";
95+
}
96+
}
97+
}

‎src/runtime/fieldobject.cs‎

Lines changed: 86 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using System;
22
using System.Reflection;
33

4+
using Fasterflect;
5+
46
namespace Python.Runtime
57
{
68
using MaybeFieldInfo = MaybeMemberInfo<FieldInfo>;
@@ -12,6 +14,15 @@ internal class FieldObject : ExtensionType
1214
{
1315
private MaybeFieldInfo info;
1416

17+
private MemberGetter _memberGetter;
18+
private Type _memberGetterType;
19+
20+
private MemberSetter _memberSetter;
21+
private Type _memberSetterType;
22+
23+
private bool _isValueType;
24+
private Type _isValueTypeType;
25+
1526
public FieldObject(FieldInfo info)
1627
{
1728
this.info = info;
@@ -49,7 +60,16 @@ public static IntPtr tp_descr_get(IntPtr ds, IntPtr ob, IntPtr tp)
4960
}
5061
try
5162
{
52-
result = info.GetValue(null);
63+
// Fasterflect does not support constant fields
64+
if (info.IsLiteral && !info.IsInitOnly)
65+
{
66+
result = info.GetValue(null);
67+
}
68+
else
69+
{
70+
result = self.GetMemberGetter(info.DeclaringType)(info.DeclaringType);
71+
}
72+
5373
return Converter.ToPython(result, info.FieldType);
5474
}
5575
catch (Exception e)
@@ -67,7 +87,18 @@ public static IntPtr tp_descr_get(IntPtr ds, IntPtr ob, IntPtr tp)
6787
Exceptions.SetError(Exceptions.TypeError, "instance is not a clr object");
6888
return IntPtr.Zero;
6989
}
70-
result = info.GetValue(co.inst);
90+
91+
// Fasterflect does not support constant fields
92+
if (info.IsLiteral && !info.IsInitOnly)
93+
{
94+
result = info.GetValue(co.inst);
95+
}
96+
else
97+
{
98+
var type = co.inst.GetType();
99+
result = self.GetMemberGetter(type)(self.IsValueType(type) ? co.inst.WrapIfValueType() : co.inst);
100+
}
101+
71102
return Converter.ToPython(result, info.FieldType);
72103
}
73104
catch (Exception e)
@@ -137,11 +168,29 @@ public static IntPtr tp_descr_get(IntPtr ds, IntPtr ob, IntPtr tp)
137168
Exceptions.SetError(Exceptions.TypeError, "instance is not a clr object");
138169
return -1;
139170
}
140-
info.SetValue(co.inst, newval);
171+
172+
// Fasterflect does not support constant fields
173+
if (info.IsLiteral && !info.IsInitOnly)
174+
{
175+
info.SetValue(co.inst, newval);
176+
}
177+
else
178+
{
179+
var type = co.inst.GetType();
180+
self.GetMemberSetter(type)(self.IsValueType(type) ? co.inst.WrapIfValueType() : co.inst, newval);
181+
}
141182
}
142183
else
143184
{
144-
info.SetValue(null, newval);
185+
// Fasterflect does not support constant fields
186+
if (info.IsLiteral && !info.IsInitOnly)
187+
{
188+
info.SetValue(null, newval);
189+
}
190+
else
191+
{
192+
self.GetMemberSetter(info.DeclaringType)(info.DeclaringType, newval);
193+
}
145194
}
146195
return 0;
147196
}
@@ -160,5 +209,38 @@ public static IntPtr tp_repr(IntPtr ob)
160209
var self = (FieldObject)GetManagedObject(ob);
161210
return Runtime.PyString_FromString($"<field '{self.info}'>");
162211
}
212+
213+
private MemberGetter GetMemberGetter(Type type)
214+
{
215+
if (type != _memberGetterType)
216+
{
217+
_memberGetter = FasterflectManager.GetFieldGetter(type, info.Value.Name);
218+
_memberGetterType = type;
219+
}
220+
221+
return _memberGetter;
222+
}
223+
224+
private MemberSetter GetMemberSetter(Type type)
225+
{
226+
if (type != _memberSetterType)
227+
{
228+
_memberSetter = FasterflectManager.GetFieldSetter(type, info.Value.Name);
229+
_memberSetterType = type;
230+
}
231+
232+
return _memberSetter;
233+
}
234+
235+
private bool IsValueType(Type type)
236+
{
237+
if (type != _isValueTypeType)
238+
{
239+
_isValueType = FasterflectManager.IsValueType(type);
240+
_isValueTypeType = type;
241+
}
242+
243+
return _isValueType;
244+
}
163245
}
164246
}

‎src/runtime/managedtype.cs‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ internal long RefCount
6060
internal GCHandle AllocGCHandle(TrackTypes track = TrackTypes.Untrack)
6161
{
6262
gcHandle = GCHandle.Alloc(this);
63-
if (track != TrackTypes.Untrack)
63+
if (track != TrackTypes.Untrack && PythonEngine.ShutdownMode == ShutdownMode.Reload)
6464
{
6565
_managedObjs.Add(this, track);
6666
}
@@ -69,7 +69,11 @@ internal GCHandle AllocGCHandle(TrackTypes track = TrackTypes.Untrack)
6969

7070
internal void FreeGCHandle()
7171
{
72-
_managedObjs.Remove(this);
72+
if (PythonEngine.ShutdownMode == ShutdownMode.Reload)
73+
{
74+
_managedObjs.Remove(this);
75+
}
76+
7377
if (gcHandle.IsAllocated)
7478
{
7579
gcHandle.Free();

0 commit comments

Comments
 (0)

Back | FazBrowse Home | New Git URL