Live data from Hacker News

Why Python, Ruby, and Javascript are Slow

speakerdeck.com

131–140 of 203 posts

Re: Why Python, Ruby, and Javascript are Slow

#131
post #109

Earlier quoted context omitted.

There is no "production" in scientific programs. It runs once correctly to make the figure... more seriously, ontology is often a moving target, so the longer in takes to rewrite significant parts of the data structures, the less time there is to do science. re: concurrency: I have a script that boots hundreds of IPython workers on hundreds of cores. I then make a client object (in antoher IPython shell), and map my…

It's 50% faster but took more than 50% longer to write At this point it's useful to know how long it takes to run, and how long to write. Is a run days long, months long, or years long? Or another way, is concurrency more expensive than a C re-programmer? Also a win because PyCUDA takes care of the uglier details. Is there not an analogous C++ library to take care of ugly details? (I actually like python a lot, so th…

Typical simulations for us take between half a minute and several days, but this can depend because it's typically necessary to do a parameter sweep in several dimensions (leading in extreme cases to runtimes of several months on a cluster).

I believe Thrift (now shipped w/ CUDA SDK) makes things easier, but (since you know Python) nothing like NumPy exists in C++ and PyCUDA maps NumPy seamlessly into GPU computing, which is a big win.

Re: Why Python, Ruby, and Javascript are Slow

#132

Earlier quoted context omitted.

Haskell and languages in the ML family have a lot of opportunities for elaborate static analysis, which often allows the resulting programs to be quite clever about optimizing the resulting programs. As one example, the GHC Haskell compiler uses loop fusion to combine multiple passes over a list into a single pass with no intermediate copies of the list produced. Consequently, Haskell code like map f (map g (map h so…

In the Haskell case you could also do something like this to avoid needing that optimisation: map (f . g . h) someList And in Python 3, map returns an iterator, not a list, so you aren't building the full list until you ask for it, and you never build intermediate lists in your example. You can do the same thing in Python 2 with the itertools.imap function.

`map` is the trivial case. There are plenty of loop compositions that are completely non-trivial to do by hand. That's why array fusion (see e.g. repa or vector) is a huge win.

    foldl g . scanl y . concatMap x . filter h . unfoldr k
Fuse that by hand.

This is why we have optimizing compilers. They do what you could have done, only more often, and without mistakes.

Re: Why Python, Ruby, and Javascript are Slow

#133
My own piece of feedback based on my experience. The slides were good. But like others, JIT is not all rosy. In V8 and Dart and .NET, code gets compiled to native code as soon as possible. I think that's the best case scenario in general. You then don't have to guess as much.

The author didn't mention method dispatching. I think it's an issue for many languages. In Dart, they tried to optimize it by the specification by mostly eliminating the need to change methods at runtime. In Ruby I watched a video by one of the core Ruby developers and he said that in Ruby method dispatching can be very complicated requiring up to 20 steps to resolve them.

As important as getting the best performance out of programs is to get the programs created in the first place. That's why I'm against shying away from larger codebases. I'm in favor of OO programming exactly because I think getting things done comes first, even if that could complicate the implementation of the toolset. And OO is all about layers of abstractions that bring more performance costs with them.

That said, I absolutely abhor type annotations. They make code hideous and decrease the opportunities for experimentations. Instead of reading a + b = c algorithms, you may need to parse A a + B b = C c source code.

In Dart we have Optional Types. But the core developers are fond of type annotations, so most samples they post come with them. I take relief in being able to omit type annotations while experimenting, researching and ultimately prototyping. Although in a way I feel like a rebel in the community for this disregard. Thankfully there is this chance to share a community with them.

Reading the part that you don't like adding heuristics to help programs to go faster reminded of adding types to them even if they are mostly disregarded as in Dart.

Then again, not all "dynamic languages" are the same. Some are truly dynamic with eval and runtime method changes. Others, not so much. Sometimes the tradeoffs allow for other kinds of gains that could come into play like when deploying. So there is a lot more to it than just getting the algorithms correct.

Re: Why Python, Ruby, and Javascript are Slow

#134
post #59

Earlier quoted context omitted.

It's how you write fast algorithms in general, in any programming language. Minimize the number of reads and writes per iteration/recursion. In higher-level programming languages, it's just a bit harder to control the number of reads and writes because you're working at several layers of abstraction above them, and are concerned with solving higher-level problems. Use the language that provides the appropriate level…

On the other hand, why should the abstraction layers prevent that? I mean, abstraction layers abstract away the [hopefully] unimportant low-level choices from me - but "copy or not copy" or "allocate once or allocate thrice" isn't a choice that I need to make anyway; the abstraction layer simply should make the 'non-copy' choice for me. Exactly the same way that the C abstraction layer right now makes the proper opco…

its not always possible to go with the "non-copy" choice. for example, there are very good reasons for having immutable strings, and once you've made that choice at the language level every string function you write is going to copy at least once.

I think Alex Gaynor is correct and that basically what is wrong at the moment is that dynamic languages lack API's that have any sensitivity to performance concerns. There's always going to be a hard limit based on the nature of using a JIT vs. a static multi-pass compiler. There's always going to be a hard limit based on fundamental language choices (implementations of primitives, mutable vs. immutable strings, amount of overhead in object instantation, etc.) But we're nowhere near those limits right now.

Re: Why Python, Ruby, and Javascript are Slow

#135

Earlier quoted context omitted.

Forgive the naive question, but why not: l = [object()] * 100 Perhaps the difference is stack vs. heap?

That will create a list of 100 instances of the same object. object[0].x = 1 print object[1].x > 1 Edit: On second read, it looks like you're asking something other than what I thought you were asking. Yes, you could create a list of 100 items and then replace its elements, but that's not idiomatic.

but it is idiomatic in C, which is the point of the slide. C was built around a performance focused idiom, which is to pre-allocate memory and then do in place writes and swaps to mutate the buffer to the state you need it to be. Python is built around an idiom of largely creating copies of objects and appending them to dynamically allocated lists. Its a much slower idiom.

Re: Why Python, Ruby, and Javascript are Slow

#136

Earlier quoted context omitted.

Yes, I've always just assumed that internally a resizable Python list was a linked-list I learned about in C... fits perfectly. I suppose using an array must improve performance in typical cases, while the resizing (a linked-list advantage) happens less often.

Resizing is not a linked-list advantage, though. Resizing is an ammortized-constant-time operation. The only advantage I'm aware of for linked lists is insert and delete (but not append and pop), which are constant time in a linked list but O(n) in an array. The thing is, though, that doing insert() on a linked-list usually requires a seek first. Which is O(n) on a linked-list (and may be O(n), O(logn), or O(1) on an…

linked lists are the default data structure underlying almost every implementation of Stacks and Queues and the variants of those. they're far from useless.

they're also such a flexible data structure that it is literally the ONLY data structure necessary to implement any of the LISPs.

Re: Why Python, Ruby, and Javascript are Slow

#138

Earlier quoted context omitted.

Speed in Python (or Ruby, or JS) isn't a big deal... until it is. When that happens, would you rather have to switch over to C and glue the resulting binary in (assuming you're not using JS, in which case you're just SOL), or would you rather have a high performance API at your fingertips for optimization when you need it?

Well, my usual answer there is to change the file extension to .pyx and see what Cython can do with a few type annotations. Usually the results are pretty good, and sometimes they're very good.

I think most of the gains you'd get from that would be orthogonal to the gains you would get from giving the JIT a little more information about allocations.

Re: Why Python, Ruby, and Javascript are Slow

#139
post #99
post #91

Earlier quoted context omitted.

Being fairly new to C, is appending to / dynamically growing an array really just a matter of "a pointer or two"? How can you take for granted the memory space past the end pointer is available?

On an array, you can't. This means that you can't on a Python list, either. mixmastamyk is mistaken about the implementation details. But if you assume that "list" means "linked list", then you can just navigate to the correct part of the list, allocate enough space for one new cell, and stitch together a few pointers. Allocation and stitching is O(1). In general, navigating to part of the list is O(n), but if your l…

Thanks for the detailed response.

I was in fact taking it as almost a given that Python lists were backed by arrays under the hood.

Re: Why Python, Ruby, and Javascript are Slow

#140

Related to this is the importance of deforestation. Some good links: * http://en.wikipedia.org/wiki/Deforestation_%28computer_scien... * http://www.haskell.org/haskellwiki/Short_cut_fusion Deforestation is basically eliminating intermediate data structures, which is similar to what the "int(s.split("-", 1)[1])" versus "atoi(strchr(s, '-') + 1)" slides are about. If you consider strings as just lists of characters, th…

Try telling that to a dedicated C++ guy. Apparently the C calls are dangerous, dirty and just to be avoided.
Post reply on HN