top | item 5545625

What's Going On (on "Why Python, Ruby, and Javascript are Slow")

283 points| ANTSANTS | 13 years ago |jmoiron.net | reply

192 comments

order
[+] NateDad|13 years ago|reply
Everyone keeps saying "Use Python and write the slow parts in C"..... except that writing GOOD (secure, correct, safe) C is really hard. That's actually the main reason why the Go authors came up with Go. Not to replace Python etc, but to replace C/C++.

I'm kind of surprised that all the "Python: let's get stuff done" people are suggesting writing a whole bunch of code in C, one of the least "Getting stuff done" languages there is.

For those who have asked what the productivity of Go is like vs. Python + C: Go is very productive. When newbies to Go say it's "verbose" they mean "I have to write out some 3 line loops that in other languages are one line". If that's the major cause of slowness in my development cycle, I'd be ecstatic.

Go is also very easy to write "correct" code in... no buffer overflows, no memory leaks, no access of random memory, no unnecessary allocations, and a very clear and defined behavior from clear, concise code.

[+] gsnedders|13 years ago|reply
Given the original presenter is one of the major contributors to PyPy, I'm fairly certain that wouldn't be his first advice: I'd expect it'd be to try PyPy (and file a bug if it's slower than CPython!).

Also note on the last slide: "Stop writing C extensions, use something like cffi". There's two (really) good reasons you should do this: firstly, your code is no longer tied tied to CPython, so using alternative Python implementations (like PyPy) becomes viable; secondly, you don't have to use C — you can trivially use anything that exports functions using the C ABI.

[+] klrr|13 years ago|reply
Go is by far a replacement for C. C was made as a language for writing operating systems. C++ tried add more abstraction making it a better choice for more regular applications. Go was made for writing distributed systems.
[+] hogu|13 years ago|reply
I've had pretty easy success moving my bottlenecks into Cython. With the development of Numba, https://github.com/numba/numba, this is going to become even easier(I work for the company that produces Numba, but I do not work on Numba). I think one thing the scientific community does right, is that we keep our data in NumPy arrays. This makes it really easy to write extensions in other langauges because NumPy arrays are very transparent and easy to access in other languages (they're just chunks of memory with dtypes, and a shape)
[+] pekk|13 years ago|reply
Nobody said you have to write any C. If you are freaking out about performance, the option to write some parts in C is valuable.
[+] officemonkey|13 years ago|reply
>"I have to write out some 3 line loops that in other languages are one line"

I think every vim, emacs and decent IDE user would say "I has a text editor. Your argument is invalid."

[+] MostAwesomeDude|13 years ago|reply
Please don't say that "all" of us endorse writing C. Many of us gave up on writing C a long time ago and want to see pure-Python solutions to things.
[+] d0m|13 years ago|reply
Time of "Getting that shit done" and cost of long-term maintenance are two of the main reasons why people use Ruby/Python, not for the raw performance. I thought that was an old debate. And anyway, from my experience, Python has never been the cause of the slowness.. It's more about tweaking that DB query, smartly packaging the assets or using a better algorithm. (I know it's not always the case. I.e. If my program has to run in the space where bits could be randomly shifted, some additional thoughts would be needed, obviously.)
[+] pifflesnort|13 years ago|reply
> Time of "Getting that shit done" and cost of long-term maintenance are two of the main reasons why people use Ruby/Python, not for the raw performance.

Plenty of other languages "get shit done".

> And anyway, from my experience, Python has never been the cause of the slowness.

Then you're not actually performing performance analysis. Python is S-L-O-W.

I've written large systems in Python. I've profiled them to find the hotspots. I've micro-benchmarked them against implementations in other languages (C, Java). Python lost. Always. By a lot.

