8000 Merge pull request #31 from losttech/perf/Interop · saaib/pythonnet@2e56c59 · GitHub
[go: up one dir, main page]

Skip to content

Commit 2e56c59

Browse files
Merge pull request QuantConnect#31 from losttech/perf/Interop
Improve Python.NET interop performance
2 parents de514d8 + c70724a commit 2e56c59

File tree

9 files changed

+100
-47
lines changed

9 files changed

+100
-47
lines changed

AUTHORS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
- Sam Winstanley ([@swinstanley](https://github.com/swinstanley))
4040
- Sean Freitag ([@cowboygneox](https://github.com/cowboygneox))
4141
- Serge Weinstock ([@sweinst](https://github.com/sweinst))
42+
- Victor Milovanov([@lostmsu](https://github.com/lostmsu))
4243
- Ville M. Vainio ([@vivainio](https://github.com/vivainio))
4344
- Virgil Dupras ([@hsoft](https://github.com/hsoft))
4445
- Wenguang Yang ([@yagweb](https://github.com/yagweb))

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
2323

2424
### Changed
2525
- PythonException included C# call stack
26+
- improved performance of calls from Python to C#
2627

2728
### Fixed
2829

src/runtime/classbase.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ public static int tp_is_gc(IntPtr type)
271271
public static void tp_dealloc(IntPtr ob)
272272
{
273273
ManagedType self = GetManagedObject(ob);
274-
IntPtr dict = Marshal.ReadIntPtr(ob, ObjectOffset.DictOffset(ob));
274+
IntPtr dict = Marshal.ReadIntPtr(ob, ObjectOffset.TypeDictOffset(self.tpHandle));
275275
if (dict != IntPtr.Zero)
276276
{
277277
Runtime.XDecref(dict);
< 67E6 code class="diff-text-cell hunk">

src/runtime/classderived.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System;
1+
using System;
22
using System.Collections.Generic;
33
using System.Linq;
44
using System.Reflection;
@@ -870,7 +870,7 @@ public static void Finalize(IPythonDerivedType obj)
870870
// the C# object is being destroyed which must mean there are no more
871871
// references to the Python object as well so now we can dealloc the
872872
// python object.
873-
IntPtr dict = Marshal.ReadIntPtr(self.pyHandle, ObjectOffset.DictOffset(self.pyHandle));
873+
IntPtr dict = Marshal.ReadIntPtr(self.pyHandle, ObjectOffset.TypeDictOffset(self.tpHandle));
874874
if (dict != IntPtr.Zero)
875875
{
876876
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
long flags = Util.ReadCLong(tp, TypeOffset.tp_flags);
1515
if ((flags & TypeFlags.Subclass) != 0)
1616
{
17-
IntPtr dict = Marshal.ReadIntPtr(py, ObjectOffset.DictOffset(tp));
17+
IntPtr dict = 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: 87 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections;
33
using System.Collections.Generic;
4+
using System.Diagnostics;
45
using System.Runtime.InteropServices;
56
using System.Reflection;
67
using System.Text;
@@ -67,11 +68,47 @@ public ModulePropertyAttribute()
6768
}
6869
}
6970

71+
internal static class ManagedDataOffsets
72+
{
73+
static ManagedDataOffsets()
74+
{
75+
FieldInfo[] fi = typeof(ManagedDataOffsets).GetFields(BindingFlags.Static | BindingFlags.Public);
76+
for (int i = 0; i < fi.Length; i++)
77+
{
78+
fi[i].SetValue(null, -(i * IntPtr.Size) - IntPtr.Size);
79+
}
7080

71-
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
72-
internal class ObjectOffset
81+
size = fi.Length * IntPtr.Size;
82+
}
83+
84+
public static readonly int ob_data;
85+
public static readonly int ob_dict;
86+
87+
private static int BaseOffset(IntPtr type)
88+
{
89+
Debug.Assert(type != IntPtr.Zero);
90+
int typeSize = Marshal.ReadInt32(type, TypeOffset.tp_basicsize);
91+
Debug.Assert(typeSize > 0 && typeSize <= ExceptionOffset.Size());
92+
return typeSize;
93+
}
94+
public static int DataOffset(IntPtr type)
95+
{
96+
return BaseOffset(type) + ob_data;
97+
}
98+
99+
public static int DictOffset(IntPtr type)
100+
{
101+
return BaseOffset(type) + ob_dict;
102+
}
103+
104+
public static int Size { get { return size; } }
105+
106+
private static readonly int size;
107+
}
108+
109+
internal static class OriginalObjectOffsets
73110
{
74-
static ObjectOffset()
111+
static OriginalObjectOffsets()
75112
{
76113
int size = IntPtr.Size;
77114
var n = 0; // Py_TRACE_REFS add two pointers to PyObject_HEAD
@@ -82,39 +119,58 @@ static ObjectOffset()
82119
#endif
83120
ob_refcnt = (n + 0) * size;
84121
ob_type = (n + 1) * size;
85-
ob_dict = (n + 2) * size;
86-
ob_data = (n + 3) * size;
87122
}
88123

89-
public static int magic(IntPtr ob)
124+
public static int Size { get { return size; } }
125+
126+
private static readonly int size =
127+
#if PYTHON_WITH_PYDEBUG
128+
4 * IntPtr.Size;
129+
#else
130+
2 * IntPtr.Size;
131+
#endif
132+
133+
#if PYTHON_WITH_PYDEBUG
134+
public static int _ob_next;
135+
public static int _ob_prev;
136+
#endif
137+
public static int ob_refcnt;
138+
public static int ob_type;
139+
}
140+
141+
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
142+
internal class ObjectOffset
143+
{
144+
static ObjectOffset()
145+
{
146+
#if PYTHON_WITH_PYDEBUG
147+
_ob_next = OriginalObjectOffsets._ob_next;
148+
_ob_prev = OriginalObjectOffsets._ob_prev;
149+
#endif
150+
ob_refcnt = OriginalObjectOffsets.ob_refcnt;
151+
ob_type = OriginalObjectOffsets.ob_type;
152+
153+
size = OriginalObjectOffsets.Size + ManagedDataOffsets.Size;
154+
}
155+
156+
public static int magic(IntPtr type)
90157
{
91-
if (IsException(ob))
92-
{
93-
return ExceptionOffset.ob_data;
94-
}
95-
return ob_data;
158+
return ManagedDataOffsets.DataOffset(type);
96159
}
97160

98-
public static int DictOffset(IntPtr ob)
161+
public static int TypeDictOffset(IntPtr type)
99162
{
100-
if (IsException(ob))
101-
{
102-
return ExceptionOffset.ob_dict;
103-
}
104-
return ob_dict;
163+
return ManagedDataOffsets.DictOffset(type);
105164
}
106165

107-
public static int Size(IntPtr ob)
166+
public static int Size(IntPtr pyType)
108167
{
109-
if (IsException(ob))
168+
if (IsException(pyType))
110169
{
111170
return ExceptionOffset.Size();
112171
}
113-
#if PYTHON_WITH_PYDEBUG
114-
return 6 * IntPtr.Size;
115-
#else
116-
return 4 * IntPtr.Size;
117-
#endif
172+
173+
return size;
118174
}
119175

120176
#if PYTHON_WITH_PYDEBUG
@@ -123,8 +179,7 @@ public static int Size(IntPtr ob)
123179
#endif
124180
public static int ob_refcnt;
125181
public static int ob_type;
126-
private static int ob_dict;
127-
private static int ob_data;
182+
private static readonly int size;
128183

129184
private static bool IsException(IntPtr pyObject)
130185
{
@@ -141,19 +196,17 @@ internal class ExceptionOffset
141196
static ExceptionOffset()
142197
{
143198
Type type = typeof(ExceptionOffset);
144-
FieldInfo[] fi = type.GetFields();
145-
int size = IntPtr.Size;
199+
FieldInfo[] fi = type.GetFields(BindingFlags.Static | BindingFlags.Public);
146200
for (int i = 0; i < fi.Length; i++)
147201
{
148-
fi[i].SetValue(null, (i * size) + ObjectOffset.ob_type + size);
202+
fi[i].SetValue(null, (i * IntPtr.Size) + OriginalObjectOffsets.Size);
149203
}
150-
}
151204

152-
public static int Size()
153-
{
154-
return ob_data + IntPtr.Size;
205+
size = fi.Length * IntPtr.Size + OriginalObjectOffsets.Size + ManagedDataOffsets.Size;
155206
}
156207

208+
public static int Size() { return size; }
209+
157210
// PyException_HEAD
158211
// (start after PyObject_HEAD)
159212
public static int dict = 0;
@@ -167,9 +220,7 @@ public static int Size()
167220
public static int suppress_context = 0;
168221
#endif
169222

170-
// extra c# data
171-
public static int ob_dict;
172-
public static int ob_data;
223+
private static readonly int size;
173224
}
174225

175226

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
IntPtr op = 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
return null;

src/runtime/moduleobject.cs

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

56-
Marshal.WriteIntPtr(pyHandle, ObjectOffset.DictOffset(pyHandle), dict);
56+
Marshal.WriteIntPtr(pyHandle, ObjectOffset.TypeDictOffset(tpHandle), dict);
5757

5858
InitializeModuleMembers();
5959
}

src/runtime/typemanager.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System;
1+
using System;
22
using System.Collections;
33
using System.Collections.Generic;
44
using System.Reflection;
@@ -79,7 +79,7 @@ internal static IntPtr CreateType(Type impl)
7979
// Set tp_basicsize to the size of our managed instance objects.
8080
Marshal.WriteIntPtr(type, TypeOffset.tp_basicsize, (IntPtr)ob_size);
8181

82-
var offset = (IntPtr)ObjectOffset.DictOffset(type);
82+
var offset = (IntPtr)ObjectOffset.TypeDictOffset(type);
8383
Marshal.WriteIntPtr(type, TypeOffset.tp_dictoffset, offset);
8484

8585
InitializeSlots(type, impl);
@@ -117,17 +117,17 @@ internal static IntPtr CreateType(ManagedType impl, Type clrType)
117117

118118
IntPtr base_ = IntPtr.Zero;
119119
int ob_size = ObjectOffset.Size(Runtime.PyTypeType);
120-
int tp_dictoffset = ObjectOffset.DictOffset(Runtime.PyTypeType);
121120

122121
// XXX Hack, use a different base class for System.Exception
123122
// Python 2.5+ allows new style class exceptions but they *must*
124123
// subclass BaseException (or better Exception).
125124
if (typeof(Exception).IsAssignableFrom(clrType))
126125
{
127126
ob_size = ObjectOffset.Size(Exceptions.Exception);
128-
tp_dictoffset = ObjectOffset.DictOffset(Exceptions.Exception);
129127
}
130128

129+
int tp_dictoffset = ob_size + ManagedDataOffsets.ob_dict;
130+
131131
if (clrType == typeof(Exception))
132132
{
133133
base_ = Exceptions.Exception;

0 commit comments

Comments
 (0)
0