What's Going On (on "Why Python, Ruby, and Javascript are Slow")
jmoiron.net
jmoiron.net
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.
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.
I think every vim, emacs and decent IDE user would say "I has a text editor. Your argument is invalid."
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.
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.
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 [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[1, 2, 3, 4, 5].reduce(:+)
[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. 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! 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.Not only that, map is composable. Something a for loop never will be.
http://software.intel.com/en-us/articles/automatic-paralleli...
And that does not even address the issue of composability - which is even more important. Map fusion?
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!
strList = (str(x) for x in lst)
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 :).
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, 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.
Anyone know of a human read/write-ability comparison of programming languages?
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.
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.
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.
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.
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.
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.
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.
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.
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.)
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.
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.
Well... For those companies, obviously, dynamic languages are not an option. ;-)
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).
I don't understand. Concurrent sessions? Do you mean open connections? Worker threads? What exactly?
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.
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.
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(++).
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 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.
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...
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.
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).
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.
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.
It doesn't matter if C is 100x faster than Python if your app is running 49/50 seconds running SQL queries.
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").
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.
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.
> 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.
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.
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.
But most developers do none of those things in the first place, regardless of language.
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.
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.
How would you write PyPy to be "an order of magnitude faster"?
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.
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
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.
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.
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.
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 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.
>> 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.
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.
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.
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.
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.
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.
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.
Integration is hell. This is a universal fact.
I'd rather pick a tool which doesn't add integration requirements!
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).
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.)
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?
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.
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-...
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.
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.
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.)
I think a case can be made that it is more important that the solution be simple and clean.
http://benchmarksgame.alioth.debian.org/u64/benchmark.php?te...
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.
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.
Just leave out the outer parenthesis and put them in after that.
* 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.
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.
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.
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.
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.
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.
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).
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 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.
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.
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.
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
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.
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-...
My experiences of using Lisp compilers is very much out of date, I never saw any Lisp jobs I wanted after university.
Edit: Clarity
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).
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.
"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.
The way Eclipse is coded is not a good example of GC performance.
Slide deck: https://speakerdeck.com/pyconslides/numba-a-dynamic-python-c...
IPython notebook examples: https://www.wakari.io/nb/travis/numba https://www.wakari.io/nb/travis/Using_Numba
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.
Some of you might find the roadmap particularly interesting. It's an open-source project eager for input from others...
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.
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.
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.
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.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.