So then I had to rewrite critical sections of the Python in C (or accept that we'd need 32 servers instead of 2). Eventually I stopped bothering, and used a different language, because the aggregate cost of a thousand small performance issues (and a few big ones) is actually quite high.

[+] coldtea|13 years ago|reply
>And anyway, from my experience, Python has never been the cause of the slowness.. It's more about tweaking that DB query, smartly packaging the assets or using a better algorithm.

That's because you constrain your use of Python in I/O bound problems, like webpages and such. If you had any CPU bound problems, then you would have found Python is quite slow -- and you can get 2-20-100 times the performance in another language.

Python is very slow (compared to what it can be), which is what necessitates tons of workarounds like: C extensions, SWIG, NumPy and SciPy (C, Fortran, etc), Cython, PyPy, etc. A whole cottage industry around Python's slowness. Which is nice to exist, but not an ideal solution: suddenly you have 2 codebases to manage in 2 different languages, makefiles, portability issues, etc etc. All of those are barriers to entry.

[+] jes5199|13 years ago|reply
Yeah: your real bottlenecks are going to be 1) getting data out of your database and 2) sending packets across the internet. These are 100 times and 1000 times slower than your program, the 10x penalty to use ruby/python instead of C/C++/go/rust isn't going to matter.
[+] jacobmarble|13 years ago|reply
True, the author doesn't really address "effort to get shit done", but Others have commented on this and there seems to be consensus around "Go is a very human readable/writable language".

Anyone know of a human read/write-ability comparison of programming languages?

[+] RegRegReg|13 years ago|reply
Exactly, if you want shit done you need to use Ruby/Python. If you want actual stuff done you use something else.
[+] pointyhatuk|13 years ago|reply
Actually I find the opposite. Python is easy for quick wins but from a maintenance, performance and reliability perspective it rapidly falls off a cliff. If your code propels your business then these concerns need to be considered up front.

Even off the shelf functionality doesnt have the same time/complexity properties as say c#, java or go resulting in a fairly basic algorithm reaching ridiculous time constraints very quickly.

As for CRUD type apps, you are probably right but its the bits that aren't CRUD which add the most value to your product.

[+] kalkin|13 years ago|reply
It's too bad that this discussion is ending up mostly in language wars, because Alex' talk was about how to improve the situation, not how to prove that your favored technology is always the right tool for the job and everybody else is wrong.

Let's say that you're doing some NLP and you want to pre-process your strings in Ruby because its easy and elegant. Idiomatically you might write something like "my_objs.map(&:to_s).map { |s| s.gsub(/\W\S/+, '') }.reject(&:blank?)" - perhaps broken up into multiple lines - but you're allocating three arrays and re-allocating every string. And in real life you're probably doing more pre-processing steps which involve additional arrays and possibly additional string re-allocations.

You could switch to gsub! which does the non-word stripping in place, but it "Performs the substitutions of String#gsub in place, returning str, or nil if no substitutions were performed", so you're adding an extra conditional. And it's going to be harder to avoid re-constructing a bunch of arrays.

There's no intrinsic reason you couldn't have basic Ruby language APIs which let you write code which chains operations just as naturally and obviously, but don't wastefully re-allocate. Think of how ActiveRecord relations defer the actual SQL execution until it's unavoidable.

(Haskell-style laziness might solve the main issue above, but I've run into analogous unavoidable-allocation problems with Haskell. If I'm depth-first searching a board game state-tree I shouldn't have to re-allocate the whole state representation at each step, but it's pretty hard to avoid while writing elegant Haskell since modifying memory in-place has side-effects.)

I don't know Go, but maybe instead of talking about why people who still use Python or Ruby are or are not stupid, we can talk about what it gets right that other languages can adapt?

[+] joe_the_user|13 years ago|reply
"Programmers of elegant scripting languages desire similarly elegant looking code with flexible data structures even where no flexibility is in play. Some who know better will understand that technical limitations make the difference between hashes and other types of named lookups essentially nil for many popular language runtimes."

OK, if no flexibility is in play in many usages of hashmaps, should not an intelligent interpreter/compiler be able to detect this and modify the use of the hashmap accordingly? Maybe even use a different lookup system in those cases? It seems like that would be ... elegant.

The reason VMs don't need to be slow is that you don't need to treat an eval call literally, you don't always need to create a full environment to parse a twenty character string, right? But if you can get around that, should you not be able to get around hash-map's problems? Am I missing something.

[+] cjh_|13 years ago|reply
This question is partially address in the articles linked to at the start [1]

I linked to the slide that begins to mention it, rather short though.

The answer is that we can add heuristics, but that we should also be able to solve this somewhat on a language/library level rather than only at a vm level.

[1] https://speakerdeck.com/alex/why-python-ruby-and-javascript-...

