Hacker News new | past | comments | ask | show | jobs | submit login
What's Going On (on "Why Python, Ruby, and Javascript are Slow") (jmoiron.net)
283 points by ANTSANTS on April 14, 2013 | hide | past | favorite | 192 comments



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.


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.


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.


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)


> I work for the company that produces Numba, but I do not work on Numba

In experience Cython and C++ extensions are still the best way to speed up existing Python algorithms. Initially Numba showed great promise but after attempting to jit compile several simple hotspots ( cryptography and hashing functions primarily ) in my application I discovered lot of bugs and apparent numeric instability particularly in the trigonometric and bit functions. Have these issues been resolved? For instance for Diffie–Hellman calculations?


Right now Numba is still pre 1.0 and so there will be issues for specific cases. The best way to get them solved is to provide your test-case so that we can grow the test suite.

Right now, I agree that Cython and pure C++ is still the best approach if you have a case which Numba does not yet quite solve. However, I expect this to change over the next 6-12 months as Numba is getting a lot of attention both inside of Continuum and externally at several industrial-strength locations.

Our goal is that Numba can replace (almost all) Cython and raw C-extensions or even Go-extensions faster than other language approach will be able to build a run-time and ecosystem the equivalent of what is available with Python.

The roadmap (which includes Go-like channels and go-routines) as well as compiled class-support is here: http://numba.pydata.org/numba-doc/dev/roadmap.html

The project is open-source and contributions and contributors are welcome to join.


I also work for the company that produces Numba. We do have an issue tracker for Numba, if you're having problems then it might help to post your code. https://github.com/numba/numba/issues


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.


>"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."


On the flip side of this argument, code should be optimized for reading, not writing. Short, idiomatic code IMHO optimizes for reading. Code like

  array.map(&:to_s)
parses instantaneously. Whereas the procedural equivalent

  strings = []
  for (element in array)
    strings.push element.to_s
  end
requires scanning through the loop to extract out the logic specific to this case. And it's easy to accidentally overlook something when mentally ignoring the "boilerplate".

Not to make a mountain out of a molehill, and not commenting on Go specifically, but the extra "cost" of this style of code adds up when the language itself gets in the way of using high-level abstractions. Methods that are three meaningful lines long are inherently more understandable than equivalent methods that are twelve. Multiply this cost across an entire class, file, and/or project, and it can have a significant impact on one's ability to grok the expected behavior.


On the flip side, I had to scratch my head on what "&:" is supposed to mean. Or perhaps it's "&" ":". I know, I'll Google it </yeahright>.

More seriously, "for ..." maps into "map", "foldl", "foldl'", "foldr" and perhaps other constructs. They do pretty much the same thing, but I don't necessarily see a real cognitive advantage of either style.


stouset is obviously not arguing that the ruby version with "&:" is immediately obvious to any programmer. The point is if you know ruby, you will understand that line a lot quicker than the second example.


What is the "fold" variant in Ruby? Maps are trivial either way, but folds are a pita in my functional programming experience.


  [1, 2, 3, 4, 5].reduce(0, &:+) #=> 15
Which is short hand for:

  [1, 2, 3, 4, 5].reduce(0) { |memo, item| memo + item } #=> 15


With reduce you don't even need to use the & notation, it has that syntactic sugar built in, so you can just do this:

[1, 2, 3, 4, 5].reduce(:+)


Yes and no. In practice, you're going to fold into a different domain, which causes the types of the reduce function arguments to be different. Moreover, you are not going to have your reduce function conveniently in a library. You are going to have to write a loop body. At which point, choosing a block over a for loop becomes purely religious.


Different domain is fine.

    [1,2,3].reduce([], &:unshift) #=> [3,2,1]
I don't see a problem with writing a for loop, but I think it's not as composable and it can be a little harder to read. So I don't think it's purely religious but it's close to that.


Randomly searching for foldr on code.google.com

    inserts :: [(VarId,VarId)] -> Env -> Env
    inserts vvs env = foldr (\(a,b) acc -> M.insert a b acc) env vvs
or

    inserts(vvs, env) {
      for ((a, b) : vvs) {
        env = M.insert(a, b, env)
      }
      return env
    }
Pick your poison, but I'll stick with fors. Go blues!


If you want inscrutable Haskell you should go with the <$> operators :) I'd personally still rather have a

    vvs.reduce(env){|env, (a,b)| M.insert(a, b, env)}
but the for loop is perfectly readable also because it fits on a single line. I think for longer bodied reduces I might prefer a for loop actually.


It's bigger than that. You are separating the how from the what. Sure you could compute a map with a for loop. Or you could compute it in parallel across 1000 threads.

Not only that, map is composable. Something a for loop never will be.


For loops have been parallelized for quite a while. Having a dataflow analysis tell apart a "map" loop from a "fold" loop is quite trivial once you have the framework in place (aliasing not withstanding). Heck, Fortran is all about for loops and it's the language for parallel computations.

http://software.intel.com/en-us/articles/automatic-paralleli...


I am aware but it misses the point. Parallel computation is just one example. What about traversing trees? Graphs? Any functor? Any compiler directions to automagically make the for loop traverse those?

And that does not even address the issue of composability - which is even more important. Map fusion?


Bottom up tree traversal doesn't require an explicit for loop as long as the language supports first class functions.

    bottomsUp(node) {
      recurseChildren(node, &bottomsUp);
      switch (node.id) {
        case FOO:
          break;
        case BAR:
          break;
      }
    }
At the end of the day the actual code (the switch) is what matters, and the "glue" can be made a couple of lines either way.

PS. On a separate language feature, I like match with algebraic datatypes more than switch, but I don't know if Go has match. Go Rust!


The language is ruby.

    array.map(&:to_s)
eventually means

    array.map{|str| str.to_s }
