IronPython and Python both have ‘Python’ in their names but they are completely different animals. They both Implement the Python Language Specification (albeit different versions of this specification).
IronPython is written in C#, and runs on dotnet runtime. Python 3 (CPython) is written in C, is the reference implementation, and is by far the most popular. There are also other Python implementations e.g. PyPy
Here is what this means for us as Rhino users:
.NET Interop and Python Performance
When dealing with dotnet types e.g. RhinoCommon functionality, IronPython is incredibly fast. Almost as fast as C# since it is running on the same runtime and compiles the python code into the intermediate representation used by dotnet. So an int or a List[int] in IronPython is virtually the same as int and List<int> in C# respectively. When you add two Point3ds together in IronPython, the addition is incredibly fast since it knows how to handle the operator overloads defined on the Point3d type in RhinoCommon.
Python 3 (py3 hereon) on the other hand is written in C, and uses the Unmanaged memory. This means it is completely outside of the dotnet runtime. So an int or List[int] in python 3, do not directly translate to same data types in C#, and a conversion is necessary.
Take this class and method for example:
class Base
{
public int Foo(out Brep brep);
}
Consider this python code that uses the class above:
b = Base()
r, brep = b.Foo()
When IronPython parses this code, it creates an explicit call to Base.Foo. This means when the code is running, IronPython does not need to spend a lot of time looking up where Foo is. When it wants to pass the returned int from this method to the rest of python code, it does not need to do any conversion. It is the same int.
When Python 3 parses this code, it needs to wrap the Base type in a dynamically generated py3 type with the same name. Otherwise the python runtime would not be able to understand what Base is (since it is in dotnet memory space). This wrapper type effectively translates between py3 runtime and dotnet runtime.
Now when b.Foo() is called, py3 needs to look at all the members of the Base class to find all the many overloads of Foo(). Then it needs to look at all the input arguments, look at the type of each and perform a complicated distance computation to find which overload of Foo() is most appropriate for the given arguments. Furthermore, it also needs to Box all the out and ref arguments and generate a tuple on the return containing their captured values. It also needs to convert all these dotnet values into equivalent py3 values so the rest of the script can use them. This takes time; Much longer than what IronPython does because of this double-runtime paradigm.
So if you are adding 1000 points and vectors together in py3, the script is going to be much slower than IronPython.This is obviously something that we will keep improving but there are technical-architectural limitations here. But py3 does:
- support all the scientific c-python packages
- support package management
- have better auto-completion and linting
- offer modern syntax
Both Python 3 and IronPython 2 are available in Rhino and Grasshopper. Choose the one that makes the most sense for the specific task you are trying to achieve.