<?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; computing</title>
	<atom:link href="http://www.smallshire.org.uk/sufficientlysmall/category/computing/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>A Hindley-Milner type inference implementation in Python</title>
		<link>http://www.smallshire.org.uk/sufficientlysmall/2010/04/11/a-hindley-milner-type-inference-implementation-in-python/</link>
		<comments>http://www.smallshire.org.uk/sufficientlysmall/2010/04/11/a-hindley-milner-type-inference-implementation-in-python/#comments</comments>
		<pubDate>Sun, 11 Apr 2010 19:36:07 +0000</pubDate>
		<dc:creator>Robert Smallshire</dc:creator>
				<category><![CDATA[Functional programming]]></category>
		<category><![CDATA[OWL BASIC]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[computing]]></category>
		<category><![CDATA[software]]></category>

		<guid isPermaLink="false">http://www.smallshire.org.uk/sufficientlysmall/?p=474</guid>
		<description><![CDATA[A Python implementation of a Hindley-Milner type inferencing algorithm for a small functional language.]]></description>
			<content:encoded><![CDATA[<p><strong>Before you get too excited this is an implementation of a type inference algorithm that happens to be written in Python; it has nothing to do with the Python language itself!</strong></p>
<p>I&#8217;ve been working on <a href="http://www.smallshire.org.uk/sufficientlysmall/category/computing/software/net/owl-basic/">OWL BASIC</a>, a compiler for <a href="http://en.wikipedia.org/wiki/BBC_BASIC">BBC BASIC</a> for the .NET CLR. The compiler itself is written in IronPython.  One of the challenges of compiling BBC BASIC is to infer the types of functions from the type of their return types. The return value of a BBC BASIC function can be an arbitrary expression, including calls to other functions or recursive calls to itself.  I&#8217;ve implemented a simple type inference scheme which works well in the common cases, but for a fully capable solution my type checker and type inferencer need to be beefed up somewhat.  To that end, I&#8217;ve been investigating standard type systems such as Hindley-Milner and inferencing algorithms such as Damas-Milner, sometimes known as Algorithm W.  These algorithms or derivatives thereof are using in the ML family of languages (Standard ML, Ocaml, F#) and Haskell.</p>
<p>I managed to locate a <a href="http://lucacardelli.name/Papers/BasicTypechecking.pdf#page=19">Modula-2 implementation</a>, a <a href="http://web.archive.org/web/20050911123640/http://www.cs.berkeley.edu/~nikitab/courses/cs263/hm.html">Perl implementation</a> and a <a href="http://dysphoria.net/2009/06/28/hindley-milner-type-inference-in-scala/">Scala implementation</a> of the algorithm, each descended from the previous.  With a view to improving my understanding of the algorithm I set about reimplementing in Python, largely guided by the Scala implementation, making mine the fourth in this sequence.  I also located a <a href="http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.65.7733&#038;rep=rep1&#038;type=pdf">Haskell implementation</a> which seems to have independent ancestry.  I&#8217;ve gone back to the companion paper (<a href="http://lucacardelli.name/Papers/BasicTypechecking.pdf">Cardelli 1987</a>) to the original Modula-2 implementation and carried forward some of the code comments which had been omitted from its descendants to assist others who wish to understand the algorithm.</p>
<p>The program implements abstract syntax tree nodes for a small functional language, the type inferencing algorithm and finally exercises the algorithm by inferring the types of some canned expressions in the context of some predefined types. When executed it produces the following output:</p>
<pre class="brush: text;">
&gt; python hindley_milner.py
(letrec factorial = (fn n =&gt; (((cond (zero n)) 1) ((times n) (factorial (pred n))))) in (factorial 5)) :  int
(fn x =&gt; ((pair (x 3)) (x true))) :  Type mismatch: bool != int
((pair (f 4)) (f true)) :  Undefined symbol f
(let f = (fn x =&gt; x) in ((pair (f 4)) (f true))) :  (int * bool)
(fn f =&gt; (f f)) :  recursive unification
(let g = (fn f =&gt; 5) in (g g)) :  int
(fn g =&gt; (let f = (fn x =&gt; g) in ((pair (f 3)) (f true)))) :  (a -&gt; (a * a))
(fn f =&gt; (fn g =&gt; (fn arg =&gt; (g (f arg))))) :  ((b -&gt; c) -&gt; ((c -&gt; d) -&gt; (b -&gt; d)))
</pre>
<p>The Python code is shown below in its entirety or it can be downloaded as <a href="http://www.smallshire.org.uk/downloads/hindley_milner.py">hindley_milner.py</a>. It will run without modification on Python 2.6 or Python 3. </p>
<pre class="brush: python;">
#!/usr/bin/env python
'''
.. module:: hindley_milner
   :synopsis: An implementation of the Hindley Milner type checking algorithm
              based on the Scala code by Andrew Forrest, the Perl code by
              Nikita Borisov and the paper &quot;Basic Polymorphic Typechecking&quot;
              by Cardelli.
.. moduleauthor:: Robert Smallshire
'''

from __future__ import print_function

#=======================================================#
# Class definitions for the abstract syntax tree nodes
# which comprise the little language for which types
# will be inferred

class Lambda(object):
    &quot;&quot;&quot;Lambda abstraction&quot;&quot;&quot;

    def __init__(self, v, body):
        self.v = v
        self.body = body

    def __str__(self):
        return &quot;(fn {v} =&gt; {body})&quot;.format(v=self.v, body=self.body)

class Ident(object):
    &quot;&quot;&quot;Identfier&quot;&quot;&quot;

    def __init__(self, name):
        self.name = name

    def __str__(self):
        return self.name

class Apply(object):
    &quot;&quot;&quot;Function application&quot;&quot;&quot;

    def __init__(self, fn, arg):
        self.fn = fn
        self.arg = arg

    def __str__(self):
        return &quot;({fn} {arg})&quot;.format(fn=self.fn, arg=self.arg)

class Let(object):
    &quot;&quot;&quot;Let binding&quot;&quot;&quot;

    def __init__(self, v, defn, body):
        self.v = v
        self.defn = defn
        self.body = body

    def __str__(self):
        return &quot;(let {v} = {defn} in {body})&quot;.format(v=self.v, defn=self.defn, body=self.body)

class Letrec(object):
    &quot;&quot;&quot;Letrec binding&quot;&quot;&quot;

    def __init__(self, v, defn, body):
        self.v = v
        self.defn = defn
        self.body = body

    def __str__(self):
        return &quot;(letrec {v} = {defn} in {body})&quot;.format(v=self.v, defn=self.defn, body=self.body)

#=======================================================#
# Exception types

class TypeError(Exception):
    &quot;&quot;&quot;Raised if the type inference algorithm cannot infer types successfully&quot;&quot;&quot;

    def __init__(self, message):
        self.__message = message

    message = property(lambda self: self.__message)

    def __str__(self):
        return str(self.message)

class ParseError(Exception):
    &quot;&quot;&quot;Raised if the type environment supplied for is incomplete&quot;&quot;&quot;
    def __init__(self, message):
        self.__message = message

    message = property(lambda self: self.__message)

    def __str__(self):
        return str(self.message)

#=======================================================#
# Types and type constructors

class TypeVariable(object):
    &quot;&quot;&quot;A type variable standing for an arbitrary type.

    All type variables have a unique id, but names are only assigned lazily,
    when required.
    &quot;&quot;&quot;

    next_variable_id = 0

    def __init__(self):
        self.id = TypeVariable.next_variable_id
        TypeVariable.next_variable_id += 1
        self.instance = None
        self.__name = None

    next_variable_name = 'a'

    def _getName(self):
        &quot;&quot;&quot;Names are allocated to TypeVariables lazily, so that only TypeVariables
        present
        &quot;&quot;&quot;
        if self.__name is None:
            self.__name = TypeVariable.next_variable_name
            TypeVariable.next_variable_name = chr(ord(TypeVariable.next_variable_name) + 1)
        return self.__name

    name = property(_getName)

    def __str__(self):
        if self.instance is not None:
            return str(self.instance)
        else:
            return self.name

    def __repr__(self):
        return &quot;TypeVariable(id = {0})&quot;.format(self.id)

class TypeOperator(object):
    &quot;&quot;&quot;An n-ary type constructor which builds a new type from old&quot;&quot;&quot;

    def __init__(self, name, types):
        self.name = name
        self.types = types

    def __str__(self):
        num_types = len(self.types)
        if num_types == 0:
            return self.name
        elif num_types == 2:
            return &quot;({0} {1} {2})&quot;.format(str(self.types[0]), self.name, str(self.types[1]))
        else:
            return &quot;{0} {1}&quot; % (self.name, ' '.join(self.types))

class Function(TypeOperator):
    &quot;&quot;&quot;A binary type constructor which builds function types&quot;&quot;&quot;

    def __init__(self, from_type, to_type):
        super(Function, self).__init__(&quot;-&gt;&quot;, [from_type, to_type])

# Basic types are constructed with a nullary type constructor
Integer = TypeOperator(&quot;int&quot;, [])  # Basic integer
Bool    = TypeOperator(&quot;bool&quot;, []) # Basic bool

#=======================================================#
# Type inference machinery

def analyse(node, env, non_generic=None):
    &quot;&quot;&quot;Computes the type of the expression given by node.

    The type of the node is computed in the context of the context of the
    supplied type environment env. Data types can be introduced into the
    language simply by having a predefined set of identifiers in the initial
    environment. environment; this way there is no need to change the syntax or, more
    importantly, the type-checking program when extending the language.

    Args:
        node: The root of the abstract syntax tree.
        env: The type environment is a mapping of expression identifier names
            to type assignments.
            to type assignments.
        non_generic: A set of non-generic variables, or None

    Returns:
        The computed type of the expression.

    Raises:
        TypeError: The type of the expression could not be inferred, for example
            if it is not possible to unify two types such as Integer and Bool
        ParseError: The abstract syntax tree rooted at node could not be parsed
    &quot;&quot;&quot;

    if non_generic is None:
        non_generic = set()

    if isinstance(node, Ident):
        return getType(node.name, env, non_generic)
    elif isinstance(node, Apply):
        fun_type = analyse(node.fn, env, non_generic)
        arg_type = analyse(node.arg, env, non_generic)
        result_type = TypeVariable()
        unify(Function(arg_type, result_type), fun_type)
        return result_type
    elif isinstance(node, Lambda):
        arg_type = TypeVariable()
        new_env = env.copy()
        new_env[node.v] = arg_type
        new_non_generic = non_generic.copy()
        new_non_generic.add(arg_type)
        result_type = analyse(node.body, new_env, new_non_generic)
        return Function(arg_type, result_type)
    elif isinstance(node, Let):
        defn_type = analyse(node.defn, env, non_generic)
        new_env = env.copy()
        new_env[node.v] = defn_type
        return analyse(node.body, new_env, non_generic)
    elif isinstance(node, Letrec):
        new_type = TypeVariable()
        new_env = env.copy()
        new_env[node.v] = new_type
        new_non_generic = non_generic.copy()
        new_non_generic.add(new_type)
        defn_type = analyse(node.defn, new_env, new_non_generic)
        unify(new_type, defn_type)
        return analyse(node.body, new_env, non_generic)
    assert 0, &quot;Unhandled syntax node {0}&quot;.format(type(t))

def getType(name, env, non_generic):
    &quot;&quot;&quot;Get the type of identifier name from the type environment env.

    Args:
        name: The identifier name
        env: The type environment mapping from identifier names to types
        non_generic: A set of non-generic TypeVariables

    Raises:
        ParseError: Raised if name is an undefined symbol in the type
            environment.
    &quot;&quot;&quot;
    if name in env:
        return fresh(env[name], non_generic)
    elif isIntegerLiteral(name):
        return Integer
    else:
        raise ParseError(&quot;Undefined symbol {0}&quot;.format(name))

def fresh(t, non_generic):
    &quot;&quot;&quot;Makes a copy of a type expression.

    The type t is copied. The the generic variables are duplicated and the
    non_generic variables are shared.

    Args:
        t: A type to be copied.
        non_generic: A set of non-generic TypeVariables
    &quot;&quot;&quot;
    mappings = {} # A mapping of TypeVariables to TypeVariables

    def freshrec(tp):
        p = prune(tp)
        if isinstance(p, TypeVariable):
            if isGeneric(p, non_generic):
                if p not in mappings:
                    mappings[p] = TypeVariable()
                return mappings[p]
            else:
                return p
        elif isinstance(p, TypeOperator):
            return TypeOperator(p.name, [freshrec(x) for x in p.types])

    return freshrec(t)

def unify(t1, t2):
    &quot;&quot;&quot;Unify the two types t1 and t2.

    Makes the types t1 and t2 the same.

    Args:
        t1: The first type to be made equivalent
        t2: The second type to be be equivalent

    Returns:
        None

    Raises:
        TypeError: Raised if the types cannot be unified.
    &quot;&quot;&quot;

    a = prune(t1)
    b = prune(t2)
    if isinstance(a, TypeVariable):
        if a != b:
            if occursInType(a, b):
                raise TypeError(&quot;recursive unification&quot;)
            a.instance = b
    elif isinstance(a, TypeOperator) and isinstance(b, TypeVariable):
        unify(b, a)
    elif isinstance(a, TypeOperator) and isinstance(b, TypeOperator):
        if (a.name != b.name or len(a.types) != len(b.types)):
            raise TypeError(&quot;Type mismatch: {0} != {1}&quot;.format(str(a), str(b)))
        for p, q in zip(a.types, b.types):
            unify(p, q)
    else:
        assert 0, &quot;Not unified&quot;

def prune(t):
    &quot;&quot;&quot;Returns the currently defining instance of t.

    As a side effect, collapses the list of type instances. The function Prune
    is used whenever a type expression has to be inspected: it will always
    return a type expression which is either an uninstantiated type variable or
    a type operator; i.e. it will skip instantiated variables, and will
    actually prune them from expressions to remove long chains of instantiated
    variables.

    Args:
        t: The type to be pruned

    Returns:
        An uninstantiated TypeVariable or a TypeOperator
    &quot;&quot;&quot;
    if isinstance(t, TypeVariable):
        if t.instance is not None:
            t.instance = prune(t.instance)
            return t.instance
    return t

def isGeneric(v, non_generic):
    &quot;&quot;&quot;Checks whether a given variable occurs in a list of non-generic variables

    Note that a variables in such a list may be instantiated to a type term,
    in which case the variables contained in the type term are considered
    non-generic.

    Note: Must be called with v pre-pruned

    Args:
        v: The TypeVariable to be tested for genericity
        non_generic: A set of non-generic TypeVariables

    Returns:
        True if v is a generic variable, otherwise False
    &quot;&quot;&quot;
    return not occursIn(v, non_generic)

def occursInType(v, type2):
    &quot;&quot;&quot;Checks whether a type variable occurs in a type expression.

    Note: Must be called with v pre-pruned

    Args:
        v:  The TypeVariable to be tested for
        type2: The type in which to search

    Returns:
        True if v occurs in type2, otherwise False
    &quot;&quot;&quot;
    pruned_type2 = prune(type2)
    if pruned_type2 == v:
        return True
    elif isinstance(pruned_type2, TypeOperator):
        return occursIn(v, pruned_type2.types)
    return False

def occursIn(t, types):
    &quot;&quot;&quot;Checks whether a types variable occurs in any other types.

    Args:
        v:  The TypeVariable to be tested for
        types: The sequence of types in which to search

    Returns:
        True if t occurs in any of types, otherwise False
    &quot;&quot;&quot;
    return any(occursInType(t, t2) for t2 in types)

def isIntegerLiteral(name):
    &quot;&quot;&quot;Checks whether name is an integer literal string.

    Args:
        name: The identifier to check

    Returns:
        True if name is an integer literal, otherwise False
    &quot;&quot;&quot;
    result = True
    try:
        int(name)
    except ValueError:
        result = False
    return result

#==================================================================#
# Example code to exercise the above

def tryExp(env, node):
    &quot;&quot;&quot;Try to evaluate a type printing the result or reporting errors.

    Args:
        env: The type environment in which to evaluate the expression.
        node: The root node of the abstract syntax tree of the expression.

    Returns:
        None
    &quot;&quot;&quot;
    print(str(node) + &quot; : &quot;, end=' ')
    try:
        t = analyse(node, env)
        print(str(t))
    except (ParseError, TypeError) as e:
        print(e)

def main():
    &quot;&quot;&quot;The main example program.

    Sets up some predefined types using the type constructors TypeVariable,
    TypeOperator and Function.  Creates a list of example expressions to be
    evaluated. Evaluates the expressions, printing the type or errors arising
    from each.

    Returns:
        None
    &quot;&quot;&quot;

    var1 = TypeVariable()
    var2 = TypeVariable()
    pair_type = TypeOperator(&quot;*&quot;, (var1, var2))

    var3 = TypeVariable()

    my_env = { &quot;pair&quot; : Function(var1, Function(var2, pair_type)),
               &quot;true&quot; : Bool,
               &quot;cond&quot; : Function(Bool, Function(var3, Function(var3, var3))),
               &quot;zero&quot; : Function(Integer, Bool),
               &quot;pred&quot; : Function(Integer, Integer),
               &quot;times&quot;: Function(Integer, Function(Integer, Integer)) }

    pair = Apply(Apply(Ident(&quot;pair&quot;), Apply(Ident(&quot;f&quot;), Ident(&quot;4&quot;))), Apply(Ident(&quot;f&quot;), Ident(&quot;true&quot;)))

    examples = [
            # factorial
            Letrec(&quot;factorial&quot;, # letrec factorial =
                Lambda(&quot;n&quot;,    # fn n =&gt;
                    Apply(
                        Apply(   # cond (zero n) 1
                            Apply(Ident(&quot;cond&quot;),     # cond (zero n)
                                Apply(Ident(&quot;zero&quot;), Ident(&quot;n&quot;))),
                            Ident(&quot;1&quot;)),
                        Apply(    # times n
                            Apply(Ident(&quot;times&quot;), Ident(&quot;n&quot;)),
                            Apply(Ident(&quot;factorial&quot;),
                                Apply(Ident(&quot;pred&quot;), Ident(&quot;n&quot;)))
                        )
                    )
                ),      # in
                Apply(Ident(&quot;factorial&quot;), Ident(&quot;5&quot;))
            ),

            # Should fail:
            # fn x =&gt; (pair(x(3) (x(true)))
            Lambda(&quot;x&quot;,
                Apply(
                    Apply(Ident(&quot;pair&quot;),
                        Apply(Ident(&quot;x&quot;), Ident(&quot;3&quot;))),
                    Apply(Ident(&quot;x&quot;), Ident(&quot;true&quot;)))),

            # pair(f(3), f(true))
            Apply(
                Apply(Ident(&quot;pair&quot;), Apply(Ident(&quot;f&quot;), Ident(&quot;4&quot;))),
                Apply(Ident(&quot;f&quot;), Ident(&quot;true&quot;))),

            # let f = (fn x =&gt; x) in ((pair (f 4)) (f true))
            Let(&quot;f&quot;, Lambda(&quot;x&quot;, Ident(&quot;x&quot;)), pair),

            # fn f =&gt; f f (fail)
            Lambda(&quot;f&quot;, Apply(Ident(&quot;f&quot;), Ident(&quot;f&quot;))),

            # let g = fn f =&gt; 5 in g g
            Let(&quot;g&quot;,
                Lambda(&quot;f&quot;, Ident(&quot;5&quot;)),
                Apply(Ident(&quot;g&quot;), Ident(&quot;g&quot;))),

            # example that demonstrates generic and non-generic variables:
            # fn g =&gt; let f = fn x =&gt; g in pair (f 3, f true)
            Lambda(&quot;g&quot;,
                   Let(&quot;f&quot;,
                       Lambda(&quot;x&quot;, Ident(&quot;g&quot;)),
                       Apply(
                            Apply(Ident(&quot;pair&quot;),
                                  Apply(Ident(&quot;f&quot;), Ident(&quot;3&quot;))
                            ),
                            Apply(Ident(&quot;f&quot;), Ident(&quot;true&quot;))))),

            # Function composition
            # fn f (fn g (fn arg (f g arg)))
            Lambda(&quot;f&quot;, Lambda(&quot;g&quot;, Lambda(&quot;arg&quot;, Apply(Ident(&quot;g&quot;), Apply(Ident(&quot;f&quot;), Ident(&quot;arg&quot;))))))
    ]

    for example in examples:
        tryExp(my_env, example)

if __name__ == '__main__':
    main()
</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/2010/04/11/a-hindley-milner-type-inference-implementation-in-python/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Control Flow Graph Linearisation in OWL BASIC</title>
		<link>http://www.smallshire.org.uk/sufficientlysmall/2010/02/14/control-flow-graph-linearisation-in-owl-basic/</link>
		<comments>http://www.smallshire.org.uk/sufficientlysmall/2010/02/14/control-flow-graph-linearisation-in-owl-basic/#comments</comments>
		<pubDate>Sun, 14 Feb 2010 19:27:22 +0000</pubDate>
		<dc:creator>Robert Smallshire</dc:creator>
				<category><![CDATA[.NET]]></category>
		<category><![CDATA[OWL BASIC]]></category>
		<category><![CDATA[computing]]></category>
		<category><![CDATA[BBCBASIC]]></category>
		<category><![CDATA[CLR]]></category>

		<guid isPermaLink="false">http://www.smallshire.org.uk/sufficientlysmall/?p=450</guid>
		<description><![CDATA[To compile the code comprising an OWL BASIC procedure, function or main program into CIL, we must linearise the Control Flow Graph (CFG) representing the program statements.  The CFG undergoes many transformations during compilation, for example to eliminate unreachable code or convert GOSUB routines into named procedures.  Generation of CIL using Reflection.Emit requires [...]]]></description>
			<content:encoded><![CDATA[<p>To compile the code comprising an OWL BASIC procedure, function or main program into CIL, we must linearise the <a href="http://en.wikipedia.org/wiki/Control_flow_graph">Control Flow Graph</a> (CFG) representing the program statements.  The CFG undergoes many transformations during compilation, for example to eliminate unreachable code or convert <code>GOSUB</code> routines into named procedures.  Generation of CIL using Reflection.Emit requires that we can define branch targets in advance of generating branch instructions or marking the target instruction and of course we want to do this in a manner which minimises the number of branches required to represent the code.  The structure of the graph may be quite complex, especially for traditional BASIC spaghetti code which uses GOTO excessively rather than the more structured alternatives such as procedures and functions or the control structures introduced in BBC BASIC V .</p>
<p>Consider the following procedure from Sphinx Adventure.  It contains three loops, one on line 271 formed with a <code>GOTO</code> back to the start of the line, and two <code>REPEAT .. UNTIL</code> loops.</p>
<pre class="brush: bbc;">
266 DEF PROCL(L)
267 LOCAL I,J:CO=0:CN=0
268 IF L=1 THEN278
269 PRINT': RESTORE L: IF O?31&lt;&gt;0 THEN O?31=0:DW=1
270 READ R$,R$:R$=&quot;You are &quot;+R$
271 IF LEN(R$)+ POS&gt;CO-CN+39 THEN R$= FNS(R$,39+CO-CN):CO=CO+39: GOTO271
272 PRINT R$: IFL=136 OR L=15 THEN O?56=L
273 IF L=16 AND FL=1 THEN PRINT&quot;The walls are very hot!&quot; ELSE IF L=16 THEN PRINT &quot;The walls are steaming!&quot;
274 IF L&lt;&gt;3 AND L&lt;&gt;142 AND L&lt;&gt;143 THEN PROCEX(L): IF ABS(L-19)=1 AND CH=1 THEN PRINT ELSE IF ABS(L-42)=1 AND VO=1 THEN PRINT
275 IF CH=1 AND ABS(L-19)=1 THEN PROC R(22): PRINT &quot;chasm.&quot;:O?53=L
276 IF VO=1 AND ABS(L-42)=1 THEN PROCR(22): PRINT&quot;glacier.&quot;:O?53=L
277 IF L=26 OR L=27 THEN O?53=L
278 J=0:I=0:CO=0
279 REPEAT:J=J+1: IF O?J=L  THEN CO=CO+1
280 UNTIL J=52: IF CO=0 AND L=1 THEN PROCR(L): GOTO 284 ELSE IF CO=0 AND L&lt;&gt;1 THEN 284 ELSE PRINT:MAX=CO
281 IF L=1 THEN PROCR(3) ELSE PROCR(4)
282 CN=0:CO=MAX: REPEAT I=I+1: IF O?I=L  THEN PROCOT(I,CO):CO=CO-1
283 UNTIL I=52
284 IF D&lt;&gt;0 THEN O?31=L
285 IF CF=1 AND L=94 THEN PRINT'&quot;The casket is open.&quot;
286 IF L=24 AND SA=1 THEN PRINT'&quot;The safe door is open.&quot;
287 PRINT: ENDPROC
</pre>
<p>The CFG for this code is shown below.  Each program statement is shown as a purple box, with control flow to the following statement(s). Conditionals are shown in diamond boxes.  The numbers in each purple box are source line numbers, where known.</p>
<p>Careful comparison of the source above and the diagram below will reveal some of the transformations that have been applied to the program; for example, <code>READ R$,R$</code> on line 270 has been transformed into two consecutive assignment statements which actually take the form <code>R$ = READ()</code> where <code>READ()</code> is a function not available in the source language.</p>
<p>The <i>statement</i> level CFG has been analysed to identify <a href="http://en.wikipedia.org/wiki/Basic_block">basic-blocks</a>, shown as yellow group nodes, thereby defining a higher level <i>basic-block</i> level CFG.  Each basic block has only one entry point statement; none of the statement within the basic block are destinations of other jump instructions. Furthermore, each block has only one exit point.</p>
<p><i>More text follows this long diagram&#8230;</i></p>
<p><img src="/images/Sphinx_basic_blocks.png" alt="Control Flow Graph for PROC L in Sphinx Adventure" /></p>
<p>Generating the CIL code for a single basic block is straightforward enough &#8211; we can simply iterate through the statements comprising the basic block in order and generate the code for each in turn.  However, there are many possible orders in which the code for the basic block themselves could be representing in the CIL, since we can branch from the end of any block to the next block, although of course we must start at the <i>entry block</i> for the procedure. Although any order starting with the entry block can be made to work, where possible we would like program control to flow smoothly from the end of a block to one of its successors without requiring a branch.</p>
<p>At first sight, some sort of topological ordering would seem to be appropriate, but a topological ordering is only well defined for a <a href="http://en.wikipedia.org/wiki/Directed_acyclic_graph">directed acyclic graph</a> (DAG), and a DAG this program is not.  The key to this conundrum is to reduce the directed graph to a DAG by identifying <a href="http://en.wikipedia.org/wiki/Strongly_connected_components">strongly connected components</a>.  By contracting each SCC to a single node we obtain what is called the <i>condensation</i> of the CFG which <i>will</i> be a DAG. To the resulting DAG we can apply a topological ordering. The ordering of vertices with each SCC is chosen by starting at the vertex with the greatest <a href="http://en.wikipedia.org/wiki/Indegree#Indegree_and_outdegree">in-degree</a>.</p>
<p>In order to identify and contract the SCCs we use an implementation of <a href="http://en.wikipedia.org/wiki/Tarjan's_strongly_connected_components_algorithm">Tarjan&#8217;s algorithm</a> during depth first traversal of the CFG.  The reverse post ordering of the primary depth first traversal is used to generate the topological ordering of the condensed CFG.</p>
<p>The resulting ordering of basic blocks is shown in the diagram by the numeric labels to the top-left of each. This will be the order in which the CIL code for them is generated, and it can be seen that in about half of the cases, fall through from one block to the next (consecutive block numbers) without explicit branching can be exploited.  Future optimisations will focus on further simplifying the generated code by removing vertices, such as block 31, which contain only jumps.</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/2010/02/14/control-flow-graph-linearisation-in-owl-basic/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>OWL BASIC produces its first executable</title>
		<link>http://www.smallshire.org.uk/sufficientlysmall/2009/08/04/owl-basic-produces-its-first-executable/</link>
		<comments>http://www.smallshire.org.uk/sufficientlysmall/2009/08/04/owl-basic-produces-its-first-executable/#comments</comments>
		<pubDate>Tue, 04 Aug 2009 21:45:26 +0000</pubDate>
		<dc:creator>Robert Smallshire</dc:creator>
				<category><![CDATA[.NET]]></category>
		<category><![CDATA[IronPython]]></category>
		<category><![CDATA[OWL BASIC]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[computing]]></category>

		<guid isPermaLink="false">http://www.smallshire.org.uk/sufficientlysmall/?p=394</guid>
		<description><![CDATA[After a long haul, and diversions into other more important projects &#8212; including starting a family &#8212; OWL BASIC today produced its first executable.  Its not much. In fact its hardly anything. Just 2048 bytes of Windows PE executable containing the global variable declarations from Acornsoft&#8217;s 1982 Sphinx Adventure. Each file of BASIC source [...]]]></description>
			<content:encoded><![CDATA[<p>After a long haul, and diversions into other more important projects &#8212; including starting a family &#8212; <a href="http://www.smallshire.org.uk/sufficientlysmall/2007/06/10/writing-a-bbc-basic-compiler-for-the-clr/">OWL BASIC</a> today produced its first executable.  Its not much. In fact its hardly anything. Just 2048 bytes of Windows PE executable containing the global variable declarations from Acornsoft&#8217;s 1982 Sphinx Adventure. Each file of BASIC source code will be converted to a single .NET static class, with the global variables as private static fields.</p>
<div id="attachment_395" class="wp-caption aligncenter" style="width: 538px"><img src="http://www.smallshire.org.uk/sufficientlysmall/wp-content/uploads/sphinx_reflector.png" alt="The first executable produced from OWL BASIC." title="sphinx_reflector" width="528" height="571" class="size-full wp-image-395" /><p class="wp-caption-text">The first executable produced from OWL BASIC.</p></div>
<p>Above you can see the executable loaded up into <a href="http://www.red-gate.com/products/reflector/">.NET Reflector</a>, which can be used to introspect the executable, and in this case attempt to disassemble it into C#.  Now we see what makes .NET such a great platform for compiler construction; below is the <a href="http://www.codeplex.com/IronPython">IronPython</a> source code for the embryonic assembly generation function. It clocks in at fewer than ten lines of code to create an assembly, create a module, create a class, add one private static field to it for each global variable, and save the result as an <code>.exe</code>.</p>
<pre class="brush: python;">
def generateAssembly(name, global_symbols):
    domain = Thread.GetDomain()
    assembly_name = AssemblyName(name)
    assembly_builder = domain.DefineDynamicAssembly(assembly_name, AssemblyBuilderAccess.RunAndSave)
    module_builder = assembly_builder.DefineDynamicModule(name + &quot;.exe&quot;)
    type_builder = module_builder.DefineType(name, TypeAttributes.Class | TypeAttributes.Public, object().GetType())

    # Add global variables to the class
    for symbol in global_symbols.symbols.values():
        field_builder = type_builder.DefineField(symbol.name, ctsType(symbol),
                                                 FieldAttributes.Private | FieldAttributes.Static)

    result = type_builder.CreateType()
    assembly_builder.Save(name + &quot;.exe&quot;)
</pre>
<p>where <code>global_symbols</code> is the global symbol table constructed during traversal of the Abstract Syntax Tree and the Control Flow Graph and the <code>ctsType</code> function maps OWL BASIC types to their equivalent <a href="http://en.wikipedia.org/wiki/Common_Type_System">Common Type System</a> types for .NET.  Everything else is provided by <a href="http://msdn.microsoft.com/en-us/library/system.reflection.emit.aspx">Reflection.Emit</a> and other parts of .NET.</p>
<p>Its interesting that no validation was applied to the variable names supplied to <code>Reflection.Emit</code>.  As you can see, the variable names still include the <a href="http://en.wikipedia.org/wiki/Sigil_(computer_programming)">sigil</a> suffixes for variable typing (<i>e.g.</i> $ for string) and Reflector happily dissassembles these into invalid C# identifiers.  For the final version these names will need to be mangled (<a href="http://en.wikipedia.org/wiki/Hungarian_notation">Hungarian notation</a>?), or merely de-sigiled if no conflicts result, for compatibility with other .NET languages and tools.</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/08/04/owl-basic-produces-its-first-executable/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>In C++ throw is an expression</title>
		<link>http://www.smallshire.org.uk/sufficientlysmall/2009/07/31/in-c-throw-is-an-expression/</link>
		<comments>http://www.smallshire.org.uk/sufficientlysmall/2009/07/31/in-c-throw-is-an-expression/#comments</comments>
		<pubDate>Fri, 31 Jul 2009 11:11:45 +0000</pubDate>
		<dc:creator>Robert Smallshire</dc:creator>
				<category><![CDATA[C++]]></category>
		<category><![CDATA[computing]]></category>
		<category><![CDATA[software]]></category>

		<guid isPermaLink="false">http://www.smallshire.org.uk/sufficientlysmall/?p=384</guid>
		<description><![CDATA[After 15 years programming in C++, I was surprised to discover today that throw in C++ is an expression rather than a statement. As a result, throw may be used as part of larger expressions.

int x = 5;
int y = x &#62; 4 ? x : throw std::out_of_range;

The only use I&#8217;ve found for this &#8212; [...]]]></description>
			<content:encoded><![CDATA[<p>After 15 years programming in C++, I was surprised to discover today that <code>throw</code> in C++ is an expression rather than a statement. As a result, <code>throw</code> may be used as part of larger expressions.</p>
<pre class="brush: cpp;">
int x = 5;
int y = x &gt; 4 ? x : throw std::out_of_range;
</pre>
<p>The only use I&#8217;ve found for this &#8212; and in fact the speculative attempt by which I discovered it &#8212; is range checking within constructor initializer lists.</p>
<pre class="brush: cpp;">
RangeChecked::RangeChecked(int x) :
    int_member(x &gt; 0 ? x : throw std::out_of_range)
{
}
</pre>
<p>It&#8217;s academic what the <i>type</i> of a throw expression is, since it will never be returned, but for type-checking purposes the compiler seems to be happy to use it in place of any type.</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/07/31/in-c-throw-is-an-expression/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Installing Eclipse on Windows Vista</title>
		<link>http://www.smallshire.org.uk/sufficientlysmall/2009/07/27/installing-eclipse-on-windows-vista/</link>
		<comments>http://www.smallshire.org.uk/sufficientlysmall/2009/07/27/installing-eclipse-on-windows-vista/#comments</comments>
		<pubDate>Mon, 27 Jul 2009 19:20:30 +0000</pubDate>
		<dc:creator>Robert Smallshire</dc:creator>
				<category><![CDATA[computing]]></category>
		<category><![CDATA[software]]></category>
		<category><![CDATA[eclipse]]></category>
		<category><![CDATA[vista]]></category>
		<category><![CDATA[windows]]></category>

		<guid isPermaLink="false">http://www.smallshire.org.uk/sufficientlysmall/?p=374</guid>
		<description><![CDATA[Eclipse doesn&#8217;t come with an installer for Windows.  As a result of this, installing Eclipse into Program Files is very awkward without getting into a tussle with User Account Control, and virtualisation of the Program Files directory.
After far too much effort I have finally found a sequence that Works For Me™ on Windows Vista [...]]]></description>
			<content:encoded><![CDATA[<p>Eclipse doesn&#8217;t come with an installer for Windows.  As a result of this, installing Eclipse into Program Files is very awkward without getting into a tussle with User Account Control, and virtualisation of the Program Files directory.</p>
<p>After far too much effort I have finally found a sequence that Works For Me™ on Windows Vista Ultimate x64.</p>
<h1>The procedure</h1>
<ol>
<li>Download <code>eclipse-SDK-3.5-win32.zip</code></li>
<li>Extract the zip file into a <strong>temporary</strong> directory.  I used <code>C:\Users\&lt;usename&gt;\Documents\tmp\eclipse</code></li>
<li>Within the temporary directory (important!) edit <code>eclipse.ini</code>, adding the line
<pre class="brush: text;">
-configuration @user.home\.eclipse_35_config
</pre>
<p>My <code>eclipse.ini</code> then looked like this:</p>
<pre class="brush: text;">
-startup
plugins/org.eclipse.equinox.launcher_1.0.200.v20090520.jar
--launcher.library
plugins/org.eclipse.equinox.launcher.win32.win32.x86_1.0.200.v20090519
-showsplash
org.eclipse.platform
-configuration @user.home\.eclipse_35_config
--launcher.XXMaxPermSize
256m
-vmargs
-Xms40m
-Xmx256m
</pre>
</li>
<li>Within the temporary directory create a UTF-8 text file called <code>eclipse.exe.manifest</code> file side-by-side with the <code>eclipse.exe</code> file with the following content:
<pre class="brush: xml;">
&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;yes&quot;?&gt;
&lt;assembly xmlns=&quot;urn:schemas-microsoft-com:asm.v1&quot; manifestVersion=&quot;1.0&quot;&gt;
  &lt;assemblyIdentity version=&quot;1.0.0.0&quot;
     processorArchitecture=&quot;X86&quot;
     name=&quot;eclipse&quot;
     type=&quot;win32&quot;/&gt;
  &lt;description&gt;Eclipse&lt;/description&gt;
  &lt;!-- Identify the application security requirements. --&gt;
  &lt;trustInfo xmlns=&quot;urn:schemas-microsoft-com:asm.v2&quot;&gt;
    &lt;security&gt;
      &lt;requestedPrivileges&gt;
        &lt;requestedExecutionLevel level=&quot;asInvoker&quot; uiAccess=&quot;false&quot;/&gt;
        &lt;/requestedPrivileges&gt;
       &lt;/security&gt;
  &lt;/trustInfo&gt;
&lt;/assembly&gt;
</pre>
</li>
<li>Using drag and drop copy in Explorer, copy the entire <code>eclipse</code> directory from your temporary location to <code>C:\Program Files (x86)\eclipse</code>.  We copy, rather than move simply so you can easily return to this step.
</li>
<li>Right-click on <code>eclipse.exe</code> in Explorer and choose Properties</li>
<li>Click on the Security tab</li>
<li>Click Advanced in the lower right</li>
<li>In the Advanced Security Settings window that pops up, click on the<br />
Owner tab</li>
<li>Click Edit</li>
<li>Click Continue if you get a UAC dialog</li>
<li>Click Other users or groups</li>
<li>Click Advanced in the lower left corner</li>
<li>Click Find Now</li>
<li>Scroll through the results and click on your current user account</li>
<li>Click OK to all of the remaining windows</li>
<li>Right-click the file and select Properties (again)</li>
<li>Click on the Security tab</li>
<li>Click Edit&#8230;</li>
<li>Click on the Users group</li>
<li>Adjust the permissions for your user using the check boxes at the bottom of the dialog. e.g. enable Full Control</li>
<li>Click OK to all of the remaining windows</li>
<li>Right-click the file and select Properties (again!)</li>
<li>Click Unblock and close the dialog</li>
<li>Double click eclipse.exe to run it. You <strong>should not</strong> get an Open File &#8211; Security Warning dialog.</li>
<li>Remove the temporary <code>eclipse</code> directory you created when you unzipped the archive.
</li>
</ol>
<h1>Credit where it is due</h1>
<p>The above post is largely pulled together from various sources I located mixed with a good degree of trial and error.  I found the following particularly useful:</p>
<ul>
<li><a href="http://www.vistaheads.com/forums/microsoft-public-windows-vista-general/30843-open-file-security-warning.html">Open File &#8211; Security Warning on VistaHeads</a></li>
<li><a href="http://www.eclipsezone.com/eclipse/forums/t97323.html">How do you run Eclipse 3.3 on Vista?</a></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/07/27/installing-eclipse-on-windows-vista/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>An unofficial Europython 2009 retrospective</title>
		<link>http://www.smallshire.org.uk/sufficientlysmall/2009/07/04/an-unofficial-europython-2009-retrospective/</link>
		<comments>http://www.smallshire.org.uk/sufficientlysmall/2009/07/04/an-unofficial-europython-2009-retrospective/#comments</comments>
		<pubDate>Sat, 04 Jul 2009 20:10:16 +0000</pubDate>
		<dc:creator>Robert Smallshire</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[computing]]></category>
		<category><![CDATA[software]]></category>

		<guid isPermaLink="false">http://www.smallshire.org.uk/sufficientlysmall/?p=371</guid>
		<description><![CDATA[Europython 2009 was my first Python conference, this being the first year that I&#8217;ve been able to use Python professionally for application development. We&#8217;d made a decision that if we were to be using Python commercially, we should be active within the Python community, so two of our team travelled from Norway to Birmingham, UK. [...]]]></description>
			<content:encoded><![CDATA[<p>Europython 2009 was my first Python conference, this being the first year that I&#8217;ve been able to use Python professionally for application development. We&#8217;d made a decision that if we were to be using Python commercially, we should be active within the Python community, so two of our team travelled from Norway to Birmingham, UK.  Prior to Europython 2009 my use of Python had been confined hobby projects, occasional scripts and substantial commercial use with a several industrial-scale SCons builds.</p>
<p>I&#8217;ll start out by stating that I came away from the conference a little dissappointed, although I must emphasise in no way cheated, by the experience.  I&#8217;ve attended a great many professionally and community organized conferences over the years, and I felt in many regards Europython was somewhat further down the quality scale than it should be, given the rising importance of Python.  I&#8217;m concious that this posting may come across as a litany of complaint, but I feel that some changes could significantly improve the experience for everybody involved. Organisers of such events are forever soliciting feedback, so here goes!</p>
<p>Europython 2009 was a multi-tracked conference with up to six parallel sessions, so its not possible for me to report on even a representative sample of the sessions. Nonetheless, the overview of the not-so-random sample presented below may give you a flavour of the experience.  I&#8217;m not involved in web development currently so I tended to shy away from the plethora of sessions covering web frameworks.  What <em>does</em> interest me are the alternative Python implementatons, IronPython and Jython and uses of Python for scientific computation.  Unfortunately, I couldn&#8217;t make it to the SCons talk, another interest of mine.</p>
<h1>Size, venue and organization</h1>
<p>Europython had around 500 delegates in attendance, which is I believe a record.  The chosen venue was something of a disappointment. Although the main lecture theatre (The Adrian Bolt Hall) was effective, the other rooms in use were without exception compromised in significant ways.  One was quite small with insufficient capacity and little or no air conditioning &#8211; although it should be noted that Birmingham was experiencing something of a heatwave at the time. Another featured a concrete pillar between the speaker and his audience &#8211; although much better air conditioning in here &#8211; and a third had the audience seated on a raked seating arrangement that creaked and groaned with the slightest movement. I wonder how the musicians who usually inhabit the placed manage?</p>
<p>The lunch arrangements were wholly inadequate. Lunch involved collecting a polystyrene plate, bowl and a drink from a refectory upstairs, loading these with food canteen style and then precariously balancing these items (no trays here!) and trooping down a fire escape stairwell back into the corridors between the lecture halls where there was insufficient seating. At one point our Vice President of Product Development, had expressed an interest in attending Europython, having shown a genuine curiosity in the langauge &#8211; we&#8217;ve been working hard on internal advocacy. At the point at which I found myself having to stand up to eat my lunch standing up in a corridor, having requisitioned a corner of the coffee serving table with my colleague, I was glad he wasn&#8217;t here to witness this somewhat unprofessional scenario (Yes, I know its a community event, and not &#8216;professionally&#8217; organised, but a touch of professionalism can go a long way in the Python advocacy stakes with the right people).  I appreciate there were a mitigating circumstances (a broken lift) but the nature of the facilities alone will make me think twice about who I encourage to attend this event in future.</p>
<h1>Preparation and planning</h1>
<p>Shortly before Europython I&#8217;d returned from a conference called <a href="http://www.eage.org/events/index.php?eventid=103">EAGE 2009</a> which had 7000 student and professional delegates in a major European convention centre (RAI in Amsterdam). I&#8217;ll confess that this may have done much to anchor my expection levels vis-à-vis conferences immediately prior to Europython.  Venue differences and registration cost aside (Europython was 5× cheaper than EAGE) one clear difference was the poor level of preparedness of speakers at Europython compared to EAGE, even though at both conferences speakers were drawn from professional, student and amateur circles.  Time and again at Europython speakers hurriedly arrived in the room with little or no time for setup of equipment such as projectors.  At this point I must give a note &#8212; no, make that a plea &#8212; to conference organizers: Please place a printed notice in 144 point text stating the preferred projector resolution on the table or lectern at the front so we can be spared a linear search through the possible resolutions at the beginning of each talk.</p>
<p>Now, I don&#8217;t know about you, but on occasions that I&#8217;ve been presenting and a private meeting or a conference, especially if it involves a live demo requiring network connectivity, I&#8217;m in the room setting up, connecting, authenticating, testing, and re-testing for up to 30 minutes before hand ensuring that everything will run smoothly.  Although this doesn&#8217;t ensure a professional delivery, it can usually eliminate the tiresome, and stressful for the speaker, configuration during the talk. Its also a common courtesy to your audience to be ready before they are and not keep them waiting. Doing this means you means you&#8217;ll get longer to talk, and time for questions.  I&#8217;m sure most people would agree that time for questions beats staring Display Settings for the umpteenth time.</p>
<p>Some talks dependend on a &#8216;live&#8217; Internet connection &#8211; always a bad idea but in this case it was fatal.  Wireless connectivity was <a href="http://www.finalcog.com/europython-2009-wifi-cartoon">intermittent throughout the conference</a>.   </p>
<h1>Talk quality</h1>
<p>I found the quality of the material presented at Europython to be highly variable; it covered the gamut from borderline incompetent to very good indeed.  Many of the talks simply did not have sufficient material behind them to justify the length of the time slot they were given.  I wonder if fewer parallel tracks with shorter talks would give a better result; audiences would be larger, the pace quicker and delegates would be a able to see a larger proportion of talks, which would help in technology transfer between the different subgroups within the wider Python community.</p>
<h1>Keynotes</h1>
<p>The keynotes were engaging as one expects. Cory Doctorow gave the predictable but entertaining DRM is evil and copyright unenforceable polemic, in the moral vacuum we have come to expect. Bruce Eckel delivered an entertaining historical overview of language features across C++, Java, C# and Python. Susan Blackmore and Simon Greenish made an impassioned plea for funds for <a href="http://www.savingbletchleypark.org/">Saving Bletchley Park</a>, although the talk lacked any technical content about the operations or machines there, which given the technical audience was a little disappointing. Sir Tony Hoare rounded off the keynotes with a comparative study of the disciplines of software engineering and computing science, finishing with the not unreasonable prediction that one day &#8220;software will be the most reliable component of every product which contains it&#8221;, although he didn&#8217;t venture to suggest a time-scale on which this might come to pass.</p>
<h1>Conclusion</h1>
<p>I&#8217;m glad I attended. The conference was good value and on the whole informative.  The organisers should seriously consider a better venue for next year and raising the standard of talks would make a return visit a more compelling prospect. The easiest way to do this may be to simply reduce the number of talks, or shift more of the content into a more appropriate format such as lightning talks.</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/07/04/an-unofficial-europython-2009-retrospective/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<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>
	</channel>
</rss>