As I understand it, the & means convert a variable to a code block. If the object already is a callable object (proc or lambda) ruby doesn't need to do anything to pass it through. Symbols (:to_s) aren't callable so ruby calls the to_proc method on it. The rails framework addd this for Ruby 1.8. The code is below.

    # File activesupport/lib/active_support/core_ext/symbol.rb, line 10
    def to_proc
      Proc.new { |*args| args.shift.__send__(self, *args) }
    end


I think a list comprehension is clearer than mapping some sort of lambda construct.


yeah in python it would be [str(x) for x in lst]


In more modern (idiomatic) Python you would use a generator expression so:

strList = (str(x) for x in lst)


Hi yawaramin, there's nothing less modern or idiomatic about list versus generator comprehensions in python. You would use a generator if you want to limit maximum memory usage, and/or if you know that the iterable only needs to be consumed once.


I've seen the for x in lst syntax before, but where should I start to learn about generators?


For list comprehensions, it's the same syntax, but in ()s rather than []s. A generator will be lazily evaluated (so until something tries to get a value out, nothing is computed) and can only be iterated through once. That's pretty much the only differences in that case.

There's more complex generator stuff you can do (ie. using the ``yield`` syntax inside functions), but unfortunately I don't know a good resource for that off the top of my head :).


The problem with redundant code is usually not editing, it's bugs. More code statistically corresponds to a higher number of bugs.


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.


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.)


> 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.


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

You totally miss the point. There are large classes of systems where CPU time in your app is not a significant factor. E.g. my go-to example of this is the first Ruby project I deployed in a production environment:

