<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Sufficiently Small &#187; Jython</title>
	<atom:link href="http://www.smallshire.org.uk/sufficientlysmall/category/computing/software/python/jython/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.smallshire.org.uk/sufficientlysmall</link>
	<description>sin(x) = x</description>
	<lastBuildDate>Sun, 11 Apr 2010 19:36:07 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.4</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>String compatibility between Python implementations</title>
		<link>http://www.smallshire.org.uk/sufficientlysmall/2009/06/18/string-compatibility-between-python-implementations/</link>
		<comments>http://www.smallshire.org.uk/sufficientlysmall/2009/06/18/string-compatibility-between-python-implementations/#comments</comments>
		<pubDate>Thu, 18 Jun 2009 14:28:51 +0000</pubDate>
		<dc:creator>Robert Smallshire</dc:creator>
				<category><![CDATA[IronPython]]></category>
		<category><![CDATA[Jython]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[computing]]></category>
		<category><![CDATA[software]]></category>

		<guid isPermaLink="false">http://www.smallshire.org.uk/sufficientlysmall/?p=340</guid>
		<description><![CDATA[Jython and IronPython run on platforms where strings are unicode capable by default. Both implementations have chosen to make str essentially an alias for unicode in Python source code. The bytes type, introduced in PEP358 as part of transition to fully unicode Python 3.0, is unambiguously a sequence of single byte values. We can see [...]]]></description>
			<content:encoded><![CDATA[<p>Jython and IronPython run on platforms where strings are unicode capable by default. Both implementations have chosen to make <code>str</code> essentially an alias for <code>unicode</code> in Python source code. The <code>bytes</code> type, introduced in <a href="http://www.python.org/dev/peps/pep-0358/">PEP358</a> as part of transition to fully unicode Python 3.0, is unambiguously a sequence of single byte values. We can see in the table below that Jython and IronPython are caught between what is on the one hand most practical for interopability with existing code and their host platforms, and on the other hand the Right Thing as delivered by Python 3.0.</p>
<table>
<tr>
<th></th>
<th>Jython 2.5</th>
<th>IronPython 2.6</th>
<th>CPython 2.6</th>
<th>CPython 3.0</th>
</tr>
<tr>
<th>str</th>
<td>multibyte</td>
<td>multibyte</td>
<td>byte</td>
<td>multibyte</td>
</tr>
<tr>
<th>unicode</th>
<td>multibyte</td>
<td>multibyte</td>
<td>multibyte</td>
<td>multibyte</td>
</tr>
<tr>
<th>bytes</th>
<td>byte</td>
<td>byte</td>
<td>byte</td>
<td>byte</td>
</tr>
</table>
<p>It seems clear that if you need to write code that is portable between the different Python implementations you should steer clear <code>str</code> and use <code>bytes</code> and <code>unicode</code> to unambigiously express your intent.</p>
<p>Of course, this is impossible since the Python Standard Library is littered with uses of <code>str</code>. For example, in IronPython <code>pickle.dumps()</code> returns <code>str</code> just like Python 2.6 but the <code>str</code> is actually has multibyte storage.  IronPython hides this well, but the abstraction can leak, resulting in much confusion.  Again Python 3.0 does what is right, and <code>pickle.dumps()</code> returns a <code>bytes</code> instance.</p>
<p>These difficulties are most likely to occur when interfacing with native Java or .NET APIs that expect byte arrays, for example when pickling to database blobs. </p>
<p>In Jython an <code>str</code> instance can be converted to a Java byte array as follows.</p>
<pre class="brush: python;">
&gt;&gt;&gt; import jarray
&gt;&gt;&gt; a = jarray.array(&quot;This is  string&quot;, 'b')
&gt;&gt;&gt; a
array('b', [84, 104, 105, 115, 32, 105, 115, 32, 32, 115, 116, 114, 105, 110, 103])
</pre>
<p>The equivalent in IronPython, as provided by <a href="http://www.voidspace.org.uk/python/weblog/">Michael Foord</a>,  being,</p>
<pre class="brush: python;">
&gt;&gt;&gt; from System import Array, Byte
&gt;&gt;&gt; a = Array[Byte](tuple(Byte(ord(c)) for c in &quot;This is a string&quot;))
&gt;&gt;&gt; a
Array[Byte]((&lt;System.Byte object at 0x000000000000002B [84]&gt;, &lt;System.Byte object at 0x000000000000002C [104]&gt;, &lt;System.Byte object at 0x000000000000002D [105]&gt;, &lt;System.Byte object at 0x000000000000002E [115]&gt;, &lt;System.Byte object at 0x000000000000002F [32]&gt;, &lt;System.Byte object at 0x0000000000000030 [105]&gt;, &lt;System.Byte object at 0x0000000000000031 [115]&gt;, &lt;System.Byte object at 0x0000000000000032 [32]&gt;, &lt;System.Byte object at 0x0000000000000033 [97]&gt;, &lt;System.Byte object at 0x0000000000000034 [32]&gt;, &lt;System.Byte object at 0x0000000000000035 [115]&gt;, &lt;System.Byte object at 0x0000000000000036 [116]&gt;, &lt;System.Byte object at 0x0000000000000037 [114]&gt;, &lt;System.Byte object at 0x0000000000000038 [105]&gt;, &lt;System.Byte object at 0x0000000000000039 [110]&gt;, &lt;System.Byte object at 0x000000000000003A [103]&gt;))
</pre>
<p>Going back we can use identical code in IronPython and Jython.</p>
<pre class="brush: python;">
&gt;&gt;&gt; s = ''.join(chr(c) for c in a)
&gt;&gt;&gt; s
'This is a string'
</pre>
<hr/>Copyright &copy; 2010 <strong><a href="http://www.smallshire.org.uk/sufficientlysmall">Sufficiently Small</a></strong>. This Feed is for personal non-commercial use only. If you are not reading this material in your news aggregator, the site you are looking at is guilty of copyright infringement. Please contact legal@smallshire.org.uk so we can take legal action immediately.<br/><span style="float: right;font-size: 7pt"><a href="http://blog.taragana.com/index.php/archive/wordpress-plugins-provided-by-taraganacom/">Plugin</a> by <a href="http://www.taragana.com/">Taragana</a></span>]]></content:encoded>
			<wfw:commentRss>http://www.smallshire.org.uk/sufficientlysmall/2009/06/18/string-compatibility-between-python-implementations/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>IronPython hammers CPython when not mutating class attributes</title>
		<link>http://www.smallshire.org.uk/sufficientlysmall/2009/05/22/ironpython-hammers-cpython-when-not-mutating-class-attributes/</link>
		<comments>http://www.smallshire.org.uk/sufficientlysmall/2009/05/22/ironpython-hammers-cpython-when-not-mutating-class-attributes/#comments</comments>
		<pubDate>Fri, 22 May 2009 18:52:58 +0000</pubDate>
		<dc:creator>Robert Smallshire</dc:creator>
				<category><![CDATA[.NET]]></category>
		<category><![CDATA[IronPython]]></category>
		<category><![CDATA[Jython]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[computing]]></category>
		<category><![CDATA[software]]></category>

		<guid isPermaLink="false">http://www.smallshire.org.uk/sufficientlysmall/?p=171</guid>
		<description><![CDATA[Earlier today I posted the second article in what is turning out to be a short series in the investigation into why the performance of IronPython is around 100× slower than CPython, when running the front-end of my OWL BASIC compiler.
The most informative comment was from Curt Hagenlocher who works on IronPython in the Visual [...]]]></description>
			<content:encoded><![CDATA[<p>Earlier today I posted the <a href="http://www.smallshire.org.uk/sufficientlysmall/2009/05/22/ironpython-2-0-and-jython-2-5-performance-compared-to-python-2-5/">second article</a> in what is turning out to be a short series in the investigation into why the performance of IronPython is around 100× slower than CPython, when running the front-end of my OWL BASIC compiler.</p>
<p>The most informative <a href="http://www.smallshire.org.uk/sufficientlysmall/2009/05/22/ironpython-2-0-and-jython-2-5-performance-compared-to-python-2-5/#comment-2071">comment</a> was from <a href="http://blogs.msdn.com/curth/">Curt Hagenlocher</a> who works on IronPython in the Visual Studio Managed Languages group at Microsoft.</p>
<p>Curt suggested,</p>
<blockquote><p>
Try storing the counter as a global variable instead of a class-level member of Node — I think you’ll notice a dramatic improvement.
</p></blockquote>
<p>The modified benchmark program looks like this:</p>
<pre class="brush: python;">
counter = 0

class Node(object):

    def __init__(self, children):
        global counter
        counter += 1
        self._children = children

def make_tree(depth):
    if depth &gt; 1:
        return Node ([make_tree(depth - 1), make_tree(depth - 1)])
    else:
        return Node([])

def main(argv=None):
    global counter
    if argv is None:
        argv = sys.argv
    depth = int(argv[1]) if len(argv) &gt; 1 else 10

    root = make_tree(depth)
    print counter
    return 0

if __name__ == '__main__':
    import sys
    sys.exit(main())
</pre>
<h2>A dramatic improvement!</h2>
<p>Well, Curt wasn&#8217;t wrong. This made a phenomenal difference with IronPython completing in only 12% of the time taken by CPython &#8211; over 8× <em>faster</em> with a binary tree depth of 20.</p>
<p>Let&#8217;s look in detail at the results.  All results are from a dual quad-core 1.86 GHz Xeon with 4 GB RAM, and as before each benchmark was run five times, and the shortest time of the five taken.</p>
<p>The three test environments are:</p>
<ol>
<li>Python 2.5.2 x86 32-bit</li>
<li>Jython 2.5rc2 on Java Hotspot 1.6 32-bit</li>
<li>IronPython 2.0 on .NET 2.0 x64</li>
</ol>
<div class="wp-caption alignnone" style="width: 610px"><img alt="The relative performance of the three main Python implementations on a benchmark that uses a global counter, rather than mutating a class attribute." src="/sufficientlysmall/wp-content/ipy_performance/tree_x64_inclusive_global.png" title="The relative performance of the three main Python implementations" width="600" height="450" /><p class="wp-caption-text">Figure 1. The relative performance of the three main Python implementations on a benchmark that uses a global counter, rather than mutating a class attribute.</p></div>
<p>Here we can see how IronPython&#8217;s performance has been improved hugely by this simple change. Although startup time dominates for the smaller problem size, now both Jython <em>and</em> IronPython surpass CPython at around half-a-million nodes.</p>
<p>Removing start-up time, which may be irrelevant for long-running processes, gives us the following chart:</p>
<div class="wp-caption alignnone" style="width: 610px"><img alt="The performance of the three main Python implementations excluding start-up time." src="/sufficientlysmall/wp-content/ipy_performance/tree_x64_exclusive_global.png" title="The performance of the three main Python implementations excluding start-up time." width="600" height="450" /><p class="wp-caption-text">Figure 2. The performance of the three main Python implementations excluding start-up time.</p></div>
<p>Again there is a lot of noise in the data below 1000 nodes, but it is clear that Jython scales better than IronPython, which in turn is scaling better than CPython.</p>
<p>Up until now I&#8217;ve been using a log-log scale in the charts because of the wide variation in performance between the different implementations, but now the performance gap is much closer, it&#8217;s difficult to get a sense of just how <i>much</i> faster IronPython is on the modified benchmark.  Let&#8217;s throw in a log-linear plot to help us appreciate what&#8217;s going on:</p>
<div class="wp-caption alignnone" style="width: 610px"><img alt="Figure 3. A linear representation of the same data as in Figure 2, to highlight the performance multiple between IronPython and CPython in the larger tests." src="/sufficientlysmall/wp-content/ipy_performance/tree_x64_linear_global.png" title="A linear representation of the same data as in Figure 2, to highlight the performance multiple between IronPython and CPython in the larger tests." width="600" height="450" /><p class="wp-caption-text">Figure 3. A linear representation of the same data as in Figure 2, to highlight the performance multiple between IronPython and CPython in the larger tests.</p></div>
<p>It&#8217;s perhaps easier to see now that IronPython is doing in 14 seconds what takes CPython 114 seconds to achieve!</p>
<p>Finally, let&#8217;s plot those results as we did before, as multiples of CPython performance:</p>
<div class="wp-caption alignnone" style="width: 610px"><img alt="Figure 4. Execution time of Jython and IronPython as multiples of CPython performance." src="/sufficientlysmall/wp-content/ipy_performance/tree_x64_relative_global.png" title="Figure 4. Execution time of Jython and IronPython as multiples of CPython performance." width="600" height="450" /><p class="wp-caption-text">Figure 4. Execution time of Jython and IronPython as multiples of CPython performance.</p></div>
<p>It is easy to see that in this chart, once we pass half-a-million tree nodes (a tree depth of 19) that both Jython and IronPython are significantly beating CPython.</p>
<h2>Explanation</h2>
<p>Curt Hagenlocher offers the explanation in a <a href="http://www.reddit.com/r/programming/comments/8mfh7/terrible_ironpython_20_and_pretty_good_jython_25/c09r51x">comment</a> in the <a href="http://www.reddit.com/r/programming/comments/8mfh7/terrible_ironpython_20_and_pretty_good_jython_25/">thread </a> on <a href="http://www.reddit.com">Reddit</a>.</p>
<blockquote><p>
In this particular case, IronPython is slow because of the update to Node.counter. Currently, any update to a class will increment the version number for the class, which will have the effect of invalidating any rules compiled for that class. Effectively, the same rules are getting compiled over and over again. Moving the counter to a global should result in performance on par with that of CPython.
</p></blockquote>
<p>which is absolutely correct, except that he&#8217;s underselling the relative gain. IronPython is not only on a par with CPython, it can outperform it by a factor of eight!</p>
<p>With this knowledge in hand, I can now approach optimization of my OWL BASIC compiler, which lies back at the start of this illuminating tale.</p>
<h2>Conclusion</h2>
<ul>
<li>Avoiding mutation of Python class attributes can have significant benefits for IronPython performance.</li>
<li>Both IronPython and Jython scale better than CPython by this benchmark, and have superior performance for large trees of nodes.</li>
</ul>
<hr/>Copyright &copy; 2010 <strong><a href="http://www.smallshire.org.uk/sufficientlysmall">Sufficiently Small</a></strong>. This Feed is for personal non-commercial use only. If you are not reading this material in your news aggregator, the site you are looking at is guilty of copyright infringement. Please contact legal@smallshire.org.uk so we can take legal action immediately.<br/><span style="float: right;font-size: 7pt"><a href="http://blog.taragana.com/index.php/archive/wordpress-plugins-provided-by-taraganacom/">Plugin</a> by <a href="http://www.taragana.com/">Taragana</a></span>]]></content:encoded>
			<wfw:commentRss>http://www.smallshire.org.uk/sufficientlysmall/2009/05/22/ironpython-hammers-cpython-when-not-mutating-class-attributes/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>IronPython 2.0 and Jython 2.5 performance compared to Python 2.5</title>
		<link>http://www.smallshire.org.uk/sufficientlysmall/2009/05/22/ironpython-2-0-and-jython-2-5-performance-compared-to-python-2-5/</link>
		<comments>http://www.smallshire.org.uk/sufficientlysmall/2009/05/22/ironpython-2-0-and-jython-2-5-performance-compared-to-python-2-5/#comments</comments>
		<pubDate>Fri, 22 May 2009 11:34:28 +0000</pubDate>
		<dc:creator>Robert Smallshire</dc:creator>
				<category><![CDATA[.NET]]></category>
		<category><![CDATA[IronPython]]></category>
		<category><![CDATA[Jython]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[computing]]></category>
		<category><![CDATA[software]]></category>
		<category><![CDATA[BBCBASIC]]></category>
		<category><![CDATA[CLR]]></category>
		<category><![CDATA[dotNET]]></category>

		<guid isPermaLink="false">http://www.smallshire.org.uk/sufficientlysmall/?p=118</guid>
		<description><![CDATA[IronPython 2.0 can be hundreds of times slower than CPython on some microbenchmarks.  Jython 2.5 can scale better than CPython on those same benchmarks.]]></description>
			<content:encoded><![CDATA[<p>My <a href="http://www.smallshire.org.uk/sufficientlysmall/2009/05/17/the-performance-of-python-jython-and-ironpython/">previous post</a> covering the performance problems I&#8217;ve been experiencing with IronPython raised some questions about whether the low performance was an effect peculiar to my system, or to my program &#8212; the <a href="http://www.smallshire.org.uk/sufficientlysmall/2007/06/10/writing-a-bbc-basic-compiler-for-the-clr/">OWL BASIC</a> compiler &#8212; where the problem was first noticed. To briefly recap, I&#8217;d determined that IronPython was around 100× slower that CPython on the same program.</p>
<p>Since then, I&#8217;ve had time to reproduce the results with a small and completely unremarkable Python program, and also to run the tests on a different system. I had suspected that in the OWL BASIC compiler, my Python visitor implementation, which is used in applying transformations to the abstract syntax tree, was to blame. I set about condensing a tree visitor down to a small example, but I never got that far.  It is sufficient to simply <i>build</i> a large binary tree to demonstrate the dramatic differences in the performance characteristics of the three main Python implementations.</p>
<h2>The benchmark</h2>
<p>Here is that test program, which just builds a simple binary tree of objects to the requested depth.</p>
<pre class="brush: python;">
class Node(object):
    counter = 0

    def __init__(self, children):
        Node.counter += 1
        self._children = children

def make_tree(depth):
    if depth &gt; 1:
        return Node ([make_tree(depth - 1), make_tree(depth - 1)])
    else:
        return Node([])

def main(argv=None):
    if argv is None:
        argv = sys.argv
    depth = int(argv[1]) if len(argv) &gt; 1 else 10

    root = make_tree(depth)
    print Node.counter
    return 0

if __name__ == '__main__':
    import sys
    sys.exit(main())
</pre>
<p>The program builds a binary tree to the depth supplied as the only command line argument, or ten if one is not supplied. It counts the number of nodes as they a built. Remember that the merits or otherwise of this program are not the point! The point is the performance difference between the Python implementations when it is run.</p>
<p>My benchmarking approach has been to run this script five times for each tree depth from a depth of one, upwards to 22, or until my patience was exhausted.  I&#8217;ve taken the minimum time from each run of five. Since there is a non-linear relationship between the depth of the tree and the number of nodes contained therein, logarithmic axes are used in all the charts that follow.</p>
<h2>64 bit Windows Vista x64</h2>
<p>Here are the results for the first test machine &#8211; with dual quad-core 1.86 GHz Xeons with 4 GB RAM running Vista x64, testing IronPython 2.0.0.0 on .NET 2.0, Jython 2.5rc2 on Java Hotspot 1.6.0 and Python 2.5.2.</p>
<div class="wp-caption alignnone" style="width: 610px"><img alt="Create time for a binary tree including Python virtual machine startup on Windows Vista x64 with 1.86 GHz Xeon processors." src="/sufficientlysmall/wp-content/ipy_performance/tree_x64_inclusive.png" title="Binary tree creation on x64" width="600" height="450" /><p class="wp-caption-text">Figure 1. Creation time for a binary tree including Python virtual machine startup on Windows Vista x64 with 1.86 GHz Xeon processors.</p></div>
<p>In Figure 1 we see that above 1000 nodes or so (tree depth of 10) performance for IronPython begin to degrade rapidly. CPython holds out for another two orders of magnitude before the significant costs begin to be felt . Its interesting to see that although Jython is in the middle of the pack, it scales much better than CPython, surpassing it at around half-a-million nodes (tree depth of 19).</p>
<p>In my application &#8212; a compiler &#8212; virtual machine (VM) start-up time is important; however, in many long-running applications this is not the case, so it is interesting to subtract VM start-up time from each series, which we see in Figure 2, below.</p>
<div class="wp-caption alignnone" style="width: 610px"><img alt="By subtracting VM start-up time, we get a picture more interesting for long-running processes." src="/sufficientlysmall/wp-content/ipy_performance/tree_x64_exclusive.png" title="Execution time excluding VM start-up, on Vista x64 with 1.87 GHz Xeon processors" width="600" height="450" /><p class="wp-caption-text">By subtracting VM start-up time, we get a picture more interesting for long-running processes.</p></div>
<p>Below 100 tree nodes, there is a lot of noise in these measurements. Above 100 nodes its easy to see that the blue IronPython curve is at least two chart divisions above the red CPython curve &#8212; that&#8217;s two orders of magnitude or 100× slower, and getting relatively worse as the size of the tree increases.</p>
<h2>32 bit Windows XP x86</h2>
<p>Responses to my earlier article suggested that trying IronPython 2.0.1 with Ngen&#8217;ed binaries on x86 may make a difference.  Well, to cut a long story short, it doesn&#8217;t, but here are the details.   These tests were run on a 900 MHz Pentium M Centrino laptop with 768 MB RAM, and so cannot be directly compared with those above, although its notable that a one year old workstation is only twice as fast as a five year old laptop.  Moore&#8217;s law certainly isn&#8217;t delivering here!</p>
<div class="wp-caption alignnone" style="width: 610px"><img alt="The performance profiles are very similar with IronPython 2.0.1 on x86." src="/sufficientlysmall/wp-content/ipy_performance/tree_x86_exclusive.png" title="Performance for building a binary tree on a 900 MHz Pentium M." width="600" height="450" /><p class="wp-caption-text">The performance profiles are very simular with IronPython 2.0.1 on x86.</p></div>
<p>On x86, IronPython is still 100× slower than CPython, and Jython still scales better.  It seems the essence of this benchmark is not dependent on which hardware or CLR platform it is run.</p>
<p>I&#8217;ll close by re-presenting the data in the x86 benchmarks as multiples of CPython performance, because it dramatically demonstrates the different responses to the scale of the problem size for IronPython and Jython. Again we see Jython catching up with CPython at a tree depth of 19, just we saw on x64. and IronPython delivering 6000× worse than CPython at a tree depth depth of 15. A tree of this size with thirty-thousand nodes is very similar in scale to the AST tree sizes found in the OWL BASIC during compilation of large programs.</p>
<div class="wp-caption alignnone" style="width: 610px"><img alt="Performance of IronPython and Jython as multiples of CPython performance." src="/sufficientlysmall/wp-content/ipy_performance/tree_x86_relative.png" title="Performance of IronPython and Jython as multiples of CPython performance." width="600" height="450" /><p class="wp-caption-text">Performance of IronPython and Jython as multiples of CPython performance.</p></div>
<h2>Conclusions</h2>
<ul>
<li>
IronPython can be <strong>very</strong> slow, even on programs in the microbenchmark category, which are doing standard operations such as building trees. Presumably there are still significant optimizations to be made in IronPython to bring its performance closer to that of the other Python implementations.  Hopefully, this example and the measurements can contribute to that improvement.
</li>
<li>
Jython may scale better than Python if your application exercises Python in similar ways to this benchmark.  Speculatively, that <i>could</i> have implications for projects such as <a href="http://www.scons.org/">SCons</a>, which build large in-memory graphs.
</li>
<li>I suppose if nothing else we have demonstrated in passing that Java <i>can</i> be faster than C for some non-trivial programs (like a Python interpreter) running a trivial program, like this benchmark.</li>
</ul>
<hr/>Copyright &copy; 2010 <strong><a href="http://www.smallshire.org.uk/sufficientlysmall">Sufficiently Small</a></strong>. This Feed is for personal non-commercial use only. If you are not reading this material in your news aggregator, the site you are looking at is guilty of copyright infringement. Please contact legal@smallshire.org.uk so we can take legal action immediately.<br/><span style="float: right;font-size: 7pt"><a href="http://blog.taragana.com/index.php/archive/wordpress-plugins-provided-by-taraganacom/">Plugin</a> by <a href="http://www.taragana.com/">Taragana</a></span>]]></content:encoded>
			<wfw:commentRss>http://www.smallshire.org.uk/sufficientlysmall/2009/05/22/ironpython-2-0-and-jython-2-5-performance-compared-to-python-2-5/feed/</wfw:commentRss>
		<slash:comments>29</slash:comments>
		</item>
		<item>
		<title>Dismal performance with IronPython</title>
		<link>http://www.smallshire.org.uk/sufficientlysmall/2009/05/17/the-performance-of-python-jython-and-ironpython/</link>
		<comments>http://www.smallshire.org.uk/sufficientlysmall/2009/05/17/the-performance-of-python-jython-and-ironpython/#comments</comments>
		<pubDate>Sun, 17 May 2009 20:56:46 +0000</pubDate>
		<dc:creator>Robert Smallshire</dc:creator>
				<category><![CDATA[.NET]]></category>
		<category><![CDATA[Jython]]></category>
		<category><![CDATA[OWL BASIC]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[computing]]></category>
		<category><![CDATA[software]]></category>
		<category><![CDATA[IronPython CLR dotNET BBCBASIC]]></category>

		<guid isPermaLink="false">http://www.smallshire.org.uk/sufficientlysmall/?p=63</guid>
		<description><![CDATA[IronPython can be slow - 10x to 100x slower than CPython on real-world code and it has been observed to be up to 6000x slower.]]></description>
			<content:encoded><![CDATA[<p>Significant claims have been made about the performance of <a href="http://ironpython.codeplex.com/">IronPython</a>, notably back at its inception in 2004 when Jim Hugunin, creator of both IronPython and its cousin <a href="http://www.jython.org/">Jython</a>, presented a <a href="http://www.python.org/community/pycon/dc2004/papers/9/">paper</a> on IronPython performance at PyCon. Since then, there have been numerous claims to IronPython&#8217;s supremacy over CPython in the performance stakes. The <a href="http://www.codeplex.com/IronPython/Wiki/View.aspx?title=IP20VsCPy25Perf&#038;referringTitle=IronPython%20Performance">IronPython Performance Report</a>  reiterates that IronPython can turn in a good performance. According to Hugenin the standard line we&#8217;ll see is,</p>
<blockquote><p>
&#8220;IronPython is fast &#8211; up to 1.8x faster than CPython on the standard pystone benchmark.
</p></blockquote>
<p>But do these claims stand up in the face of real-world Python code?</p>
<p>The claims of good performance are based on synthetic micro-benchmarks which don&#8217;t accurately reflect balance of coding techniques found in complete programs.  </p>
<p>At this point I&#8217;d like to offer my own quote:</p>
<blockquote><p>
&#8220;IronPython can be slow &#8211; 10x to 100x slower than CPython on real-world code and it has been observed to be up to 6000x slower.
</p></blockquote>
<p>Now, the unfortunate thing about real-world code is that it hasn&#8217;t been hand-crafted to highlight the performance characteristics of some aspect of the language implementation like your typical micro-benchmark. Its been written, most likely without any attention to performance. This is especially in the case of a prototype, as in my case.</p>
<h2>Is my code really that bad?</h2>
<p>Over the past few years I&#8217;ve been working on a hobby project called <a href="http://www.smallshire.org.uk/sufficientlysmall/2007/06/10/writing-a-bbc-basic-compiler-for-the-clr/">OWL BASIC</a>, which is a compiler for the BBC BASIC lanaguage targeting the .NET Common Language Runtime (CLR).  At the outset of the project I decided to write the compiler itself in Python, for much the same reasons as <a href="http://codespeak.net/pypy/dist/pypy/doc/">PyPy</a> is written in Python &#8212; hackability. I planned specifically to use IronPython so I could benefit from access to useful .NET libraries such a <code>Reflection.Emit</code>, for generating Common Intermediate Language (CIL) assemblies.</p>
<p>During the course of developing OWL BASIC, as the code has become more complex, I&#8217;ve been consitently disappointed by the negative delta between the promise and reality of IronPython&#8217;s performance. The poor performance of IronPython has negated, for me, one of the main advantages of developing in Python &#8212; the rapid edit-run cycle.</p>
<p>Was my code really so inefficient?  Performance was never a goal of the project, but the underwhelming performance has threatened the viability of my approach and made me question the wisdom of my chosen route of writing a compiler in Python.</p>
<p>The absence of profiling tools for IronPython led me down the road of getting at least the compiler front-end to work on CPython, so I could use the standard Python profilers. Fortunately, my code was portable (its just regular Python) and so I determined that with a few inconsequential tweaks to my code, the entire compiler front-end can be run on the trinity of CPython, Jython and IronPython.</p>
<p>I was, to say the least, somewhat surprised by the results.</p>
<h2>The evidence: performance of CPython, Jython and IronPython</h2>
<p>The investigation into the relative performance of the three main Python implementations was centered on running my unmodified, unprofiled and unoptimized compiler front-end on third-party BBC BASIC source-code (Acornsoft Sphinx Adventure) and measuring the execution time.  All tests runs were performed five times, and the minimum time of the five chosen. Variance between the readings on successive runs was small.  The following Python implementations were on the test-bench:</p>
<ul>
<li>Python 2.5.1 (x86)</li>
<li>Jython 2.5rc2 on Java HotSpot 1.6.0_10-rc</li>
<li>IronPython 2.0 on .NET 2.0 (x64)</li>
</ul>
<p>All tests were run on a eight-core 1.86 GHz Xeon with 4 GB RAM running Vista Ultimate x64.</p>
<p>The following chart shows the results of running the compiler over the source code for Sphinx Adventure.</p>
<div class="wp-caption alignnone" style="width: 610px"><img alt="ipy_performance/absolute_performance.png" src="/sufficientlysmall/wp-content/ipy_performance/absolute_performance.png" title="Execution time of OWL BASIC compiler front-end" width="600" height="225" /><p class="wp-caption-text">The absolute performance of CPython, Jython and IronPython on my code</p></div>
<p>Frankly, I was astounded.  IronPython was left in the dust, not only by CPython but also Jython!  Overall, CPython was 93× faster on the exact same code.  The IronPython hyperbole, for now I could see that was what is was, had led to me expect numbers similar to those for CPython, although I had perhaps more realistic expectations that performance would be similar to Jython. </p>
<p>At this point I assumed I&#8217;d hit some corner case in which IronPython was performing relatively badly.  I&#8217;d had a similar experience early on in the project with code from the PLY parsing package causing IronPython 1.1 to perform badly, but I&#8217;d worked around the issue by modifying parts of PLY to use pickling rather than eval-ing large list literals for the cached parsing tables. [Its worth noting in passing that this problem still exists with the IronPython and PLY combination - I'll publish my solution in another post].</p>
<p>I decided to dig in a little more detail and collect some timing data on the sequence of top level calls the compiler front-end makes to parse the source code followed by a sequence of transformations to the abstract syntax tree (AST). The chart below shows these top level calls, in the order in which the occur during compilation:</p>
<div class="wp-caption alignnone" style="width: 610px"><img alt="ipy_performance/performance_ratio.png" src="/sufficientlysmall/wp-content/ipy_performance/performance_ratio.png" title="The ratio of performance of CPython to both IronPython and Jython when running the OWL BASIC compiler" width="600" /><p class="wp-caption-text">The ratio of performance of CPython to both IronPython and Jython when running the OWL BASIC compiler</p></div>
<p>As you can see, I have had to resort to a logarithmic scale in order to convey the huge variation in the performance of IronPython relative to CPython, ranging from buildParser with a multiple of 3.8 to convertSubroutinesToProcedures with a multiple of over 6400.  Even if we ignore this outlier (maybe we are suffering from a garbage collection &#8211; but notice that Jython is also relatively much slower on this function) we can see that Jython is typically 5× to 10× slower than CPython whereas IronPython is typically 10× to 100× slower than CPython.</p>
<p>Notice also that there is a marked decline in the performance of IronPython from the parse function onwards; the common factor in these operations are that they are all transformations to the AST, and my AST node classes are instantiated by a Python metaclass, although its pure speculation on my part that metaclasses are the cause of this performance drop.</p>
<h2>Conclusion</h2>
<p>On my program at least, Jython is 6× slower than CPython and IronPython is nearly 100× slower.   If you&#8217;re suffering from poor performance with IronPython, it may be well worth your time checking performance of your code on the other Python implementations, if that is an option for you.</p>
<p>Now need to find the root cause and boil my problem down to a short example which can form the basis of a bug report to the IronPython team.  Given that the problem is pervasive in my code, that won&#8217;t be hard.</p>
<p>See you at EuroPython 2009 !</p>
<hr/>Copyright &copy; 2010 <strong><a href="http://www.smallshire.org.uk/sufficientlysmall">Sufficiently Small</a></strong>. This Feed is for personal non-commercial use only. If you are not reading this material in your news aggregator, the site you are looking at is guilty of copyright infringement. Please contact legal@smallshire.org.uk so we can take legal action immediately.<br/><span style="float: right;font-size: 7pt"><a href="http://blog.taragana.com/index.php/archive/wordpress-plugins-provided-by-taraganacom/">Plugin</a> by <a href="http://www.taragana.com/">Taragana</a></span>]]></content:encoded>
			<wfw:commentRss>http://www.smallshire.org.uk/sufficientlysmall/2009/05/17/the-performance-of-python-jython-and-ironpython/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Jython, Swing &amp; Curry</title>
		<link>http://www.smallshire.org.uk/sufficientlysmall/2005/12/03/jython-swing-curry/</link>
		<comments>http://www.smallshire.org.uk/sufficientlysmall/2005/12/03/jython-swing-curry/#comments</comments>
		<pubDate>Sat, 03 Dec 2005 15:57:45 +0000</pubDate>
		<dc:creator>Robert Smallshire</dc:creator>
				<category><![CDATA[Jython]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[computing]]></category>
		<category><![CDATA[software]]></category>

		<guid isPermaLink="false">http://www.smallshire.org.uk/sufficientlysmall/?p=8</guid>
		<description><![CDATA[...higher-order techniques can be used to aid user-interface programming with Swing...]]></description>
			<content:encoded><![CDATA[<p>Jython is currently, by far the most useful of the the three main Python implementations, <a href="http://www.jython.org">Jython</a>, <a href="http://www.python.org">CPython</a> and <a href="http://www.ironpython.com">IronPython</a>. It allows access to the Java standard library which often provides more standard, better defined, and certainly better documented alternatives to much of what is in the Python standard library. It is also more cross-platform than .NET bound IronPython and offers superior multithreading support to CPython.</p>
<p>Its well known within the Jython community that having first-class functions in Python makes development with Swing more productive in Jython than it is with Java, by allowing us to attach functions or methods directly to Swing callbacks such as <code>actionPerformed</code>, without the need for anonymous classes or excessive need for interface implementation.  This example taken from <a href="http://www.catalysoft.com/articles/GoodAboutJython.html">What&#8217;s Good About Jython?</a> shows this in action:</p>
<pre class="brush: python;">
import javax.swing as swing

def printMessage(event):
    print &quot;Ouch!&quot;

if __name__== &quot;__main__&quot;:
    frame=swing.JFrame(title=&quot;My Frame&quot;, size=(300,300))
    frame.defaultCloseOperation=swing.JFrame.EXIT_ON_CLOSE;
    button=swing.JButton(&quot;Push Me!&quot;, actionPerformed=printMessage)
    frame.contentPane.add(button)
    frame.visible=1
</pre>
<p>The most powerful result of learning different programming languages is being able to apply concepts learned in one language, in another language. This type of &#8216;technology transfer&#8217; drives progress in many science and engineering fields. This cross-fertilisation has always been possible, but is becoming both more feasible and popular thanks to multiparadigm languages such as Python and Ruby, and wider use of the STL and Boost in Standard C++ which encourage and facilitate functional and higher-order programming style. There are even books on the subject of <a href="http://hop.perl.plover.com/">Higher Order Perl</a>.</p>
<p>Although its had many notable ongoing and recent successes (<em>e.g. </em><a href="http://www.gnu.org/software/emacs/emacs.html">Emacs</a>, <a href="http://abridgegame.org/darcs/">Darcs</a>, <a href="http://www.pugscode.org/">Pugs</a>) the functional community is unfortunately widely perceived as academic or impractical. Indeed, Guido van Rossum has recently threatened to remove some functional programming support from Python, bizarrely whilst simultaneously adding higher-order features such as decorators. This is unfortunate because functional programming style and idioms can go a long way to improving software quality in any language &#8211; functions without side-effects are far easier to test, debug and reason about.</p>
<p>For example, the technique of <a href="http://en.wikipedia.org/wiki/Currying">currying </a>- fixing a function argument and returning a new function which takes the remaining parameters can be very useful for development with the Swing UI toolkit supplied with Java.</p>
<p>In this example extracted from one of my own projects, where the user interface features two radio buttons, we look at how some higher-order techniques can be used to aid user-interface programming with Swing. In the code below,</p>
<pre class="brush: python;">
fixed_radio = JRadioButton(text=&quot;Regular columns of fixed width&quot;,
        actionPerformed = self.handleFixedOption)

separated_radio = JRadioButton(text=&quot;Fields separated by spaces,
        commas, or other characters&quot;,
        actionPerformed = self.handleSeparatedOption)
</pre>
<p>two bound-methods are attached to the <code>actionPerformed</code> event of each button.</p>
<pre class="brush: python;">
def handleFixedOption(self, event=None):
    self.controller.setOption(self.controller.FIXED)

def handleSeparatedOption(self, event=None):
    self.controller.setOption(self.controller.SEPARATED)
</pre>
<p>We don&#8217;t actually use the <code>event</code> parameter in this case, but it will be passed to the handler function we provide from <code>actionPerformed</code>, so we must accommodate it. These two methods are identical apart from the value passed to <code>setOption</code> internally, so we could factor out the common code and these two functions with a single function and pass the option value <code>FIXED</code> or <code>SEPARATED</code>  in as an extra argument,</p>
<pre class="brush: python;">
def handleOption(self, option, event=None):
    self.controller.setOption(option)
</pre>
<p>However, we now have a difficulty in that we can no longer bind our new function directly to the actionPerformed handler of the buttons, since <code>actionPerformed</code> expects a function which takes single <code>event</code> argument. We know at coding time which <code>option</code> value should be attached to each button &#8211; but we don&#8217;t know until the code is run, which event will be passed from<code> actionPerformed</code>. We need to bind the <code>option</code> argument before the <code>event</code> argument. The solution to this is currying. </p>
<p>Rather than writing our own currying facility, we can use on off-the-shelf implementation provided by the <a href="http://www.xoltar.org/languages/python.html">Xoltar Toolkit</a> which provides some functional programming support in Python. In particular we use the <code>curry</code> function to curry our <code>handleOption</code> function into some new functions that only take take a single <code>event</code> argument.</p>
<pre class="brush: python;">
from functional import curry

fixed_radio = JRadioButton(text=&quot;Regular columns of fixed width&quot;,
        actionPerformed = curry(self.handleOption,
                self.controller.FIXED))

separated_radio = JRadioButton(text=&quot;Fields separated by spaces,
        commas, or other characters&quot;,
        actionPerformed = curry(self.handleOption,
                self.controller.SEPARATED))
</pre>
<p>The curry function takes the function to be curried as its first argument. In this case we pass the bound-method <code>self.optionHandler</code>. The second argument to <code>curry</code> is the value of the first parameter to the function to be curried, which want tobe fixed to this value in the created function, for example, <code>self.controller.FIXED</code>. The result of the <code>curry</code> function is a new function which accepts one less argument than the original function &#8211; so in this case we get a new function which only accepts the remaining <code>event</code> parameter, and is therefore equivalent to the handleFixedOption function we started with, but which we have now avoided having to write.</p>
<p>The material advantage with such a simple example is minimal, but when such techniques are used throughout a project the savings in space and complexity can be substantial. The combination of functional concepts from borrowed from languages such a Haskell, facilitated by tools provided by the Xoltar Tookit, developed in dynamically-typed Jython and exploiting one of the better designed user interface APIs in the form of Swing can be concise and powerful.</p>
<hr/>Copyright &copy; 2010 <strong><a href="http://www.smallshire.org.uk/sufficientlysmall">Sufficiently Small</a></strong>. This Feed is for personal non-commercial use only. If you are not reading this material in your news aggregator, the site you are looking at is guilty of copyright infringement. Please contact legal@smallshire.org.uk so we can take legal action immediately.<br/><span style="float: right;font-size: 7pt"><a href="http://blog.taragana.com/index.php/archive/wordpress-plugins-provided-by-taraganacom/">Plugin</a> by <a href="http://www.taragana.com/">Taragana</a></span>]]></content:encoded>
			<wfw:commentRss>http://www.smallshire.org.uk/sufficientlysmall/2005/12/03/jython-swing-curry/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>