[+] jes5199|13 years ago|reply
Doesn't the V8 javascript engine do something like this? Where it builds C-structs instead of hashmaps if it can detect that your objects are being built in a consistent way
[+] Too|13 years ago|reply
IIRC the Dictionary<TKey, TValue> in .NET does this. If you have less than 8 items in it a lookup is simply iterating over the keys like a plain array instead of keeping a hashmap.
[+] voidlogic|13 years ago|reply
Since the author uses Go as a basis of comparison. I thought these benchmarks from The Computer Language Benchmarks Game might be interesting:

Go vs. Ruby: http://benchmarksgame.alioth.debian.org/u64/benchmark.php?te...

Go vs. Python: http://benchmarksgame.alioth.debian.org/u64/benchmark.php?te...

Note: These results are from the 64-bit single core machine since it has already been updated to Go 1.1 beta 1. There is also a faster Go regex program (#7) that hopefully can be made to work again after 1.1 is released (in place of #1).

Although still using Go 1.0.X ,the recent TechEmpower benchmarks might be interesting if you have not seen them: http://www.techempower.com/blog/2013/03/28/framework-benchma... (It looks like Go 1.1 might be 3x faster: https://gist.github.com/errnoh/5320784)

P.S. As always your own program is always the best benchmark to use, your millage may very, take your gain of salt, etc etc.

[+] yew|13 years ago|reply
Probably worth noting that the presentation that the author is discussing is less about 'which language is faster' and more about what sort of abstractions different types of languages encourage.

The presenter explicitly takes the position that Go and Python (actually PyPy) should be equally fast given the same sort of operations. Just that Python's idioms tend to be less efficient. He's a PyPy developer too, so I would think he would know something about what's going on under the hood. (He also talks a bit about why the VM doesn't/shouldn't try to optimize these cases automatically. Worth a look, it isn't long.)

[+] ilaksh|13 years ago|reply
I am sure I am just going to have this comment buried because people don't like to have Python and Ruby compared unfavorably, but I have to try to help people out.

Looks like a lot of people STILL haven't caught on to how big of a performance advantage Node.js's V8 engine gives you over Python and Ruby. Look at benchmarks like this one: http://fengmk2.github.io/blog/2011/fibonacci/nodejs-python-p...

Most of the time modern JavaScript is compiled to native code. Its much faster than you realize.

The cleanest code is a new language called CoffeeScript which compiles to JavaScript. Its more Pythonic than Python. Its the closest to pseudocode that you can get (especially if you avoid a few Rubyisms).

The best APIs available are in Node.js modules (see npmjs.org). Best meaning most advanced API design in that the modules are focused, decoupled, very straightforward. This is also by far the best design for package management and dependencies since it is very open and flexible.

On top of that, Node.js is built from the ground up to support highly concurrent input/output. It just amazes me that people haven't figured out what a big advantage Node.js and CoffeeScript provide over Python or Ruby.

I actually think that a big thing holding people back is that Node.js and CoffeeScript make things so much easier, people think its cheating, and are afraid their programmer friends will think less of them if they take advantage of something that really simplifies their jobs.

[+] TazeTSchnitzel|13 years ago|reply
CoffeeScript is horribly... stabby? Its ambiguous syntax makes it easy to hurt yourself.

And node.js, while great, is not magic.

I would also much rather use Python's classes than do JS OOP or use CS classes. That said, I use node.js more often than Python.

[+] kragen|13 years ago|reply
I've been programming in JS since the previous millennium (compilers, parser generators, 3-D engines, browser extensions, web servers, Comet in 2000) and I've used Node on and off for a few years now. V8 is pretty awesome, but outside of a relatively narrow niche, the advantage of Node doesn't seem that huge to me.