A queueing middleware server. If we served the queues entirely out of memory we could serve a few million messages a day out of 10% of a single 2005-era Xeon core (and while Ruby doesn't scale across cores, this system could easily spread load by simply running multiple queue processes).But of of that 10%, 90% was spent in kernel space. So even if switching to C (or whatever) were to speed up the user-space part of the code 90%, it would only cut CPU time spent from 10% to 9.1%.

This is fairly typical. If your code is CPU-bound, and it's not CPU bound because of stupid algorithm choices, then things can look very different. The first thing to do is find out if you're CPU or IO bound.


> You totally miss the point. There are large classes of systems where CPU time in your app is not a significant factor. E.g. my go-to example of this is the first Ruby project I deployed in a production environment:

This is only true if you ignore response latency as a comparative metric entirely.

> But of of that 10%, 90% was spent in kernel space. So even if switching to C (or whatever) were to speed up the user-space part of the code 90%, it would only cut CPU time spent from 10% to 9.1%.

I'm really curious what you were doing in Ruby that spent 90% of your runtime in the kernel. Having implemented similar middleware, I found that nearly all my runtime was spent just performing basic decoding/encoding of framed packet data, and as a kernel developer, I'm having a hard time imagining what an in memory queue broker could have been doing to incur that overhead


> This is only true if you ignore response latency as a comparative metric entirely.

Response latency makes scripting language unacceptable for some types of problems. But in my experience very few latency problems I've come across are down to language choice vs. badly written database queries, lack of caching etc., that'd still be unacceptable regardless of language. Basically, the moment a database is involved, that's where you're most likely to see your low latency going out the window.

Of course there are situations where language choice definitively matters in terms of latency. Despite using mostly Ruby now, there are certainly places where I'd use C. E.g. I'd never try to replace Sphinx search with pure Ruby code, for example.

> I'm really curious what you were doing in Ruby that spent 90% of your runtime in the kernel. Having implemented similar middleware, I found that nearly all my runtime was spent just performing basic decoding/encoding of framed packet data, and as a kernel developer, I'm having a hard time imagining what an in memory queue broker could have been doing to incur that overhead

The moment you find yourself "decoding/encoding framed packet data", you have lost if your goal is a really efficient message queue if said decoding/encoding consists of more than check a length field and possibly verify a magic boundary value.

If the userspace part of your queueing system does more than wait on sockets to be available and read/write large-ish blocks, you'll be slow.

Of course there are plenty of situations where you don't get to choose the protocol, and you're stuck doing things the slow way, and sometimes that may make a language like Ruby impractical.

But there are also plenty of situations where in-memory queues are also not acceptable, and the moment you hit disk, the entire performance profile shifts so dramatically that you suddenly often have much more flexibility.


> Response latency makes scripting language unacceptable for some types of problems. But in my experience very few latency problems I've come across are down to language choice vs. badly written database queries, lack of caching etc.

5 ms you save in your runtime -- for free, without any work on your part -- is 5 ms longer you can now afford to spend waiting on the database.

> But there are also plenty of situations where in-memory queues are also not acceptable, and the moment you hit disk, the entire performance profile shifts so dramatically that you suddenly often have much more flexibility.

I can only disagree here. I view CPU cycles and memory as a bank account. If you waste them on an inefficient runtime, you can't spend them elsewhere, and there are always places where they can be spent.

Excluding huge sections of the problem domain -- such as "decoding/encoding framed packet data" -- is a cop-out. There's no valid reason for it, unless you're going to go so far as to claim that Ruby/Python et al increase developer efficiency over other language's baselines so dramatically that it outweighs the gross inefficiency of the languages.


I think you missed the point. There are plenty of use cases where Python isn't the cause of slowness.

It doesn't matter if C is 100x faster than Python if your app is running 49/50 seconds running SQL queries.


No. But it DOES matter to me than Python restricts you to using it for those cases, whereas I would want (and could technically be able to) use it for far more stuff.


So you prematurely optimize Python out of everything you write, because it might not be suitable for... some use case yet to be determined?


No, I don't "prematurely optimize Python out of everything I write".

The exact opposite, I would like to be able to use Python for more stuff but I can't because it's slow.

Nothing about doing it "prematurely" here. I have tried it for specific problems and it was slow. If anything, having built in in Python first is the reverse of "prematurely". And I have, as many programmers have, a very good general idea of where it's slow and what it can be used adequately for without resorting to C.

And what I argued is that people constrain themselves unconsciously, based on that very knowledge, to the kind of projects they do with pure Python.

Nobody writes a AAA game in Python. Nobody tries to build a browser in Python. Nobody writes a performant Apache/Nginx class web-server in Python. Nobody writes a database engine in Python. Etc etc.

(Note for the "Vulcan"-style literal readers: by "nobody" here I mean "not many" / "not a sane person". I'm sure you can find some experimental, bug ridden, half-developed example of all of those done in Python, with maybe 100 users at most. Oh, and I specifically wrote: "pure python").


> Nobody writes a AAA game in Python. Nobody tries to build a browser in Python. Nobody writes a performant Apache/Nginx class web-server in Python. Nobody writes a database engine in Python. Etc etc.

Almost nobody does any of those things regularly. I've written perhaps two ISAPI modules (in C++), one Apache module (in C) and in none of those cases the code I wrote replaced the bottleneck. It was just that that was the "right" place to put the code. I've done performance analysis countless times (the first time was, indeed, to demonstrate moving off VB3's p-code and into Turbo Pascal's native code was going to have a negligible effect on the performance of a client-server application) and I've seen, over and over again, the bottleneck being the network and the database.

For the exceedingly rare case it's not, I agree, Python/Ruby/Perl/Smalltalk is not the best choice.


>Nobody writes a AAA game in Python.

No, but the scripting/UI/mod toolkit for the game might be in Python

>Nobody writes a performant Apache/Nginx class web-server in Python

No, but Python can do quite well serving WSGI to Nginx

>Nobody writes a database engine in Python

No, but Python ORMs work pretty well.

The point I'm trying to make is that I don't think that it's a bad thing that Python sometimes gets relegated to a "support" language.

I think the time of the single language programmer is over. If you need something faster than Python, use something else.


>The point I'm trying to make is that I don't think that it's a bad thing that Python sometimes gets relegated to a "support" language.

Of course it is bad. It would only be good if the inverse (being able to write the whole thing in Python and get the same speed) would be worse.

But it clearly is not -- hence, it's bad. It might be pragmatic and necessary compromise at the moment, but that doesn't make it "not bad".

>I think the time of the single language programmer is over. If you need something faster than Python, use something else.

On the contrary, multi language projects are a kludge born out of (pragmatic) necessity.

Nothing inherently inevitable (or good) about them.


> On the contrary, multi language projects are a kludge born out of (pragmatic) necessity.

> Nothing inherently inevitable (or good) about them.

Um, except that coherent planes/tiers of abstraction is the foundation of robust software engineering. You write languages to a spec; compiler authors emit bytecodes for another spec; hardware engineers optimize transistors and lay traces on silicon to meet yet another spec. Etc.

Multi-language is the only thing that makes the world work today. The closest single-language runtime that exists is FORTH and things like that.

Every other "modern" language where you get to pretend your process has a contiguous address space, or that there even exists a thing such as "process", is s pragmatic compromise between compiler and runtime technology.


>Um, except that coherent planes/tiers of abstraction is the foundation of robust software engineering. You write languages to a spec; compiler authors emit bytecodes for another spec; hardware engineers optimize transistors and lay traces on silicon to meet yet another spec. Etc.

Except that we talk about stuff that should be in the same level of abstraction in the first place. It's not about writing a compiler or OS in Python, or using Python for CPU microcode.

It's about writing the numerical routines in a scientific/statistics package in the same language that you write the core business logic, etc.


> Nobody writes a AAA game in Python. Nobody tries to build a browser in Python. Nobody writes a performant Apache/Nginx class web-server in Python. Nobody writes a database engine in Python. Etc etc.

But most developers do none of those things in the first place, regardless of language.


"So then I had to rewrite critical sections of the Python in C"

I think that's kinda the point; rewriting critical sections in C is pretty much Working As Intended. That's why it's a scripting language. The point is that you didn't have to write the whole thing in C.


>I think that's kinda the point; rewriting critical sections in C is pretty much Working As Intended. That's why it's a scripting language.

No, it's pretty much not "Working As Intended".

It's just "Working as we have been used to, because, well, what can you do?".

As V8, IonMonkey, Dart, Smalltalk VMs, LuaJIT, even PyPy etc have showed, nothing prevents Python from being an order of magnitude faster (and sometimes up to two), while remaining just as dynamic.

Rewriting parts of a Python program in C is not a technical necessity due to some (unsurmountable) theoretical limit. It's a technical necessity because of a non optimal interpreter implementation.


It isn't a necessity at all. It's an available option.

How would you write PyPy to be "an order of magnitude faster"?


Do you think that most Python developers can write safe C code? I doubt it but I have no data on it either.


A fairly moot point. Do you think most C developers can write safe C code? The 2 I've encountered in my short career basically have no idea what they're doing (which I suspect is partly why they stuck with C instead of exploring alternative languages).


If that were the case it would make the situation even worse. It means the whole "write performance critical parts in C" argument is bogus.


To answer your original question - no, but there's less C code than the equivalent C-only program, so you're still better off. Or if your argument is that C is a security nightmare (which you won't find much argument from me on), replace C/CPython with Java/Jython.


You could write performance critical parts in a safer language, though, such as Rust (disclaimer: I work on Rust). C isn't the only option here.


>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.


There are definitely limitations of the implementation of CPython itself, which do not need to be there if the language were willing to ratchet down the dynamism in certain cases. (Even without that, there are potentially implementation avenues that would have permitted better concurrency than the current codebase.)

However, implementation aside, I think it's invalid to assert that the existence of a cottage industry is inherently a sign of weakness; it's also a sign of just how pervasively Python is used. Find me another language that's used as widely, as popularly (i.e. not just two random guys using it that way), and in as many different kind of settings as Python. (Java might be a contender, except that it's not really used for the really high-performance scientific workloads like Python.)


This.

Integration is hell. This is a universal fact.

I'd rather pick a tool which doesn't add integration requirements!


Python doesn't add integration requirements. If you want to just use PyPy for something, then use it in good health (without fearing that your e-penis is tiny and will hamper your 'performance' because you didn't do whatever coldtea said is necessary for ULTIMATE SPEED).


Why the juvenile mockery about "e-penis" and "ULTIMATE SPEED" and such mocking? Are we 12 year old here?

I made my case explicitly, and it's about technical limitations of CPython that can (and, at some points WILL) be overcome. PyPy is a step in that direction, though not mature yet.

Python, in the form of the standard and most used implementation for scientific, technical etc programming, CPython, DOES add integration requirements.

It necessitates the use of C/Swig/Cython etc extensions for performance critical stuff. And it necessitates it, not because it puts a gun in your head or says so in some contract clause, but in the pragmatic way, that people find it's not fast enough for their needs in pure form.

That's why all the popular related Python projects are not written in pure Python, but in C/Fortran/et al (NumPy etc).


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.


>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.

Only if you're talking about the typical CRUD, website, etc program.

Why should we? People use Python for numerical analysis, scientific computing, etc. Why not let them be able to do that, while still writing in Python (instead of using libraries that delegate to C/Fortran/etc)?

And even a website can do intensive computation work. Why HAVE to write it in 2 languages?


The people who use python for numerical analysis use solutions like numpy, with which their programs spend most of their time executing optimised numeric routines.


If you can structure your program in such a way that all your inner loops are handled by BLAS/LAPACK/FFTW, then python works great.

The sad fact is you can't always do this. Then you are forced to drop down to C/C++/Fortran.

It would be great if you didn't always have to do this.


This is exactly what Numba is designed to help with. And it already works extremely well for such cases. Numba lets you have ease of use (un-typed containers) and speed (with typed-containers) in the same run-time and the same syntax. Here is are some IPython notebooks which illustrate:

https://www.wakari.io/nb/travis/Using_Numba https://www.wakari.io/nb/travis/numba

And the slides from PyCon Talk: https://speakerdeck.com/pyconslides/numba-a-dynamic-python-c...


Numba is great for numerical expressions, but I'd be surprised if it works for trickier bits.

For example, consider the problem here:

http://arxiv.org/abs/0903.1817

Ultimately I needed to run a math formula on some surfels and connect an edge in a graph if the formula came out True. Following that I had to prune a bunch of edges in the graph. In 2009 (when I did it), I had to drop down to c++. (Admittedly, this was also partly due to a lack of a good python graph lib.)

I'd be very surprised if Numba is helpful with something like that - the branching and complex data structures don't seem like a great fit for it.


The scientific community seems to be welcoming Julia open arms, given its performance improvements over Python, if you look at the rate code is being ported to such a young language.


Do you use Python for numerical analysis, scientific computing, etc.? If so, why are you saying "why not let them be able to do that"? If not, why are you holding forth on Python's unsuitability for these applications without knowing about the available tools?


>Do you use Python for numerical analysis, scientific computing, etc.? If so, why are you saying "why not let them be able to do that"?

1) Yes I do (occasionally).

2) Because it applies to everybody, not just me. You surely heard of the plural form of speaking before.

>If not, why are you holding forth on Python's unsuitability for these applications without knowing about the available tools?

I never said Python is "unsuitable for these applications".

On the contrary, I acknowledged that Python IS USED for these applications.

What I said is that the tools you mention (the ones that make it suitable for these applications) are written in other languages, which is kludgy. I'd rather be able to have them written (and modified etc) in Python too.

So, Python + NumPy + etc is suitable for these applications, but Python the interpreted language without external C/Fortran help is not.


if you're doing numerical analysis, you're doing it offline, not in real-time. And so again, bit-packing doesn't matter. If your space usage grows exponentially, that becomes a problem, but in that case the difference between programming languages is just a multiplier, and is dwarfed by your algorithmic problems.


Why use a hammer and a screwdriver when you can make due with only a hammer?


It's even worse than that.

Python and the faster extension languages used are the same kind of tool (well, obviously, both are languages).

So, let's compare them to two hammers.

The difference is Python is made from soft plastic, and while it is light, it only works for softer nails. For harder stuff you need a metallic hammer, which is heavier.

And the sad thing is, the Python-hammer can also be almost as hard as the other hammer while remaining light, it just needs some coating (faster interpreter innards).

So nothing (except besides the man-years of work needed on the interpreter) forbids you to have a hammer both light AND strong (== dynamic and expressive as Python WHILE many times faster).


You conveniently forgot a key bottleneck: the amount of RAM on your server. That's what determines how many concurrent sessions you can keep running before performance degrades and you need to add another server.


"Conveniently forgot" is a very rude thing to say. Even if you think someone left something out on purpose, your argumentation won't benefit from the usage if that phrase.


Sorry if it came out that way... that was not my intention. I just wanted to highlight a fairly large omission.


I haven't run into a situation where RAM was a limit I cared much about for many years. If you rely on hamstrung VPS's, perhaps, but RAM is cheap. The servers we buy at work rarely have less than 32GB these days, and most can take 384GB or more. A couple can take up to 768GB.

If anything, I work hard to put more stuff in RAM to avoid hitting disk, as even though we have SSD's in all our new servers, disk IO is still inevitably where we hit limits first, followed by CPU. Running out of memory is a rare event, and when it is it is consistently an indication of a bug.


"the amount of RAM [...] determines how many concurrent sessions you can keep"

I don't understand. Concurrent sessions? Do you mean open connections? Worker threads? What exactly?


Yeah, but as a programmer that's not really my domain, and anyway throwing more servers at the problem fixes it, and doesn't require much change in the architecture of most apps.

The maximum speed of the internet, the maximum speed of your database, the maximum speed of your programming language are all basically constants that you can't improve by throwing money at the problem.


That is just plain appalling.

As a programmer, available memory is absolutely your domain or else you're increasing hardware/VM costs for yourself or your employer needlessly. Then you've transferred the cost of increased hardware to host your app/site/whatever off to users.

I can see today bit pinching isn't fashionable as it was long ago when memory wasn't cheap (it's not really "cheap" today either since you're paying proportionally greater per VM than the actual cost), but when a basic tenet like efficiency is ignored because DB all you're doing is compounding the problem.

