Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit4a92d80

Browse files
filmorlostmsu
andauthored
Improve performance of unwrapping .NET objects passed from Python (#930)
This addresses the following scenario:1. A C# object `a` is created and filled with some data.2. `a` is passed via Python.NET to Python. To do that Python.NET creates a wrapper object `w`, and stores reference to `a` in one of its fields.3. Python code later passes `w` back to C#, e.g. calls `SomeCSharpMethod(w)`.4. Python.NET has to unwrap `w`, so it reads the reference to `a` from it.Prior to this change in 4. Python.NET had to determine what kind of anobject `a` is. If it is an exception, a different offset needed to beused. That check was very expensive (up to 4 calls into Pythoninterpreter).This change replaces that check with computing offset unconditionallyby subtracting a constant from the object size (which is read from the wrapper),thus avoiding calls to Python interpreter.Co-authored-by: Victor Milovanov <lost@losttech.software>
1 parent782eff8 commit4a92d80

File tree

10 files changed

+113
-51
lines changed

10 files changed

+113
-51
lines changed

‎CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
2424
- Added argument types information to "No method matches given arguments" message
2525
- Moved wheel import in setup.py inside of a try/except to prevent pip collection failures
2626
- Removes PyLong_GetMax and PyClass_New when targetting Python3
27+
- Improved performance of calls from Python to C#
2728
- Added support for converting python iterators to C# arrays
2829
- Changed usage of obselete function GetDelegateForFunctionPointer(IntPtr, Type) to GetDelegateForFunctionPointer<TDelegate>(IntPtr)
2930
- When calling C# from Python, enable passing argument of any type to a parameter of C# type`object` by wrapping it into`PyObject` instance. ([#881][i881])

‎src/perf_tests/BenchmarkTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,14 @@ public void SetUp()
3030
publicvoidReadInt64Property()
3131
{
3232
doubleoptimisticPerfRatio=GetOptimisticPerfRatio(this.summary.Reports);
33-
AssertPerformanceIsBetterOrSame(optimisticPerfRatio,target:0.66);
33+
AssertPerformanceIsBetterOrSame(optimisticPerfRatio,target:0.57);
3434
}
3535

3636
[Test]
3737
publicvoidWriteInt64Property()
3838
{
3939
doubleoptimisticPerfRatio=GetOptimisticPerfRatio(this.summary.Reports);
40-
AssertPerformanceIsBetterOrSame(optimisticPerfRatio,target:0.64);
40+
AssertPerformanceIsBetterOrSame(optimisticPerfRatio,target:0.57);
4141
}
4242

4343
staticdoubleGetOptimisticPerfRatio(

‎src/runtime/classbase.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ public static IntPtr tp_repr(IntPtr ob)
291291
publicstaticvoidtp_dealloc(IntPtrob)
292292
{
293293
ManagedTypeself=GetManagedObject(ob);
294-
IntPtrdict=Marshal.ReadIntPtr(ob,ObjectOffset.DictOffset(ob));
294+
IntPtrdict=Marshal.ReadIntPtr(ob,ObjectOffset.TypeDictOffset(self.tpHandle));
295295
if(dict!=IntPtr.Zero)
296296
{
297297
Runtime.XDecref(dict);

‎src/runtime/classderived.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -877,7 +877,7 @@ public static void Finalize(IPythonDerivedType obj)
877877
// the C# object is being destroyed which must mean there are no more
878878
// references to the Python object as well so now we can dealloc the
879879
// python object.
880-
IntPtrdict=Marshal.ReadIntPtr(self.pyHandle,ObjectOffset.DictOffset(self.pyHandle));
880+
IntPtrdict=Marshal.ReadIntPtr(self.pyHandle,ObjectOffset.TypeDictOffset(self.tpHandle));
881881
if(dict!=IntPtr.Zero)
882882
{
883883
Runtime.XDecref(dict);

‎src/runtime/clrobject.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ internal CLRObject(object ob, IntPtr tp)
1414
longflags=Util.ReadCLong(tp,TypeOffset.tp_flags);
1515
if((flags&TypeFlags.Subclass)!=0)
1616
{
17-
IntPtrdict=Marshal.ReadIntPtr(py,ObjectOffset.DictOffset(tp));
17+
IntPtrdict=Marshal.ReadIntPtr(py,ObjectOffset.TypeDictOffset(tp));
1818
if(dict==IntPtr.Zero)
1919
{
2020
dict=Runtime.PyDict_New();
21-
Marshal.WriteIntPtr(py,ObjectOffset.DictOffset(tp),dict);
21+
Marshal.WriteIntPtr(py,ObjectOffset.TypeDictOffset(tp),dict);
2222
}
2323
}
2424

‎src/runtime/interop.cs

Lines changed: 96 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
usingSystem;
22
usingSystem.Collections;
3-
usingSystem.Collections.Specialized;
3+
usingSystem.Collections.Generic;
4+
usingSystem.Diagnostics;
45
usingSystem.Runtime.InteropServices;
56
usingSystem.Reflection;
67
usingSystem.Text;
@@ -68,11 +69,47 @@ public ModulePropertyAttribute()
6869
}
6970
}
7071

72+
internalstaticclassManagedDataOffsets
73+
{
74+
staticManagedDataOffsets()
75+
{
76+
FieldInfo[]fi=typeof(ManagedDataOffsets).GetFields(BindingFlags.Static|BindingFlags.Public);
77+
for(inti=0;i<fi.Length;i++)
78+
{
79+
fi[i].SetValue(null,-(i*IntPtr.Size)-IntPtr.Size);
80+
}
7181

72-
[StructLayout(LayoutKind.Sequential,CharSet=CharSet.Ansi)]
73-
internalclassObjectOffset
82+
size=fi.Length*IntPtr.Size;
83+
}
84+
85+
publicstaticreadonlyintob_data;
86+
publicstaticreadonlyintob_dict;
87+
88+
privatestaticintBaseOffset(IntPtrtype)
89+
{
90+
Debug.Assert(type!=IntPtr.Zero);
91+
inttypeSize=Marshal.ReadInt32(type,TypeOffset.tp_basicsize);
92+
Debug.Assert(typeSize>0&&typeSize<=ExceptionOffset.Size());
93+
returntypeSize;
94+
}
95+
publicstaticintDataOffset(IntPtrtype)
96+
{
97+
returnBaseOffset(type)+ob_data;
98+
}
99+
100+
publicstaticintDictOffset(IntPtrtype)
101+
{
102+
returnBaseOffset(type)+ob_dict;
103+
}
104+
105+
publicstaticintSize{get{returnsize;}}
106+
107+
privatestaticreadonlyintsize;
108+
}
109+
110+
internalstaticclassOriginalObjectOffsets
74111
{
75-
staticObjectOffset()
112+
staticOriginalObjectOffsets()
76113
{
77114
intsize=IntPtr.Size;
78115
varn=0;// Py_TRACE_REFS add two pointers to PyObject_HEAD
@@ -83,42 +120,58 @@ static ObjectOffset()
83120
#endif
84121
ob_refcnt=(n+0)*size;
85122
ob_type=(n+1)*size;
86-
ob_dict=(n+2)*size;
87-
ob_data=(n+3)*size;
88123
}
89124

90-
publicstaticintmagic(IntPtrob)
125+
publicstaticintSize{get{returnsize;}}
126+
127+
privatestaticreadonlyintsize=
128+
#ifPYTHON_WITH_PYDEBUG
129+
4*IntPtr.Size;
130+
#else
131+
2*IntPtr.Size;
132+
#endif
133+
134+
#ifPYTHON_WITH_PYDEBUG
135+
publicstaticint_ob_next;
136+
publicstaticint_ob_prev;
137+
#endif
138+
publicstaticintob_refcnt;
139+
publicstaticintob_type;
140+
}
141+
142+
[StructLayout(LayoutKind.Sequential,CharSet=CharSet.Ansi)]
143+
internalclassObjectOffset
144+
{
145+
staticObjectOffset()
146+
{
147+
#ifPYTHON_WITH_PYDEBUG
148+
_ob_next=OriginalObjectOffsets._ob_next;
149+
_ob_prev=OriginalObjectOffsets._ob_prev;
150+
#endif
151+
ob_refcnt=OriginalObjectOffsets.ob_refcnt;
152+
ob_type=OriginalObjectOffsets.ob_type;
153+
154+
size=OriginalObjectOffsets.Size+ManagedDataOffsets.Size;
155+
}
156+
157+
publicstaticintmagic(IntPtrtype)
91158
{
92-
if((Runtime.PyObject_TypeCheck(ob,Exceptions.BaseException)||
93-
(Runtime.PyType_Check(ob)&&Runtime.PyType_IsSubtype(ob,Exceptions.BaseException))))
94-
{
95-
returnExceptionOffset.ob_data;
96-
}
97-
returnob_data;
159+
returnManagedDataOffsets.DataOffset(type);
98160
}
99161

100-
publicstaticintDictOffset(IntPtrob)
162+
publicstaticintTypeDictOffset(IntPtrtype)
101163
{
102-
if((Runtime.PyObject_TypeCheck(ob,Exceptions.BaseException)||
103-
(Runtime.PyType_Check(ob)&&Runtime.PyType_IsSubtype(ob,Exceptions.BaseException))))
104-
{
105-
returnExceptionOffset.ob_dict;
106-
}
107-
returnob_dict;
164+
returnManagedDataOffsets.DictOffset(type);
108165
}
109166

110-
publicstaticintSize(IntPtrob)
167+
publicstaticintSize(IntPtrpyType)
111168
{
112-
if((Runtime.PyObject_TypeCheck(ob,Exceptions.BaseException)||
113-
(Runtime.PyType_Check(ob)&&Runtime.PyType_IsSubtype(ob,Exceptions.BaseException))))
169+
if(IsException(pyType))
114170
{
115171
returnExceptionOffset.Size();
116172
}
117-
#ifPYTHON_WITH_PYDEBUG
118-
return6*IntPtr.Size;
119-
#else
120-
return4*IntPtr.Size;
121-
#endif
173+
174+
returnsize;
122175
}
123176

124177
#ifPYTHON_WITH_PYDEBUG
@@ -127,8 +180,15 @@ public static int Size(IntPtr ob)
127180
#endif
128181
publicstaticintob_refcnt;
129182
publicstaticintob_type;
130-
privatestaticintob_dict;
131-
privatestaticintob_data;
183+
privatestaticreadonlyintsize;
184+
185+
privatestaticboolIsException(IntPtrpyObject)
186+
{
187+
vartype=Runtime.PyObject_TYPE(pyObject);
188+
returnRuntime.PyType_IsSameAsOrSubtype(type,ofType:Exceptions.BaseException)
189+
||Runtime.PyType_IsSameAsOrSubtype(type,ofType:Runtime.PyTypeType)
190+
&&Runtime.PyType_IsSubtype(pyObject,Exceptions.BaseException);
191+
}
132192
}
133193

134194
[StructLayout(LayoutKind.Sequential,CharSet=CharSet.Ansi)]
@@ -137,19 +197,17 @@ internal class ExceptionOffset
137197
staticExceptionOffset()
138198
{
139199
Typetype=typeof(ExceptionOffset);
140-
FieldInfo[]fi=type.GetFields();
141-
intsize=IntPtr.Size;
200+
FieldInfo[]fi=type.GetFields(BindingFlags.Static|BindingFlags.Public);
142201
for(inti=0;i<fi.Length;i++)
143202
{
144-
fi[i].SetValue(null,(i*size)+ObjectOffset.ob_type+size);
203+
fi[i].SetValue(null,(i*IntPtr.Size)+OriginalObjectOffsets.Size);
145204
}
146-
}
147205

148-
publicstaticintSize()
149-
{
150-
returnob_data+IntPtr.Size;
206+
size=fi.Length*IntPtr.Size+OriginalObjectOffsets.Size+ManagedDataOffsets.Size;
151207
}
152208

209+
publicstaticintSize(){returnsize;}
210+
153211
// PyException_HEAD
154212
// (start after PyObject_HEAD)
155213
publicstaticintdict=0;
@@ -163,9 +221,7 @@ public static int Size()
163221
publicstaticintsuppress_context=0;
164222
#endif
165223

166-
// extra c# data
167-
publicstaticintob_dict;
168-
publicstaticintob_data;
224+
privatestaticreadonlyintsize;
169225
}
170226

171227

‎src/runtime/managedtype.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ internal static ManagedType GetManagedObject(IntPtr ob)
3333
{
3434
IntPtrop=tp==ob
3535
?Marshal.ReadIntPtr(tp,TypeOffset.magic())
36-
:Marshal.ReadIntPtr(ob,ObjectOffset.magic(ob));
36+
:Marshal.ReadIntPtr(ob,ObjectOffset.magic(tp));
3737
if(op==IntPtr.Zero)
3838
{
3939
returnnull;

‎src/runtime/moduleobject.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public ModuleObject(string name)
5454
Runtime.XDecref(pyfilename);
5555
Runtime.XDecref(pydocstring);
5656

57-
Marshal.WriteIntPtr(pyHandle,ObjectOffset.DictOffset(pyHandle),dict);
57+
Marshal.WriteIntPtr(pyHandle,ObjectOffset.TypeDictOffset(tpHandle),dict);
5858

5959
InitializeModuleMembers();
6060
}

‎src/runtime/runtime.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1889,6 +1889,11 @@ internal static bool PyObject_TypeCheck(IntPtr ob, IntPtr tp)
18891889
return(t== tp)|| PyType_IsSubtype(t, tp);
18901890
}
18911891

1892+
internalstaticbool PyType_IsSameAsOrSubtype(IntPtr type, IntPtr ofType)
1893+
{
1894+
return(type== ofType)|| PyType_IsSubtype(type, ofType);
1895+
}
1896+
18921897
[DllImport(_PythonDll, CallingConvention= CallingConvention.Cdecl)]
18931898
internalstaticextern IntPtr PyType_GenericNew(IntPtr type, IntPtr args, IntPtr kw);
18941899

‎src/runtime/typemanager.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ internal static IntPtr CreateType(Type impl)
8787
// Set tp_basicsize to the size of our managed instance objects.
8888
Marshal.WriteIntPtr(type,TypeOffset.tp_basicsize,(IntPtr)ob_size);
8989

90-
varoffset=(IntPtr)ObjectOffset.DictOffset(type);
90+
varoffset=(IntPtr)ObjectOffset.TypeDictOffset(type);
9191
Marshal.WriteIntPtr(type,TypeOffset.tp_dictoffset,offset);
9292

9393
InitializeSlots(type,impl);
@@ -125,17 +125,17 @@ internal static IntPtr CreateType(ManagedType impl, Type clrType)
125125

126126
IntPtrbase_=IntPtr.Zero;
127127
intob_size=ObjectOffset.Size(Runtime.PyTypeType);
128-
inttp_dictoffset=ObjectOffset.DictOffset(Runtime.PyTypeType);
129128

130129
// XXX Hack, use a different base class for System.Exception
131130
// Python 2.5+ allows new style class exceptions but they *must*
132131
// subclass BaseException (or better Exception).
133132
if(typeof(Exception).IsAssignableFrom(clrType))
134133
{
135134
ob_size=ObjectOffset.Size(Exceptions.Exception);
136-
tp_dictoffset=ObjectOffset.DictOffset(Exceptions.Exception);
137135
}
138136

137+
inttp_dictoffset=ob_size+ManagedDataOffsets.ob_dict;
138+
139139
if(clrType==typeof(Exception))
140140
{
141141
base_=Exceptions.Exception;

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp