Monday, November 25, 2013

The Richards benchmark is listed on speed.pypy.org as being very fast compared to CPython. PythonJS with full optimizations beats PyPy-2.2 in the Richards benchmark. PythonJS fully optimized scores 13.9ms per-iteration, and finishes the test in 0.131 seconds. PyPy-2.2 scores 38.3ms per-iteration, and finishes the test in 0.38 seconds. (lower times are better).

Brython and Empythoned

Brython is once again very slow, taking 36,613ms per-iteration, and completing the test in 366 seconds. Empythoned and PythonJS (default mode) have about the same performance: Empythoned (4,590ms) and PythonJS (3,390ms). Python2.7 scores 197ms per-iteration.

The following Pystone benchmarks were performed with the default 50,000 iterations, the results are the average of five runs. PythonJS is tested in four different modes with greater levels of optimizations. Test machine is: dual core 2.4GHZ, 4GB RAM, LinuxMint with Google-Chrome v31.

PythonJS vs PyPy

Python2.7 reaches an average of 112,257 Pystones per-second.
Pythonium (another Python to JavaScript translator) in its fastest veloce mode reaches an average of pystones 291,480 per-second.
PythonJS in javascript mode, reaches an average of 850,120 Pystones per-second.
PyPy-1.9 is the winner with an average score of 1,813,461 Pystones per-second.

Brython PythonJS Integration

I am also working on integrating Brython's tokenizer and parser into PythonJS so that PythonJS can be fully self hosted. The file in Brython's source code is py2js.js. It transforms Python code into its own abstract syntax tree that is very similar to the standard Python AST. I am making code to walk this tree and convert it into a standard AST that is required by the translator in PythonJS. See these files, and contact me if you would like to help.

Saturday, November 16, 2013

PythonJS 0.8.5, includes a new module nodejs.tornado that provides an API compatibility layer with the Tornado web framework. This allows you to very quickly port your server side code written in Python and Tornado to PythonJS and NodeJS.

Monday, November 11, 2013

NodeJS simplifies the process of writing fast asynchronous server software, using Google's V8 engine and C optimized libev eventloop. It is designed by Ryan Dahl from the ground up for async programming using callbacks. It is under going rapid development and is taking the lead in terms of simplifying the process for writting high performance, JIT accelerated servers. By using JavaScript on both the server and client side, you can more easily shift code from one platform to the other as needed. By comparison, Python is advancing slowly, adoption of Python3 has been very slow, and the CPython interpreter remains slow without JIT technology.

Whatever the future may hold for the Python interpreter, we can continue to use the language by translating scripts to run in other interpreters like NodeJS.
PythonJS 0.8.4 can now translate code that is compatible with NodeJS, and includes some basic wrappers that help emulate some of Python's standard libraries like: os, sys, and io. Download PythonJS 0.8.4 here.

Sunday, November 10, 2013

If you have not heard of Brython, it is one of the most popular in-browser Python implementations. From the top of Brython's documentation: "Brython's goal is to replace Javascript with Python, as the scripting language for web browsers." Brython is a long way from reaching this goal, because as we will see below, it has serious performance issues. Brython is slow because of its conformant implementation of the Python standard, which is very dynamic and is generally hard to map to JavaScript.

The following tests compare the performance of Brython 1.1, Python 2.7, PyPy 1.9 and PythonJS 0.8.3. Note that PyPy tests were done with a warmed up JIT. These tests were performed with N=100,000 on a dual core 2.4GHZ machine with 4GB of ram. (test scores below are given in seconds)

Test1 - for loop:

a = 0
for x in range( N ):
a += 1

Brython: 6.8249

Python: 0.0064

PyPy: 0.00267

PythonJS: 0.0009

In a simple for loop over a range, with a single variable incremented: Brython is 1,000 times slower than Python, and 7,500 times slower the PythonJS. Lets examine the output of Brython to see why it is so slow.

As we can see above, Brython generates alot of code for such a simple for loop. Instead of using JavaScript's native number type, Brython has its own Number object. It has a try/catch to catch the StopIteration exception, and also it testing each loop if the number has a method __iadd__, and if not it falls back to using __add__.
Lets take a look at how PythonJS translates the same code, and why it is 7,500 times faster in this case.

PythonJS Output

a = 0;
var x;
x = 0;
while(x < N) {
a += 1
x += 1
}

The PythonJS compiler knows that a for loop over a range is actually just a simple loop that can be translated to fast JavaScript.

Test2 - while loop:

a = 0
i = 0
while i < N:
a += 1
i += 1

Brython: 13.5709

Python: 0.01008

PyPy: 0.00144

PythonJS: 0.001

Things get worse for Brython when the loop gets more complicated, this tests increments two values. In this test Brython is 1,300 times slower than Python.

Test3 - calling simple function:

a = 0
for x in range(N):
a += no_args()

Brython: 7.717

Python: 0.0137

PyPy: 0.0025169

PythonJS: 0.0019

Test4 - function call with normal arguments:

a = 0
for x in range(N):
a += no_kwargs(1,2,3)