BTW. Most people already deal with the DB issues with aggressive caching and/or reverse proxy so that still leaves the core application.


At an hourly rate of $120, thinking 10 minutes about saving RAM costs my client $20. With that money he could've bought 1GB extra RAM. I'm sure 99.999% of the times the saved RAM, would be less than 1GB. It's a simple cost/benefit equation. If you can save more than 1GB by thinking 10 minutes about it, you're writing shitty code to begin with.

There are times when the benefits are greater, for example when your software is running a million instances, or you're working on a hardware-intensive game, but those are certainly not common case.


The cost of 1GB of extra RAM is $20 in a certain range, namely where your system fits fairly comfortably on a single machine.

Once you get past a certain level, though, the cost of the next 1GB isn't $20, it's $20 plus the cost of another computer plus the cost of exploiting multiple machines rather than just running on a single one.

Then it's $20/GB for a while again, then $20 plus the cost of adding another machine, and at a certain point you need to add the cost of dealing with the fact that performance isn't scaling linearly with amount of hardware any more.

That last bit might be a concern only in fairly rare cases. But the first, where you make the transition from needing one machine to get the job done to needing more than one, isn't so rare. And that can be a big, big cost.

(Very similar considerations apply to CPU time, of course. Typically more so.)


We can't afford hourly rates of $120 and RAM most definitely doesn't just cost $20 when you factor in the inevitable new VM/machine. RAM is critical for maintaining concurrent connections plus there are times you need to keep large datasets in memory to avoid hitting the database.

This casual disregard for resources would explain why a lot of startups run into infrastructure issues so quickly and settle on terrible solutions (or outright snake oil) to mitigate problems that shouldn't exist in the first place.

People need to start realizing that The Cloud is only a metaphor. Hardware isn't intangible and neither is their cost.

Servers don't grow on trees; they live on a rack and grow on electricity and NAND.


Yes, $20 for the memory, and $200 in time spent getting approval, another $200 to physically install it, because you 'obviously' can't just open up that server as there are procedures fr that kind of stuff, and then $2000 in time wasted by the users while they spent 6 months waiting for that one ram stick to get installed.

It's easy to think everyone has their acts together like facebook or google, but most companies i've dealt with have hardware upgrade processes measured in months or years, not hours or days. You absolutely have to take responsibility for your work as a programmer and make stuff run fast instead of labeling it somebody else's problem.


> It's easy to think everyone has their acts together like facebook or google

Well... For those companies, obviously, dynamic languages are not an option. ;-)


Most garbage collected languages can't handle all the memory available on modern systems. Hardware costs pale in comparison to revenue and the cost of programmers. (Unless your service is free)

In general if your server is making you money, you'll make more money improving the service than reducing the amount of RAM the service takes to run.

In summary, test the conversion rate of a page/feature/etc, not how much RAM it uses. If you're going to performance optimize, attach a profiler and take the easy wins, don't spend too much time on it unless it's a ridiculous amount of resources or your margins on razor thin.


"Hardware costs pale in comparison to revenue and the cost of programmers."

That's not a law of physics. The number of servers you need depends on the number of users you have. The functionality you have to build usually doesn't. So the more users you have, the less true your statement becomes.


"Most garbage collected languages can't handle all the memory available on modern systems."

I don't think that has anything to do with the languages. I think that has everything to do with the quality of the memory manager implementation, and there is at least one memory manager that does deliver in this respect, namely C4.


Most apps just don't have computation patterns where RAM usage could even be a problem; most apps are IO-bound in some way. The companies I've worked for have deployed new servers because of high load averages (in the unix-load sense), not because of RAM shortages.


That's only true because most companies admit defeat before trying: they hit the disk when serving. The big Internet companies (Google, Facebook, LiveJournal, hell, even Hacker News and Plenty of Fish) all serve out of RAM: they keep everything a user is likely to hit in main memory so that a request never needs to perform I/O other than network. In this situation you're absolutely RAM-constrained.

I remember trying to optimize some financial software a couple jobs ago and hitting a brick wall because that's the speed the disk rotates at. We ended up buying an iRAM (battery-backed RAM disk) and sticking the DB on it. You can get this a lot cheaper by avoiding the DB and using a RAM-based architecture if you're willing to sacrifice fault-tolerance under power outages (or if you have some other architectural solution for fault-tolerance, like writing to multiple computers).


It's not that they admit defeat, it's that they admit success. Yes, there are ten companies that are pushing hardware so hard that every bit counts again because if it didn't they'd be in danger of exhausting the earth's supply of elemental silicon. But everyone else can make a good living without going there.


This is terrible. It's your job to know how much memory you're using and not to use too much if you can help it. In many environments it's set at a very low value - desktop, mobile, client-side web apps, embedded. Even in an environment where RAM is cheap and you can scale it as you want... using too much RAM can cause paging issues, cache-miss issues, increase your server startup time, etc. All of these are real world issues that you, as a developer, are responsible for.


Well, you're talking about making easy things even easier (CRUD web apps connected to DBs; the inherent complexity is low). That's very important for horizontal technology expansion, i.e. 'easy IT everywhere'.

For vertical progress, i.e. making complex things possible, these micro-bottlenecks are important. These same DBs with their query processing and other domains with extremely high inherent complexity would greatly benefit from high-level languages yet still the most difficult parts have to be written in the already difficult C(++).


Is Go fast AND human friendly?


Some people certainly think so. "Fast" and "Friendly" are both relative concepts, and it's hard to get a definitive answer.


Is Go as fast as java and as friendly as python?


Neither. Less friendly ("language complexity") than Python, and less performant than Java (not in the same league).

Its just another trade off. Beyond the hype, it has interesting choices, and some very poor ones. (No algebraic datatypes, conservative exploitable will-break-your-program garbage collector, no tail recursion)

But the trade off between performance, safety and compile time is quite unique, and should be considered its main contribution to the language design space. Its just too bad it ignores every other contribution from the last thirty years. Like how to do a type system, or how to write a garbage collection that survives long run proccesses.