* Yes, your code runs a lot faster in V8 than in CPython or Ruby, maybe a factor of 3 to 10 slower than C (http://benchmarksgame.alioth.debian.org/u32/benchmark.php?te...) instead of the factor of 30 you get with CPython or YARV (http://benchmarksgame.alioth.debian.org/u32/benchmark.php?te...) or the factor of 100 you get with MRI Ruby. But much of the time, if a factor of 30 is a problem, so is a factor of 10! And there are plenty of language implementations that get closer to C than V8 does, without being as error-prone and verbose as C (Java, SBCL, Ada (just kidding), Haskell, OCaml, Scala, occasionally LuaJIT). PyPy has similar performance overall to V8, but a different performance profile. See http://speed.pypy.org/ for details.

* JS is a pretty reasonable language, and CS is even better, despite its lack of getters and setters, its stupid Ruby-derived inclusive-bounds ranges, and a few other minor warts. But it's not nearly as clean as Python or as flexible as Ruby.

* Node makes you rewrite your program into explicit continuation-passing style every time you do I/O, in order to maintain responsiveness. Also, every time you do a potentially long computation, unless they've finally gotten around to adding Web Workers to Node. I've done this in order to get massively scalable I/O in high-performance systems in C, Python, and JS, and it is my considered opinion that it is sometimes not the best way to do things. In cases where this is the best option, Python has Twisted and Tornado, which give you the massively-scalable nonblocking goodness while they still let you run other threads if that's what you think best.

* npm is a pretty amazing ecosystem, but the Python, Ruby, and C ecosystems are orders of magnitude bigger. On my netbook right now, among other things, I have Python bindings to 0MQ, D-Bus, GConf, TidyLib, DNS, GObject, CUPS, Cairo, and the GNOME keyring. Of these, 0MQ, D-Bus, DNS, and Cairo have bindings in npm, while GConf, TidyLib, GObject, CUPS, and the GNOME keyring do not. In realms like natural language processing, symbolic mathematics, and numerical computation (e.g. DSP), there's nothing in the JS world that comes close. Note that this objection applies also to PyPy, since many Python bindings do not yet work with PyPy.

PyPy also offers massive multithreading (a la Erlang, Golang, or rust) and sandboxing (the ability to run untrusted code safely, at least in theory); it contains a generalized JIT compiler generator that allows you to write JIT compilers for other languages very easily; and the PyPy people are working on software transactional memory and dynamic code generation for numerical algorithms.

In summary, I agree that Node is pretty awesome, especially, as you say, coupled with CoffeeScript, and also when you want to share code between browser and server. But there are still valid reasons for choosing other alternatives.

[+] pjmlp|13 years ago|reply
Overall nice article, it just looses a bit by focusing too much on Go vs Python issue.

1 - The are quite quite a few strong type languages with native code implementations since the mid-80's, even GC enabled, Go is not the only one.

2 - There are dynamic languages like Self, Dylan, Lisp even the young Julia that have implementations which achieve C like speeds when compiled to native code, especially when the developers make use of type annotations and use the right data structures.

Self is specially important given that the research work, in a dynamic language, ended up being the heart of Sun's JIT compiler.

[+] mwcampbell|13 years ago|reply
Yes, there have been many other languages with ahead-of-time compilation and garbage-collected runtimes, but Go is one of the few that might be considered popular today. At this point, one might invoke the old chestnut, "If you make technology choices based on popularity, you might as well go with (Windows|PHP)." But there are degrees of popularity, and there are advantages in choosing something that's at least moderately popular, e.g. a large selection of ready-to-use libraries and a pool of developers who don't need to learn a new language on the job.
[+] batgaijin|13 years ago|reply
Yeah I don't sympathize with ruby/python/go people. CL can be interpreted & compiled. Why the fuck is it okay to literally choose the worst options just because people don't like seeing an explicit AST?
[+] Moto7451|13 years ago|reply
I've noticed this in some code I've been refactoring at work recently (a feed generation system). It's easy in Perl to suck in a large amount of data into one array and map/grep it into another array of hashes and output filtered/formatted data in a few lines. The problem of course is that your business'/project's own growth overwhelms the algorithm as the data structure balloons. This is further compounded by libraries which are designed to accept an array as input (reinforcing the idiom causing the first problem) since you end up with thousands of additional iterations.

I refactored the code to lazy load each item in the collection and map/filter/generate the XML at the end of each node's iteration. The end result is a nice flat memory space (save for the odd leak here and there) that's about 1/10th of the original program's, at the 'expense' of only a few more lines of code.

[+] Xcelerate|13 years ago|reply
This thread has got me cracking up. I'm sure all of you are very smart, yet you all treat each other as though the other person is a stupid, incompetent programmer. It's a trend I observe everywhere in online programming communities.
[+] cjh_|13 years ago|reply
Very interesting read.

I have only written a small amount of Go, I have been through tour.golang.org, just waiting for the right project for me to use it on.

Currently whenever a new project comes along I reach for C or Perl, hoping to find a niche between them for Go.

[+] berntb|13 years ago|reply
The feature with scripting languages compared with system languages (C, Java, etc) is that they are fast -- not to run code, but to develop functionality.

The article argued that idiomatic Go gave at least as good runtime speedup as a normal scripting language (Perl, Ruby, etc) with the (small) time critical parts rewritten in C.

But it didn't touch the important question -- is the development time for Go faster than for scripting languages with critical sections in C?

Go looks fun, but I'd personally want information on that metric before trying it on a project. [Edit: Yeah, as the article noted, Go excels re parallelization etc. I might Go for it in those use cases.]

(What I'd really want is to find a place which use Lisp, since development speed is roughly as fast as for scripting languages and there has been good compilers for decades.)

Edit: Spelling, must start going over the room for the keyboard when using the iPad.

[+] alexchamberlain|13 years ago|reply
On the to garbage collect or not issue briefly mentioned at the end, the best reason to use an unmanaged language is predictability of performance. You know the GC won't step in at a critical moment. I regularly get frustrated by Eclipse becoming unresponsive for several seconds.
[+] pjmlp|13 years ago|reply
There are real time GCs for many languages that compile to native code.

The way Eclipse is coded is not a good example of GC performance.

[+] bm1362|13 years ago|reply
I'm currently hitting some performance issues trying to use Python to make a naive physics engine- does anyone have any reading recommendations on best practices for python performance?
[+] plg|13 years ago|reply
the endless debate continues...

choose two:

a. fast execution of the code on the machine

b. fast code generation by the programmer

c. fine-grained control over every aspect of the code

I know which two I'd rather have, esp in the long run.

[+] YesThatTom2|13 years ago|reply
Wouldn't adding goland-style slices to Python be awesome?
[+] almost|13 years ago|reply
It's not quite the same but some of the use cases (and a bunch of other stuff) would be covered by numpy arrays. You can create multiple views on the same in memory data with different start and end points and different strides.
[+] kkuduk|13 years ago|reply
well, there are memoryviews, but they could have been designed better
[+] RegRegReg|13 years ago|reply
The biggest confusion is seeing Python/Ruby/JS as a programming languages. They are not. They are scripting languages. Something you put on top of your core code.
[+] pekk|13 years ago|reply
Does the phrase "scripting language" have any hard technical meaning or is it just another subjective label?
[+] doktrin|13 years ago|reply
This might make sense if you contrasted "scripting" to something a little more specific, like "system" development. However, creating a "programming" v. "scripting" dichotomy makes no sense IMHO. Ruby & Python are programming languages that can also be considered, informally, scripting languages.
[+] corresation|13 years ago|reply
Both the submission and the target of the submission dismiss dynamic typing as a source of slowdowns (in the unfortunately haughty, completely unsupported manner that is too common in the industry. It is, in effect, the head-in-the-sand approach: If you don't want something to be true, simply keep saying it isn't and somehow that will become reality), yet I guess we need to define what "slow" is then, using as an example an empirical test (ASM.JS) that certainly serves better than some people waving their hands about innovations in JIT compilers.

The primary optimization that ASM.JS brings is its static type system. With that, and the obvious benefits to the code path, intensive activities can be run at 1/2 native speed, versus the 1/12th of so native speed available with dynamic typing.

Is 1/12th native speed "slow"? In most cases -- where the code is the duct-tape that glues together native code (which is the case on web servers, admin scripts, etc), no, it isn't slow at all. But from a basic optimization perspective, asm.js sure seems to indict dynamic typing if you really care about squeezing every cycle's maximal output.

[+] gsnedders|13 years ago|reply
No, the primary optimization asm.js brings (at least in OdinMonkey) is AOT compilation.

Fully typed code should produce the same code from IonMonkey without OdinMonkey: the problem is you have to get up to 10k calls (at least last I looked!) of a function before you get there; but once you are there it is just as good. It's the build-up, through the interpreter and the less efficient code-generators (in terms of code generated, that is; they're more efficient in terms of their own runtime), which really hurts.

[+] munificent|13 years ago|reply
> The primary optimization that ASM.JS brings is its static type system.

Well, that and not having objects, user-defined types, closures, strings, polymorphism, or even garbage collection. asm.js is fast because it's essentially BCPL, not just because it has a static type system.