<html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns:m="http://schemas.microsoft.com/office/2004/12/omml" xmlns="http://www.w3.org/TR/REC-html40">
<head>
<meta http-equiv=Content-Type content="text/html; charset=us-ascii">
<meta name=Generator content="Microsoft Word 12 (filtered medium)">
<!--[if !mso]>
<style id=owaParaStyle>
v\:* {behavior:url(#default#VML);}
o\:* {behavior:url(#default#VML);}
w\:* {behavior:url(#default#VML);}
.shape {behavior:url(#default#VML);}
</style>
<![endif]-->
<style>
<!--
/* Font Definitions */
@font-face
        {font-family:Wingdings;
        panose-1:5 0 0 0 0 0 0 0 0 0;}
@font-face
        {font-family:"Cambria Math";
        panose-1:2 4 5 3 5 4 6 3 2 4;}
@font-face
        {font-family:Calibri;
        panose-1:2 15 5 2 2 2 4 3 2 4;}
@font-face
        {font-family:Tahoma;
        panose-1:2 11 6 4 3 5 4 4 2 4;}
@font-face
        {font-family:Consolas;
        panose-1:2 11 6 9 2 2 4 3 2 4;}
/* Style Definitions */
p.MsoNormal, li.MsoNormal, div.MsoNormal
        {margin:0in;
        margin-bottom:.0001pt;
        font-size:12.0pt;
        font-family:"Times New Roman","serif";}
a:link, span.MsoHyperlink
        {mso-style-priority:99;
        color:blue;
        text-decoration:underline;}
a:visited, span.MsoHyperlinkFollowed
        {mso-style-priority:99;
        color:purple;
        text-decoration:underline;}
p
        {mso-style-priority:99;
        margin:0in;
        margin-bottom:.0001pt;
        font-size:12.0pt;
        font-family:"Times New Roman","serif";}
p.msochpdefault, li.msochpdefault, div.msochpdefault
        {mso-style-name:msochpdefault;
        margin:0in;
        margin-bottom:.0001pt;
        font-size:10.0pt;
        font-family:"Times New Roman","serif";}
span.emailstyle18
        {mso-style-name:emailstyle18;
        font-family:"Calibri","sans-serif";
        color:#1F497D;}
span.emailstyle20
        {mso-style-name:emailstyle20;
        font-family:"Calibri","sans-serif";
        color:#1F497D;}
span.EmailStyle21
        {mso-style-type:personal-reply;
        font-family:"Calibri","sans-serif";
        color:#1F497D;}
.MsoChpDefault
        {mso-style-type:export-only;
        font-size:10.0pt;}
@page Section1
        {size:8.5in 11.0in;
        margin:1.0in 1.0in 1.0in 1.0in;}
div.Section1
        {page:Section1;}
-->
</style>
<!--[if gte mso 9]><xml>
<o:shapedefaults v:ext="edit" spidmax="1026" />
</xml><![endif]--><!--[if gte mso 9]><xml>
<o:shapelayout v:ext="edit">
<o:idmap v:ext="edit" data="1" />
</o:shapelayout></xml><![endif]-->
</head>
<body lang=EN-US link=blue vlink=purple>
<div class=Section1>
<p class=MsoNormal><span style='font-size:11.0pt;font-family:"Calibri","sans-serif";
color:#1F497D'>It looks like most of the perf problem here is actually the call
to Unicode.Encoding. There’s an issue where we’re not
switching over from a sub-optimial (but quick to generate) code path to a more
optimial (but slower to generate) code path when we’re continually accessing
members. It’s a trivial fix to make this work. But I would
suggest removing the call from the loop if it’s perf sensitive anyway –
you can store it in a global and access it there.<o:p></o:p></span></p>
<p class=MsoNormal><span style='font-size:11.0pt;font-family:"Calibri","sans-serif";
color:#1F497D'><o:p> </o:p></span></p>
<p class=MsoNormal><span style='font-size:11.0pt;font-family:"Calibri","sans-serif";
color:#1F497D'>Moving Encoding.Unicode outside of the loop on my machine we
were 14x slower than C#. W/ a fix for the Unicode.Encoding issue, but
leaving the get in, we are 23x slower. W/o the fix we are ridiculously
slower, I would guess it’s like 5000x vs C# calling the empty
method. Your method probably does work though </span><span
style='font-size:11.0pt;font-family:Wingdings;color:#1F497D'>J</span><span
style='font-size:11.0pt;font-family:"Calibri","sans-serif";color:#1F497D'><o:p></o:p></span></p>
<p class=MsoNormal><span style='font-size:11.0pt;font-family:"Calibri","sans-serif";
color:#1F497D'><o:p> </o:p></span></p>
<p class=MsoNormal><span style='font-size:11.0pt;font-family:"Calibri","sans-serif";
color:#1F497D'>At that point we’re spending most of the time in the loop,
iterating over the xrange, etc… We’ll eventually start
optimizing that stuff away too but it’s not a trivial fix.<o:p></o:p></span></p>
<p class=MsoNormal><span style='font-size:11.0pt;font-family:"Calibri","sans-serif";
color:#1F497D'><o:p> </o:p></span></p>
<div style='border:none;border-left:solid blue 1.5pt;padding:0in 0in 0in 4.0pt'>
<div>
<div style='border:none;border-top:solid #B5C4DF 1.0pt;padding:3.0pt 0in 0in 0in'>
<p class=MsoNormal><b><span style='font-size:10.0pt;font-family:"Tahoma","sans-serif"'>From:</span></b><span
style='font-size:10.0pt;font-family:"Tahoma","sans-serif"'>
users-bounces@lists.ironpython.com [mailto:users-bounces@lists.ironpython.com] <b>On
Behalf Of </b>Laurion Burchall<br>
<b>Sent:</b> Tuesday, April 20, 2010 1:46 PM<br>
<b>To:</b> Discussion of IronPython<br>
<b>Subject:</b> Re: [IronPython] Bad performance calling .NET method<o:p></o:p></span></p>
</div>
</div>
<p class=MsoNormal><o:p> </o:p></p>
<div>
<div>
<p class=MsoNormal style='margin-bottom:12.0pt'><span style='font-size:10.0pt;
font-family:"Tahoma","sans-serif";color:black'>In the case that affects me the
code isn't global. The class looks like this:<br>
<br>
</span><span style='font-size:10.0pt;font-family:Consolas;color:black'>class
EseDBCursor(object):<br>
...</span><span style='font-size:10.0pt;font-family:"Tahoma","sans-serif";
color:black'><br>
<br>
</span><span style='font-size:10.0pt;font-family:Consolas;color:black'>
def __getitem__(self, key): <br>
with _EseTransaction(self._sesid):<br>
self._seekForKey(key)<br>
return
self._retrieveCurrentRecordValue()</span><span style='font-size:10.0pt;
font-family:"Tahoma","sans-serif";color:black'><br>
</span><span style='font-size:10.0pt;font-family:Consolas;color:black'><br>
def _makeKey(self, key):<br>
"""Construct a key for the
given value."""<br>
Api.MakeKey(self._sesid, self._tableid,
str(key), Encoding.Unicode, MakeKeyGrbit.NewKey)<br>
<br>
def _seekForKey(self, key):<br>
"""Seek for the specified
key. A KeyError exception is raised if the key isn't found."""<br>
self._makeKey(key)<br>
if not Api.TrySeek(self._sesid,
self._tableid, SeekGrbit.SeekEQ):<br>
raise
KeyError('key \'%s\' was not found' % key)</span><span style='font-size:10.0pt;
font-family:"Tahoma","sans-serif";color:black'><br>
<br>
I tried moving the call to Api.MakeKey into _seekForKey but that didn't improve
things. It is definitely the call to Api.MakeKey that is slowing things down --
removing that one call speeds things up. The code that tests the performance
looks like this:<br>
<br>
</span><span style='font-size:10.0pt;font-family:Consolas;color:black'>def insertRetrieveTest():<br>
...<br>
timer = Stopwatch.StartNew()<br>
for i in xrange(n):<br>
data = db[k]<br>
timer.Stop()<br>
...<br>
</span><span style='font-size:10.0pt;font-family:"Tahoma","sans-serif";
color:black'><br>
# Basic test first<br>
insertRetrieveTest()<o:p></o:p></span></p>
</div>
<div>
<div class=MsoNormal align=center style='text-align:center'><span
style='color:black'>
<hr size=2 width="100%" align=center>
</span></div>
<div id=divRpF683459>
<p class=MsoNormal style='margin-bottom:12.0pt'><b><span style='font-size:10.0pt;
font-family:"Tahoma","sans-serif";color:black'>From:</span></b><span
style='font-size:10.0pt;font-family:"Tahoma","sans-serif";color:black'>
users-bounces@lists.ironpython.com [users-bounces@lists.ironpython.com] on
behalf of Dino Viehland [dinov@microsoft.com]<br>
<b>Sent:</b> Tuesday, April 20, 2010 12:51 PM<br>
<b>To:</b> Discussion of IronPython<br>
<b>Subject:</b> Re: [IronPython]Bad performance calling .NET method</span><span
style='color:black'><o:p></o:p></span></p>
</div>
<div>
<div>
<p class=MsoNormal><span style='font-size:11.0pt;font-family:"Calibri","sans-serif";
color:#1F497D'>Is the perf problem only there when the code is global? In
general we don’t try to optimize code which occurs at the top-level of a
script – we assume the most significant work will occur inside of
function definitions. In this case the update to “i” through
each loop iteration needs to update a global value in a dictionary rather than
updating a local on the stack each time which is going to be much more
expensive. Also the reads from the globals instead of parameters will be
much more expensive as well. And while I doubt this is much of the perf
problem you’re also accessing the Unicode property on encoding each time
through in the global case. There’s also the chance depending on
what version of IronPython you’re running on that we’re staying in
the interpreter. I think on 2.6.1 we should compile the global loop
eventually but it’ll still be slower than the compiled function.</span><span
style='color:black'><o:p></o:p></span></p>
<p class=MsoNormal><span style='font-size:11.0pt;font-family:"Calibri","sans-serif";
color:#1F497D'> </span><span style='color:black'><o:p></o:p></span></p>
<p class=MsoNormal><span style='font-size:11.0pt;font-family:"Calibri","sans-serif";
color:#1F497D'>Is there a reason the top-level code needs to be efficient?</span><span
style='color:black'><o:p></o:p></span></p>
<p class=MsoNormal><span style='font-size:11.0pt;font-family:"Calibri","sans-serif";
color:#1F497D'> </span><span style='color:black'><o:p></o:p></span></p>
<div style='border:none;border-left:solid windowtext 1.5pt;padding:0in 0in 0in 4.0pt;
border-color:-moz-use-text-color -moz-use-text-color -moz-use-text-color blue'>
<div>
<div style='border:none;border-top:solid windowtext 1.0pt;padding:3.0pt 0in 0in 0in;
border-color:-moz-use-text-color -moz-use-text-color'>
<p class=MsoNormal><b><span style='font-size:10.0pt;font-family:"Tahoma","sans-serif";
color:black'>From:</span></b><span style='font-size:10.0pt;font-family:"Tahoma","sans-serif";
color:black'> users-bounces@lists.ironpython.com
[mailto:users-bounces@lists.ironpython.com] <b>On Behalf Of </b>Laurion
Burchall<br>
<b>Sent:</b> Tuesday, April 20, 2010 11:57 AM<br>
<b>To:</b> Discussion of IronPython<br>
<b>Subject:</b> Re: [IronPython] Bad performance calling .NET method</span><span
style='color:black'><o:p></o:p></span></p>
</div>
</div>
<p class=MsoNormal><span style='color:black'> <o:p></o:p></span></p>
<div>
<div>
<p class=MsoNormal style='margin-bottom:12.0pt'><span style='font-size:10.0pt;
font-family:"Tahoma","sans-serif";color:black'>You were right about the
structs. By creating a method with the same type of signature (struct, struct,
string, Encoding, enumeration). I got the same slowdown in a trivial DLL. This
is with the .NET 2.0 version of IP:<br>
<br>
** First, the C# code:<br>
<br>
</span><span style='font-size:10.0pt;font-family:Consolas;color:black'>namespace
IronPythonInteropPerf<br>
{<br>
using System.Text;<br>
<br>
public struct Struct1<br>
{<br>
internal int i;<br>
}<br>
<br>
public struct Struct2<br>
{<br>
internal int i;<br>
}<br>
<br>
public enum Enumeration<br>
{<br>
Foo,<br>
}<br>
<br>
public static class Interop<br>
{<br>
public static void A(Struct1 a,
Struct2 b, string x, Encoding encoding, Enumeration e)<br>
{<br>
}<br>
}<br>
}<br>
</span><span style='font-size:10.0pt;font-family:"Tahoma","sans-serif";
color:black'><br>
** Now the IronPython test:<br>
<br>
</span><span style='font-size:10.0pt;font-family:Consolas;color:black'>import
System<br>
import System.Diagnostics<br>
import System.Text<br>
<br>
from System.Diagnostics import Stopwatch<br>
from System.Text import Encoding<br>
<br>
import clr<br>
clr.AddReferenceByPartialName('IronPythonInteropPerf')<br>
from IronPythonInteropPerf import *<br>
<br>
N = 1000000<br>
<br>
a = Struct1()<br>
b = Struct2()<br>
c = 'hello'<br>
d = Enumeration.Foo<br>
<br>
stopwatch = Stopwatch.StartNew()<br>
for i in xrange(N):<br>
Interop.A(a, b, c, Encoding.Unicode, d)<br>
stopwatch.Stop()<br>
print ' A: %s' % stopwatch.Elapsed<br>
<br>
def foo(a1, a2, a3, a4, a5):<br>
stopwatch = Stopwatch.StartNew()<br>
for j in xrange(N):<br>
Interop.A(a1, a2, a3, a4, a5)<br>
stopwatch.Stop()<br>
print 'foo.A: %s' % stopwatch.Elapsed<br>
<br>
foo(a, b, c, Encoding.Unicode, d)<br>
<br>
</span><span style='font-size:10.0pt;font-family:"Tahoma","sans-serif";
color:black'>** Note that I am calling Interop.A twice. This gives very
different results -- the call from inside of function foo() is fast (11M
calls/second) but the call from the script is slow (50K calls/second):<br>
<br>
A: 00:00:18.6063353<br>
foo.A: 00:00:00.0894888<br>
<br>
There isn't a caching effect at work either -- foo() is faster if I call it
before the other code.</span><span style='color:black'><o:p></o:p></span></p>
</div>
<div>
<div class=MsoNormal align=center style='text-align:center'><span
style='color:black'>
<hr size=2 width="100%" align=center>
</span></div>
<div id=divRpF994385>
<p class=MsoNormal style='margin-bottom:12.0pt'><b><span style='font-size:10.0pt;
font-family:"Tahoma","sans-serif";color:black'>From:</span></b><span
style='font-size:10.0pt;font-family:"Tahoma","sans-serif";color:black'>
users-bounces@lists.ironpython.com [users-bounces@lists.ironpython.com] on
behalf of Dino Viehland [dinov@microsoft.com]<br>
<b>Sent:</b> Tuesday, April 20, 2010 11:34 AM<br>
<b>To:</b> Discussion of IronPython<br>
<b>Subject:</b> Re: [IronPython]Bad performance calling .NET method</span><span
style='color:black'><o:p></o:p></span></p>
</div>
<div>
<div>
<p class=MsoNormal><span style='font-size:11.0pt;font-family:"Calibri","sans-serif";
color:#1F497D'>I assume something is going horribly wrong with our type
checks. Can you attach the repro? Or at least are these just
classes, or are any structs, or maybe weird classes like delegates?</span><span
style='color:black'><o:p></o:p></span></p>
<p class=MsoNormal><span style='font-size:11.0pt;font-family:"Calibri","sans-serif";
color:#1F497D'> </span><span style='color:black'><o:p></o:p></span></p>
<p class=MsoNormal><span style='font-size:11.0pt;font-family:"Calibri","sans-serif";
color:#1F497D'>And is this on .NET 2.0 or .NET 4.0?</span><span
style='color:black'><o:p></o:p></span></p>
<p class=MsoNormal><span style='font-size:11.0pt;font-family:"Calibri","sans-serif";
color:#1F497D'> </span><span style='color:black'><o:p></o:p></span></p>
<div style='border:none;border-left:solid windowtext 1.5pt;padding:0in 0in 0in 4.0pt;
border-color:-moz-use-text-color -moz-use-text-color -moz-use-text-color windowtext'>
<div>
<div style='border:none;border-top:solid windowtext 1.0pt;padding:3.0pt 0in 0in 0in;
border-color:-moz-use-text-color -moz-use-text-color'>
<p class=MsoNormal><b><span style='font-size:10.0pt;font-family:"Tahoma","sans-serif";
color:black'>From:</span></b><span style='font-size:10.0pt;font-family:"Tahoma","sans-serif";
color:black'> users-bounces@lists.ironpython.com [mailto:users-bounces@lists.ironpython.com]
<b>On Behalf Of </b>Laurion Burchall<br>
<b>Sent:</b> Tuesday, April 20, 2010 2:11 AM<br>
<b>To:</b> Discussion of IronPython<br>
<b>Subject:</b> [IronPython] Bad performance calling .NET method</span><span
style='color:black'><o:p></o:p></span></p>
</div>
</div>
<p class=MsoNormal><span style='color:black'> <o:p></o:p></span></p>
<div>
<div>
<p class=MsoNormal><span style='font-size:10.0pt;font-family:"Tahoma","sans-serif";
color:black'>I am getting terrible performance invoking a C# method from IP. I
have a static class called Api with this method:<br>
<br>
public static void MakeKey(JET_SESID sesid, JET_TABLEID tableid, string
data, Encoding encoding, MakeKeyGrbit grbit)<br>
<br>
When I call it directly from C# I get about 3M calls/second. In IronPython I
get only 50,000 calls/second -- a 60X slowdown!<br>
<br>
The method is overloaded. When I call these overloads I get good performance (~
1M calls/second):<br>
public static void MakeKey(JET_SESID sesid, JET_TABLEID tableid, int
data, MakeKeyGrbit grbit)<br>
public static void MakeKey(JET_SESID sesid, JET_TABLEID tableid, float
data, MakeKeyGrbit grbit)<br>
public static void MakeKey(JET_SESID sesid, JET_TABLEID tableid, byte[]
data, MakeKeyGrbit grbit)<br>
(for the last overload I passed in the string turned into a byte array with
Encoding.GetBytes())<br>
<br>
Things I have tried that didn't help:<br>
- Changing the name of the method so it was unique.<br>
- Calling the method using Api.MakeKey.Overloads[...]<br>
- Calling other methods I have that take string arguments. They were
fast.<br>
<br>
When I profile the code the time shows up in mscorwks.dll (56%) and
mscorlib.ni.dll (17%). IronPython is only 8% and my code is 6%.<br>
<br>
Can anyone help me work out what is going wrong? I have a short, turn-key repro
of this. MakeKey is a very commonly used method so having it be so slow is
crippling.<br>
<br>
thanks,<br>
--Laurion<br>
<br>
</span><span style='color:black'><o:p></o:p></span></p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>