My experience with Go is that it is as friendly as python. Not a lot of tricks and was incredibly easy to pick up and understand. But FWIW, I don't think its as fast/mature as Java yet.


Yes.


Both of those are subjective.


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?


Isn't that a useless comparison? Chinese, Tamil and Estonian are all extremely human read/writeable, so much that children can write them. However, it's likely at least one of them is indecipherable to you.

Perhaps the metric you're more interested in is the difficulty in learning to read/write decent code in a particular language. Of course, this difficulty can vary based on what other languages the learner is already familiar with.


Chinese is often even indecipherable to the Chinese and because of this there have been many attempts to simplify it. There is a subset that people actually use and the rest is avoided.

This is a similar case with languages like C++, Ruby and Python. The languages are terrifyingly complex, but the subset that most people use is reasonably usable. If you stray outside this subset there be dragons.


I see your point about C++, but I don't think anything in Ruby or Python could be described as "terrifyingly complex". They give you plenty of rope to hang yourself in various ways, but that's a different issue than language complexity.


The libraries are quite huge, for blue collar developers.


some of the dsl's might be hard to decode


Depends on the human! (One end of the spectrum is a DSL customized to the concepts and patterns of domain experts.)


Exactly, if you want shit done you need to use Ruby/Python. If you want actual stuff done you use something else.


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.


"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."

I'm sympathetic to your point, but I don't think this is necessarily true. What adds the most value to a product is determined by economics. It has nothing to do with whether an app is doing basic crud or nlp wizardry underneath, just whether it does something people find useful. There are tons of basic crud apps out there making people millionaires.


There are tons of basic crud apps out there making people millionaires.

Name three.

Economics drives both demand and supply. CRUD apps are easy to make; the market for most CRUD-only apps is heavily saturated with competitors, so the differentiating value of your application is not likely to be your ability to perform CRUD operations. Put it this way - if you come up with a CRUD app that does something staggeringly valuable, expect strong competitors in days not years. Your chance to make millions is small.


I heard about this Facebook thing the other day. They seem to be doing all right for themselves.


Perhaps my point wasn't quite described as well as it could have been. What I meant to say was:

What distinguishes your product is not how it stores or presents the data but what value it derives or extracts from that data.

Most CRUD apps are just a form of lock in which provide no value past storing your stuff in one place. That covers pretty much the entire enterprise software space and most trendy startups these days.


If there really are business-critical parts of the app that are too slow a good developer will drop down to C (as noted by others). That doesn't negate the reasons for using Python for the rest of the app.

Your mentions of maintenance and reliability in the opening sentence are unfounded. What makes a Python app less so than the same app written in any other language? Surely it comes down to the developer. A well-factored app written in Python (with tests) is going to be far more maintainable than a big ball of mud written in C. The choice of language has little to do with it.


I didn't mention C :)

I wouldn't drop down to C ever at application level. There is virtually no need to do this these days without introducing more risks from a security and memory perspective and the inevitable situation of "integration hell". I'd rather throw it in a JVM than drop to C. Then again if I started with a JVM I wouldn't have the problem to start with as it solves both ends of the problem. The CLR is equally applicable here. Go hits the mark too.

From my experience here dealing with a massive 100kloc python project. Python maintenance:

a) Horrible to refactor. I mean really horrible. You don't get everything right the first time and when it comes to fixing that it's hell.

b) no type safety or contracts which is hell when working with other developers.

c) indentation issues which are totally horrible at merge time as it introduces a shit ton more work to do.

Reliability:

a) Some algorithms don't scale the same way as they do in other languages due to the points the article raises. It gets to the point that for something in Java is O(N), it can approach O(N^2) due to all the dict fudging and copying which is not good.

b) It's slow anyway - I mean really slow. If something feels slow, it is slow. PyPy may fix this but (c) below causes an issue as well.

c) Optimisations aren't consistent across python versions (consider how crappy IO was in the first py3k drop).

I wouldn't use Python for a new project now. The only utility I see is quick disposable bits of glue.


Thanks for elaborating and I hope you also realise I was just using C as an example of another language (a type-safe one). I think a lot of us have encountered 'big ball of mud C code-bases' in our careers which is what tends to spring to mind for me when anyone is discussing code maintainability.

>> Horrible to refactor. I mean really horrible. You don't get everything right the first time and when it comes to fixing that it's hell

I think dynamic languages like Python and Ruby are an absolute joy to refactor. There is generally less code-junk to be worrying about when making refactorings. I realise this is subjective however, I think it's difficult to make a serious case that any one language is easier to refactor than another. Granted, the refactoring tools in IDEs for Ruby/Python etc. are much more limited than say C#.

I hope you won't mind if I don't get into the indentation or type safety arguments. They've been done to death! :)

I think we're in agreement that if you need something to be really fast you should use a different language. I just don't think that's a reason to not use Python for the rest of your app. And of course as others have pointed out the vast majority of us are not writing apps that require super fast algorithms, we're all far too busy doing the same CRUD stuff over and over right :)

I appreciate your insight on a 100kloc Python project though, I have to admit I've not encountered anything of that size. At work we have a much smaller codebase that has already gotten out-of-hand but I believe that's entirely due to the developer responsible, not the language.


The problem with refactoring dynamic languages is that it's very hard for static code checking to let you know when you've missed a spot.

For example, what functions take class Foo that should now expect class Bar? What functions called method Baz, that now should call Bat?

Static typing makes all of that trivial. You know with certainty, when you've hit all the right spots, because the code won't compile otherwise. Also, a lot of that refactoring can be done automatically through tooling. "Replace all calls of Foo.Baz() with Bar.Bat()". Trivial, done... and it's often impossible to do the same thing in dynamic languages. You have to rely on tests catching everything... and how many of those tests need to be updated now, too? What's testing your tests?

I love dynamic languages, don't get me wrong... but refactoring large code based is way easier to get correct in statically typed languages.


