Live data from Hacker News

Why Python, Ruby, and Javascript are Slow

speakerdeck.com

121–130 of 203 posts

Re: Why Python, Ruby, and Javascript are Slow

#122
post #108

atoi(strchr(s, '-') + 1) What does this do? Finds the first instance of a -, and converts the remainder of a string to an int. 0 allocations, 0 copies. Doing this with 0 copies is pretty much impossible in Python, and probably in ruby and Javascript too. The copying could be avoided in non-idiomatic Python: int(buffer(s, s.find("-") + 1))

   +s.substr(s.indexOf('-') + 1)

Re: Why Python, Ruby, and Javascript are Slow

#123
post #59

A nice talk. The punchline for me was: Things that take time •Hash table lookups •Allocations •Copying Interestingly, that's exactly how you write fast C++ code. His point is that languages like Python lack good API's for preallocating memory.

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 opcode-ordering choices for me as good (or better) than I can do manually in assembler.

The problem is that we haven't yet implemented those abstraction layers in this smart way - for example, Haskell can implement 'fusion' of multiple string operations so that they are merged together and executed without intermediate copies; and the abstraction layer for that is exactly as high-level as the Python examples in original poster's slides. Sure, it's objectively hard to change core Python like that - but it theoretically can be done, so it should&will be done.

Re: Why Python, Ruby, and Javascript are Slow

#125

Earlier quoted context omitted.

Yes, you could do a background thread, with some caveats: 1. On most current CPU's, this will cause really bad cache/memory thrashing, enough to probably impact the program. 2. This may actually cause significant slowdown, depending on how long it takes to optimize a given set of code (IE it may be better to spend 100ms paused optimizing than 5000ms in the background). This is, of course, a latency issue. 3. State of…

Background compilation in a separate thread actually works pretty well. IE9 has been shipping it with Chakra for a while, and Firefox is now getting it (and it improved the benchmarks a lot, especially on ARM).

Good to hear it's gotten better. Admittedly, I wasn't thinking about browser based JITs when I said that :)

I'm actually curious if you have any stats on how much of the time this is being done on actual busy machines where it's going to compete for L1/etc resources vs how often it's able to be offloaded onto an otherwise empty core.

IE i expect their to be a significant difference in the use cases for JIT's like PyPy, which are probably going to sit on shared servers that folks are trying to maximize utilization of, vs desktops where I imagine most browsing probably doesn't use all cores at 100%.

Re: Why Python, Ruby, and Javascript are Slow

#126
post #62

Earlier quoted context omitted.

I would say that the difference between fast Python code and C is still quite large. - the syntax is less error-prone - ownership semantics are much clearer. You'll never segfault because you sent some memory into the wrong function - not as much detail is needed for memory layout, the JIT abstracts a lot of it away - there are high-level APIs handy - development and distribution are simpler with one less language -…

I'm interested in this discussion. Which of those issues could you dispense with using more modern APIs and idioms in C? Look at Objective C (mentally wipe off all the object goo), particular NSMutableString and NSMutableArray and NSMutableData, for examples of what I'm thinking about. The C syntax we're stuck with. But how big a deal is that syntax? Segfaults are mitigated if you don't expose pointers, except to the…

I would say that the things you mentioned that we shouldn't count already add up to a lot (syntax, memory management, segfaults, security vulnerabilities). Another big one is that if you have these additional constructs in Python you can smoothly migrate from slow to fast code. You don't have to create a C file, rewrite your whole algorithm, create a build process to compile the C file (which you don't need with Python), and get the C functions to be callable from Python. In contrast with the method proposed in this PyPy presentation you just change a couple of lines. If instead of advocating writing just the performance critical parts in C you are advocating writing everything in C, then in addition to the issues you mentioned then you're missing the high level features of Python for the code that isn't performance critical.

The woes of pointers (segfaults and security vulnerabilities) cannot be addressed in a library without a performance penalty. If you want a nice error message instead of a segfault or random memory overwrite you will have to pass around type information at run time. You could however have a production version of the stdlib that did not pass around type information, but that would only solve the issue at development time: the security vulnerabilities in production would still be there.

There is also an argument to be made that many of the optimizations mentioned in the presentation can be done automatically by the compiler/JIT. For example Javascript JITs already optimize small hash tables used as objects, since every Javascript object is a hash table. Load forwarding followed by code motion can remove unnecessary intermediate allocations. And the square example should have been written as:

    [i*i for in in xrange(n)]
This can allocate the result list of the right size at the start of the allocation.

Re: Why Python, Ruby, and Javascript are Slow

#127

Meh, MEH. I'm almost never waiting on my python code. I'm waiting on network or disk or database or joe to check in his changes or etc. I'm sure there are people who do wait. But that's why numpy, c extensions, all the pypy, psycho, and similar things exist. Python and more broadly "scripting" languages are for speed of development. Something else can take on speed of execution faster than 90% of people need it to be…

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.

Re: Why Python, Ruby, and Javascript are Slow

#128
post #33
post #25

Earlier quoted context omitted.

It's not an unresolved question whether idiomatic Python is slower than idiomatic C/C++ for solving comparable problems. Python is much, much slower than C.

This is completely true. It is indeed well known and common wisdom. However, I think the point the parent was trying to make is: Python is much slower than C and many other languages, however most of the time speed is unimportant. When it becomes important, there are many technologies to mitigate the problem in your "hot loops." If speed is your primary concern don't use Python et. al. If it isn't your main concern g…

When it becomes important, there are many technologies to mitigate the problem in your "hot loops."

There is an implicit assumption there that most of the time in your program that could be saved is spent in a small number of hot spots. This will often be true, but unfortunately it is not necessarily so.

This is a particular problem in languages like Python, which are useful (among other things) for their support for rapid prototyping and their easily readable code. All of that is lost if you can’t perform local optimizations to reach an acceptable level of performance, leaving a ground-up rewrite in a faster language like C as the next most likely strategy.

The kinds of techniques mentioned in the linked slides could help to create a middle ground that would be very useful for performance-sensitive projects that currently find themselves between a rock and a hard place.

Re: Why Python, Ruby, and Javascript are Slow

#129
post #50

Earlier quoted context omitted.

@chadcf and @tptacek I was responding to @tptacek criticism of the parent not the deck. The deck is great and it mirrors the wisdom I have picked up from optimizing my own code over the years. I personally find it really frustrating not being able to easily pre-alloc lists in Python. I think that having better APIs would go a long way. As the deck says: "Line for line these languages are fast!" "We need better no-cop…

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.

Re: Why Python, Ruby, and Javascript are Slow

#130

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…

Deforestation is easily done in lazy languages like Haskell. As for GC, it would be nice to have good real time GCs in runtimes.

It has nothing to do with laziness. It has everything to do with the guaranteed absence of side effects.

Deforestation is /more useful/ in strict languages, because allocation of temporary structures costs more. So fusion on strict arrays is better than on lazy streams.

You just can't do it unless you can freely reorder multiple loops, and to do that you need a proof there are no side effects. Haskell just makes that trivial.

Post reply on HN