Brython: 15.3209

Python: 0.0250

PyPy: 0.00279

PythonJS: 0.3729

PythonJS + with fastdef: 0.0039

In this test PythonJS is 14 times slower than Python, this is because by default PythonJS functions that take arguments, have logic to check those arguments at runtime, and to also check if the function was called from JavaScript and if so - adapt the arguments. This can be bypassed using the @fastdef function decorator, or fastdef with statement `with fastdef:`.

In the above test, PythonJS with fastdef is 6.4 times faster than Python. Note that you can still call fastdef function from external JavaScript, you only need to pack arguments into an array as the first argument, and pack keyword args into an Object as the second argument. Example: func( [x,y,z], {a:1, b:2, c:3} )

Test5 - function call with keyword arguments:

a = 0
for x in range(N):
a += call_kwargs(1,2,3, x=1, y=2, z=3)

Brython: 14.75

Python: 0.0370

PyPy: 0.00417

PythonJS: 0.631

PythonJS + with fastdef: 0.006

Conclusion

Brython is slow and heavy, written almost entirely in JavaScript, and closely follows the Python language standards. Its runtime brython.js is 227KB. Its tokenizer and compiler, py2js.js is 3,500 lines of JavaScript code. It lacks static type analysis and optimizations. Brython is able to translate Python code to JavaScript on the client side. Its performance is much slower than Python.

PythonJS is fast and light weight, written in pure Python, and tries to balance performance concerns with the Python language standards (read more here). Its runtime pythonjs.js is 98KB. The PythonJS translator is 1,900 lines of Python code. The translator includes static type analysis, and optimizes operator overloading and attribute access. It lacks a tokenizer and currently can only translate code on the server side using Python2 as its host. Its performance can surpass Python and approach PyPy in some cases, because it tries to output JavaScript JIT friendly code.

PythonJS 0.8.3 Released

Today I have released PythonJS 0.8.3, this includes the new fastdef and fast for loop optimizations. The performance test above is also included under tests/test_performance.html. The next release of PythonJS will feature integration with NodeJS.

Friday, November 8, 2013

I am currently working on a way to drive animations in Pixi.js using Tween.js and GoogleBlockly. The new SpriteController Block directly parses the conditional blocks it contains, if the selected Event is True, then the animation will play. In this example the Bee moves left and right when the left and right keys are pressed.

Next I will be working on improving the event system by adding more event types, branching logic, relative animation, and variables.

Wednesday, November 6, 2013

Pixi.js

Pixi.js is a fast 2D rendering engine that is optimized for WebGL, and is able to use HTML5 Canvas as a fallback. Using WebGL Pixi is able to render high resolution graphics very quickly. The Pixi API is well designed, and easily wrapped in PythonJS, see my bindings here.

Google Blockly Integration

The officially documented way to integrate Google Blockly with an external API is using Blockly.addChangeListener( my_callback ), your callback will be called each time the UI is updated. Your callback can then call Blockly.JavaScript.workspaceToCode() and then use JavaScript's eval to execute the string that it returns. This only allows for weak integration, here are the following problems:

Your callback will be called for every single UI event, like moving blocks, or typing in an input field.
This is slow and makes it very hard to catch just the events you are interested in.

You have no way to send signals back to the blocks that generated the code.
This makes it impossible to update fields on Blockly's blocks, like number fields.
For example, your custom block creates a Sprite, and then in the game the sprite is moved,
now your code will need to update the position field on the block in Blockly's workspace,
but the Sprite has no reference to the block to do so.

Each time your callback is triggered, you might have to tear down and reinitialize alot of state.

This is generally a bad fit for an Actor model, where entities carry out their own behaviors,
and interact with their environment using dynamic rules.

The above problems show us that the official way to integrate Blockly is pretty much useless with a dynamic system that requires bi-directional updates. The workaround to these problems requires us to modify Blockly's prototypes and functions so that blocks can be created from an external API, and UI events can be caught directly for each field. My solution is implemented in the binding layer between Blockly and PythonJS. Blockly's source code can remain intact - because prototypes and functions can be changed at runtime in JavaScript using special syntax in PythonJS. You can see my binding here.

Wrapper Decorators

One of my goals when starting the integration between Google Blockly and Pixi was to write as little wrapper code as possible, but not so little that it was unclear how things are tied together. To achive this I implemented two special class decorators: @pythonjs.init_callbacks and @pythonjs.property_callbacks. These are different from normal Python class decorators because they inject some code and attributes into the class and it's methods at compile time. The "Block Generator" in the Blockly binding can then use the extra attributes to hook in it's own callbacks for when instances of a class are created, and when values are set on property setters. In my first attempts to integrate Blockly with Three.js I had written alot of code to generate a custom block for each Three.js class. The new Block Generator can wrap a class and generate a custom block with a single call: block.bind_class( my_class ).

To run this demo you need the checkout the latest source code for: Pixi.js, GoogleBlockly, and Tween.js. Then pull the latest source for PythonJS. The source code for the demo is here.