If you need to touch a lot of places for refactoring on a regular basis, that should tell you you're not doing something optimally. If you write your tests so they are dependent on internal details of the other code, that's a warning sign too. Add to that, that at least in my experience, my Ruby code bases tends to end up far smaller than my old equivalent C code, and the problem tends to be a lot smaller than one might imagine.

Yes, you need testing discipline, but you need that with static languages too - if you think you're ok just because the compiler didn't complain.... Well, that's just a false sense of security.


My point entirely :)


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?


"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.


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-...


Yeah,

Perhaps if I saw/heard the talk I would get more out of it than "data-structure heuristics are the worst". It would be nice to know why they are the worst.


Python already has a feature called __slots__ which replaces the dictionary normally used to store object attributes with fixed, well, slots.


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


Yes. They call it "hidden classes": https://developers.google.com/v8/design#prop_access


Self-93, the language that pioneered this approach for dynamic native compilation, celebrates it 20th birthday this year.


So does PyPy, but only for objects (and not for hashmaps, i.e., the dict object).


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.


So does ruby Hash in 2.0 if I recall correctly.


Yes, here are someone's presentation slides on it. According to the slides, the cutoff in Ruby 2.0 is 6 elements.

http://lanyrd.com/2012/goruco/swfqq/


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.


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.)


Good summary. But I disagree with the author that immutable strings and ubiquitous hashes are merely unfortunate idioms of python - to me python is about simplicity, and those things are what make python conceptually simple. I don't have to worry about clobbered buffers, nor about datastructure semantics besides simple hashes.


The performance impact of the hashes is bad enough that people tweak designs in most unfortunate ways...

I think a case can be made that it is more important that the solution be simple and clean.


On the same site, the advantages of Go vs good dynamic language implementation like JavaScript V8 aren't so impressive:

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


Being 2-10x faster and using a lot less memory isn't impressive? I'm sure the Go optimizer still needs a lot of work. It's going to get much better. The V8 optimizer is great but we're probably closer to the limit of how much more can be done than with Go.


I think there's a bit of a disconnect here between people who work on applications where, say, a 25% reduction in CPU usage would be a big win, and people who don't worry about performance as long as their cluster isn't melting down.


Those results are from the x86-64 machine with one core (because the multi-core 64-bit machine is still running Go 1.0.3). Once Go 1.1 is realized and the multicore machine is updated, run the comparison again there. Javascript fairs even worse against Go when multiple cores are available. Gos support for parallel processing blows JS away.


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.


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.


How much time have you spent using CoffeeScript? It might take a week or two to get used to it. I think if you just stick with some basic stuff and write it like Python with less punctuation then it is pretty straightforward and CS classes are very straightforward, what's wrong with them?

Just leave out the outer parenthesis and put them in after that.


I've spent plenty of time with CoffeeScript, and I don't like it. It's not explicit enough, and the syntax is too permissive. It's easy to get bitten by things like omitting parentheses when calling a function, which creates hard-to-spot bugs. CoffeeScript also creates poorly performing code by default, by treating everything as an expression and returning it unless you tell it not to.


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.


I don't see Python as being cleaner than CoffeeScript. CS is very similar but with less punctuation. How do you figure that one?

Have you tried using CoffeeScript indented with two spaces, or using the async module? You don't always have to do continuation passing, and I feel like Node.js's way is a lot more straightforward than anything like Twisted.


> I don't see Python as being cleaner than CoffeeScript. CS is very similar but with less punctuation. How do you figure that one?

It's more semantics than syntax, although CS's syntax is sufficiently complex and nonredundant that the compiler sometimes reports syntax errors in confusing places, or worse, finds a way to interpret them as something I didn't want them to mean. But syntax can suck my dick. It's nothing more than a constant factor. It's more things like:

* No iteration protocol.

* The built-in dictionary type only allows string keys.

* The built-in dictionary type has magical keys like "constructor" by default.

* [0..4] includes 4. The correct thing, [0...4], has a longer spelling.

* [0...i] has i items, except when i is negative, in which case it has -i items. (To be fair, Python has a similarly screwed-up special case in its array indexing.)

* No built-in lexicographical comparison of arbitrary sequences.

* No comparison protocol.

* String conversion of aggregates is brain-dead. '' + [0...4] == '' + [[0...4]].

* It inherits JS's incorrect closure of this; { x: (=> (=> @top)), top: 5 }.x()() isn't 5.

> using the async module? You don't always have to do continuation passing

I haven't tried using the async module, but you got my hopes up. No dice, though. All of its functions take a continuation as their last argument!

There's a lot to be said for straightforwardness, but Twisted's non-straightforwardness justifies itself by nuking boilerplate.


>I don't see Python as being cleaner than CoffeeScript. CS is very similar but with less punctuation. How do you figure that one?

Python's syntax is less confusing and less likely to introduce subtle bugs. I've been hurt enough times by CoffeeScript's ambiguous syntax.

>You don't always have to do continuation passing, and I feel like Node.js's way is a lot more straightforward than anything like Twisted.

I like Twisted's way because it is object-oriented and well-structured. It doesn't lend itself to spaghetti code, unlike node.js.


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.


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.


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?


Why specifically are these "literally the worst" options?


Because you can hire for those languages, but good luck hiring for CL.


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.


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.


But all the others are stupid, incompetent programmers. It started at MIT.

