Python vs JS Speed Overview

January 10, 2020

First of all, given that title, let me make it clear that this is not a comprehensive analysis of performance measures between two languages. Rather, I was translating some Python code (which some very reasonable scientists gave me) and upon finding that it was about 1 order of magnitude slower than the Javascript version, I decided a little investigation was in order.

Multi-processing

The first thing I found was that the original Python code uses a package called multiprocessing. The purpose of this module is to parallelize code across cores on a multi-core machine. The researchers had in part identified DCP as a potentially valuable tool because they already knew their task was simply parallelizable.

So they essentially took a for loop in their code that was perfectly parallel and did the syntax juggling needed to use multiprocessing. Here’s an excerpt from my test case as an example:

def test(**kwargs):
    # option 1 - simple for loop
    results = [func(x, **kwargs) for x in xx]
    # option 2 - parallel for loop
    packedfunc = partial(func, **kwargs)
    pool = mp.Pool()
    results = pool.map(packedfunc, xx, chunksize=1)
    pool.close()
    pool.join()
    # either way we get back results
    return results

In the real case we had a function calling a function calling a function so I suspected some overhead their played a role, but we can ignore the details of func because this timeit call

timeit.timeit("test(amplitude=0.2, epsilon=0.1)", setup="from __main__ import test", number=2000)

took 243.281s for option 2 and 1.432s for option 1.

So the first lesson is: don’t assume that because you are using the multiprocessing function you are getting a nn-fold speed up where nn is the number of cores you have. Someone could poke through my example (it was stripped down to just 36 lines, 10 of which are above) and figure out exactly why but in the context of DCP there is one important factor to consider: Python does not support multi-core execution through multithreading. It spins up a new Python interpreter on each core and that costs overhead.

My example code exacerbates this because it executes so quickly (just a couple of dummy multiplications and array manipulations), but even in the real-world code this slow-down was around 8x and those calculations took a minute or more to run. So what else could it be?

Smart Javascript

When I dug deeper into the timing issue I found that in addition to multiprocessing slowing things down in general (in this specific use case) I was still seeing my Javascript run to completion faster. As I began doing head-to-head comparions I noticed consistently that when it came to operating on large arrays, say 60k elements, by adding, multiplying, exponentiating etc. elements together, sequentially and so on, that the Javascript was either on par with Python or as much as 10x faster.

A quick google search of speed comparisons between them confirmed what I was seeing, when it comes to crunching numbers, Node beats Python:

Some people are quick to attribute this to the fact that Javascript is run with a JIT in place to compile down to machine code (Node and Chrome use the V8 Javascript engine). But Python has a JIT too (Numba) and my project was using it. So what’s the deal?

While I don’t have a definitive answer yet, I can say this for a start: the browser wars are a fight for performance and that means (in part) Javascript performance. That means a lot of time, and effort, and research, and of course money are going into Javascript engines. If that effort eclipses the work of the hard work of the devs working on Numba, we shouldn’t be too surprised :)

Conclusion

What this boils down to is an important expectations reversal from my perspective as a researching scientist. I’m not aware of Javascript being taught anywhere in scientific computing courses in spite of the numerous benefits it offers:

  • High portability (any device with a browser)
  • Great performance (thanks to the wizards working on V8)
  • Easy parallelizability (thanks to DCP)

Now you may think it’s unfair to compare Javascript to Python in this case. After all, aren’t most scientific projects written in a compiled language for precisely this reason?

I’ll have to do another post on this topic some other time, but my experience so far in other translation projects from FORTRAN and C has often seen projects run between 1 and 3x slower in Javascript. That means the ability to parallelize such a project onto as few as 3 cores (less than what most laptops have nowadays) is sufficient reason to make the switch to Javascript. You stop managing your own memory (which is a good idea for most scientists based on the code I’ve seen our there) and you pick up all the benefits of writing / think in a higher-level language.

+
>