Just kidding! (sort of). But have a look at the Jargon Appendix (http://catb.org/jargon/html/pt03.html) - especially The Story of Mel (http://catb.org/jargon/html/story-of-mel.html).


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.


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.


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

At YouTube (which is a predominantly Python shop) we use Go for some elements of our infrastructure, and I spend approximately half of my time working in each language (main project in Go, integration tests in Python). In my experience easy things (say, split and massage some strings) are more complicated/annoying in Go than in Python (and much more verbose). However, difficult things usually end up being much simpler in Go than in Python – especially if they involve concurrency (Python threading is a trainwreck). The difficult parts of our code consume most of our brainpower, so that's a net gain from our point of view. What is more, for some weird reason Go code seems to be less susceptible to bitrot (go fmt FTW).


Even string splitting in Go isn't that bad, frankly.

    words := strings.Split(wordList, " ")
    foo := words[0]
    if len(words) > 1 {
      bar := words[1]
    }
Where things can get a bit tedious is data conversion from byte buffers, like reading in data from disk or Redis or something. But that's no different from any other statically-typed language.

I would like to see more libraries in Go, though. Much of what's complex is there, but one of the benefits of languages like Python and Ruby is there are lots of libraries for the easy stuff, too. Having to write a data model validation library yourself is boring.


I see this as a devtime-runtime tradeoff, If you follow the link to the mentioned article [1] there is some discussion about how to improve on this tradeoff.

FWIW: I use C for many of my personal projects as I often enjoy solving problems at that level, however at my job I am 90% Perl with the other 10% being various web things (css, js, html, etc.).

I too would be very interested in articles looking at performance of some of the older dynamic languages.

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


Well, partly. The argument re scripting language + C is that mostly, the run time is in a small part of the code. Measure to find that part (NYTProf etc for Perl), then rewrite it in C. (I.e. inner loops, not reading config files.) The inefficiency of scripting languages is then irrelevant for many use cases which don't even involve databases.

My experiences of using Lisp compilers is very much out of date, I never saw any Lisp jobs I wanted after university.

Edit: Clarity


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

"Writing critical sections in C" is a terrible cop-out of the Python community, by which they try to make the engineer do the compiler/VM's job.

Compare:

1) Write algorithmically efficient code.

2) Profile for hotspots.

3) Lift out the hotspots by rewriting them in C.

4) Repeat throughout the lifetime of your project.

Versus:

1) Write algorithmically efficient code.

2) There's no step two.

What winds up happening is that A) nobody does steps 1-4 with any regularity, and B) The bar at which a C rewrite is set very high, while Python is very slow across the board. The whole system winds up slow in aggregate, with only a few hotspots optimized, if any.


"But he didn't touch the important question -- is the development time for Go faster than for scripting languages + C?"

That is a good question, but- Isn't that going to vary a lot by the person/team involved? Calculating "effort" is far more difficult than run-time or complexity characteristics.

Doesn't that question also assume that people writing in the scripting language can write (correct/decent/secure) C. Ironically, I have a strong feeling that many more Go developers can write C than say Ruby or PHP devs.


>>Calculating "effort" is far more difficult than run-time or complexity characteristics.

If it really was impossible to measure development/support speed, then we'd still use hex code with switches on a console. :-)

And yeah, kids today often don't learn C at school, it is a pity.


"If it really was impossible to measure development/support speed, then we'd still use hex code with switches on a console. :-)"

Of course it can be quantified. Some people use gzipped source code size to approximate this. I just meant that this is more subjective, has more variables and sometimes must be decided per organization or team.

"And yeah, kids today often don't learn C at school, it is a pity."

Most C-S programs I have interacted with still teach C, but yes that is a problem. I was more thinking of the "too cool for school" portion of the startup crowd, those who learn only learn scripting languages, etc


Actually I think it is even worse than that.

I would say "kids today often don't learn about compiler design and computer language's history".

Given my CS background, it baffles me how languages get mixed with implementations or language runtimes with VMs in current discussions.

I started coding in the mid-80s, we lacked the information access kids have nowadays with Internet, and yet most of us were quite aware what language design was all about.


Yeah, I've been enjoying using Clojure for this reason. The strengths of the JVM and Java libraries with the development time and functional style of a Lisp.


The crop of interesting languages on top of the JVM is intriguing. My Java experience is very much out-of-date, so I haven't looked at those much.


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.


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.


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?



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.


Numba is trying to let you make a different choice for different sections of the same program so that you can do different styles of programming in code meant to be a library than you might do in other sections of code meant to be more "ocassional programmer" facing.

Some of you might find the roadmap particularly interesting. It's an open-source project eager for input from others...

http://numba.pydata.org/numba-doc/dev/roadmap.html


The video has been posted for anyone interested:

http://vimeo.com/61044810


Wouldn't adding goland-style slices to Python be awesome?


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.


Agreed. I think numpy arrays a rather overlooked by most python programmers. They allow all kinds of useful tricks like that that are quite handy even if you're not doing strictly numeric work.


well, there are memoryviews, but they could have been designed better


Memoryviews are the step-child of NumPy arrays. The buffer-protocol was the real intent and the memory view the forgotten "example" until a few brave and noble Python devs rescued it from obscurity. Memory-views are not that useful to one who will always have NumPy installed.


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.


Does the phrase "scripting language" have any hard technical meaning or is it just another subjective label?


I've always informally thought of a scripting language as anything the executes by interpreting source code or bytecode (lacking JIT compilation).


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.


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.


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.


No, the primary optimization asm.js brings (at least in OdinMonkey) is AOT compilation.

But it can do that precisely because of the static typing, which loops back to exactly what I said. Looser, more dynamic language always have a cost associated with that flexibility.


Arbitrary JS can be as statically typed as asm.js is (because, well, asm.js is just a subset). There's no reason why one couldn't compile AOT something like:

  function add_and_double(a, b) {
    a = a | 0;
    b = b | 0;
    var sum = (a + b) | 0;
    return (sum + sum) | 0;
  }
Yes, it'd be trivial to fall off the AOT-compilation path (esp. without a defined subset that got AOT compilation and hence likely would differ between implementations), so static verification that you're on the AOT-compilation path is valuable (which asm.js provides), but it doesn't fundamentally alter the semantics of the language.


You seem to be saying that the AOT path requires constrained typing and a strict subset of the language semantics, which agrees with the parent's point that static type semantics matter in terms of performance.


They matter, yes. But they aren't in any way specific to asm.js — it was something that was already true in generic JS.


But it is extremely specific to asm.js -- the optimizations possible can only happen because static typing is mandated. It is absolutely a subset of JS, but JS can't make those optimizations because the guarantees aren't there.


> 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.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: