7 ms·
Only fast languages are interesting
- maximusprime 15y agoOr you could just use java... The title says only fast languages are interesting, then rules out using Java because it's not lispy enough :/ Either you want speed, or you want your favorite syntax and high level stuff. You can't have both. FWIW My favorite syntax happens to be c/java/assembly type stuff. I'd hate to be one of the developers who hates those, must put you at a big disadvantage - as shown in the original post.
- Maro 15y agoExcept if you're like 99% of engineers whose favorite syntax is C/C++-ish and are happily using C, C++, C#, Java or maybe Python.
- deleted 15y ago[deleted]
- JoachimSchipper 15y agoWhy not? Apparently lush gets it right: C-like speed on numerics with a Lisp-ish syntax this guy likes. Yes, C-style stacks are going to beat Lisp-style closures; but there is no reason why a "fancy" language can't do fast matrix operations, which is 99.9% of numerical code anyway. Even Scheme has (rather un-Lisp-ish) vectors in addition to the standard linked lists.
- deleted 15y ago[deleted]
- nemoniac 15y ago> C-style stacks are going to beat Lisp-style closures It has been a long time since the "λ the ultimate..." series of papers gave the lie to this myth.
- rbanffy 15y agoI once made the joke that a Java web app usually catches up with a Django one in about six months. The Java app is faster, but, the Django one stars serving users three months before the Java enters live beta. And if you are serious about number crushing, my friends tell me you should use FORTRAN, not Java.
- eric_t 15y agoHere's the summation problem in Fortran, quite succinct, IMO: real, dimension(30000000) :: a call random_number(a) print *, sum(a) That's 30 million doubles, takes 0.04s on my machine.
- kaffiene 15y agoWhich is how long the Java solution took on my machine. Which can't be right, because Java is wrong whatever the question is.
- kaffiene 15y agoIf you write a Java app using the old Java2EE approach, perhaps. If you write one using Play or something equally agile, not so much.
- shadowfox 15y agoProbably should have added "for large-scale numeric computations" to the title :P
- jwr 15y agoYes, but then the flamebait/trolling factor would be gone. The title is such obvious flamebait that I hesitated whether to flag it.
- exDM69 15y agoI can relate to this. Nothing is more disappointing than putting together a nice piece of code and then seeing it run too slow to be practical. A while ago, I wrote some physics simulation in Python. The code turned out to be really neat and quite idiomatic Python code. The code ran very slow, I was seeing only 10-20 frames per second when I was shooting for something closer to 120 frames per second. All I was doing was simulating a cube suspended on a plane with four dampened springs, and I was only simulating one of those where I wanted lots of 'em. The problem was that my code was allocating and freeing way too many objects. The proper solution would have been to go from a neat idiomatic approach where the code resembles the mathematical equations it simulates to some kind of structure of arrays -style code with Numpy. Needless to say, there isn't much fun in using Python that way, so I might as well write the damn thing in C. Later I went on to rewrite that code in C and running it on my GPU with OpenCL. I did write it in a structure of arrays style, so maybe the Python experience gave me a valuable lesson. Now it runs fast enough for my purposes.
- maximusprime 15y ago> nice piece of code Too many developers are paying way too much attention to the code being "nice" or it being in a "nice" language, and not enough attention to "does it work" "is it fast".
- exDM69 15y agoThe problem is that when working with prototype code, you want to be able to understand what's going on. Code in structure of arrays style is harder to read. It would be nice that it would run with at least some kind of performance that's even close to what you need in the end. We definitely need "nice" languages, because they give a huge productivity booster over writing stuff in C or other low level language. However, many nice languages tend to be implemented using an interpreter, a crappy garbage collector and a giant lock to prevent anything with threads. This makes them unwieldy for applications where latency or throughput is important. Recently the trend has been towards languages implemented with a compiler, using smart static typing disciplines and an LLVM-based backend. This is a very positive and welcome change.
- _delirium 15y agoLanguages where the upgrade path to fastness isn't horrible are okay with me also. In Common Lisp, for example, you can start adding in type declarations in speed-critical areas, which Clojure also has something similar to (not as familiar with it, so not sure how it compares to CL's type declarations).
- wtracy 15y agoThis guy should look at Scala: Functional, runs on the JVM, and has performance comparable to "normal" Java code.
- darklajid 15y agoYou mean like this guy? https://news.ycombinator.com/item?id=3292555 https://news.ycombinator.com/item?id=3292555 Scala vs. Clojure is not something to discuss for this use case, I guess. As https://news.ycombinator.com/item?id=3293261 https://news.ycombinator.com/item?id=3293261 points out in that other thread, you won't write (recognizable) Scala (or Java. Or Clojure) code anymore if you want the best performance.
- stewbrew 15y agoWith respect to the second link: It always helps to know your data structures & their purposes--no matter what language you use, recognizable or not. With respect to performance: you might want to try writing a c-like solution and run the code distributed over several machines.
- skew 15y agoAll the clojure examples test linked lists! The first example lets lazy evaluation of the random numbers leak into the timing, toss a (dorun tmp) before the timing for a little more sense. The type annotation ^doubles is useless - it's telling Clojure to expect the definition to get an array of doubles, but then binds it to the same old linked lists.
- rbanffy 15y agoI was going to point this out. As always, if you aim for high performance, you should know your data structures. And use the right ones.
- bretthoerner 15y agoCan you fix his code and paste it? Why not have him run the bench and post it as an update?
- rjn945 15y agoBased on the work of a previous commenter, I posted this code in the blog comments: (defn add-rands [] (let [ds (double-array 30000000)] (dotimes [i 30000000] (aset ds i (Math/random))) (time (areduce ds i res 0.0 (+ res (aget ds i)))))) This adds 30,000,000 numbers in 73ms on my machine. His Lush code added 30,000,000 in 180ms. I estimate my computer is twice as fast as his, putting them on par. Hopefully he will run the code so we can see all the run times on the same machine. (I never could get his Lush code running.) Of course, the Clojure code here is fairly involved to do some basic stuff, but if you did things like this often it would not be hard to add some nice syntactic sugar over it.
- mbq 15y agoOne more benchmark for Clojure fans: R> system.time(sum(runif(3000000))) user system elapsed 0.100 0.000 0.101 R> system.time(sum(runif(30000000))) user system elapsed 1.037 0.020 1.058 not as good as lush, yet human syntax and Scheme scoping can compensate.
- mjwalshe 15y agoRemeber kids "real" programmers use FORTRAN
- eric_t 15y agoreal, dimension(30000000) :: a call random_number(a) print *, sum(a) That's 30 million doubles, takes 0.04s on my machine.
- fpgeek 15y agoTo me, this reads like someone who used C to add a large list of numbers recursively and concluded C was trash because they ran out of stack. There's no denying there is a specific problem. People are certainly entitled to decide that solving that problem is too much hassle for them. But how seriously should we take their opinions when they do that? I'd say it depends on the quality of the effort they did invest. In this case, the OP admits that they used the wrong data structure in Clojure and the right one in Lush, which makes the entire exercise meaningless.
- anonymous 15y agohttp://pastebin.com/2fGefex4 http://pastebin.com/2fGefex4 $ gcc -o addalot --std=c99 -Os addalot.c $ ./addalot [1500117.315354] 0.000032 per iteration on average. Works fine for me.
- dchest 15y agoThis is not the correct C program, this is a C program for the compiler with tail call optimization (or with the stack large enough for it to work). There's no TCO in the C standard.
- yvdriess 15y ago> There's no TCO in the C standard. That doesn't make tail-recursive C programs any less correct.
- _delirium 15y agoThey're not incorrect, but that's mostly because the C standard also allows you to assume an infinite stack, so recursive programs that ask for absurd stack depths don't violate the standard with or without TCO. But if in practice the program only runs successfully when compiled on a compiler with TCO, it's not very portable, in that it relies on non-guaranteed compiler features for it to actually work.
- stonemetal 15y ago
- luriel 15y agoHere is another place where Go hits the sweet spot between performance and expressiveness/simplicity. Also by giving you control over how memory is laid out you can be much more efficient both in space and time, and when you need to go even faster you can optimize things quite tightly.
- swah 15y agoBut there are no fast languages, only fast implementations...
- skew 15y ago... and benchmarks that compare linked lists to ontiguous vectors
- derleth 15y agoA language can't be fast; a language is a notation. Only implementations can be fast or, perhaps, generate fast code. For an example, look at GHC versus some toy-Haskell whipped up in Common Lisp. Now, with those two implementations to hand, is Haskell 'fast' or 'slow'?
- exDM69 15y agoMore practical example: CPython vs. PyPy. Also applies to CPython and CRuby vs. their JVM and CLR counterparts like Jython, IronPython, JRuby, etc. However, a language will impose some limitations on the implementation, be it a compiler or an interpreter. Dynamic typing is perhaps the most concrete example of this.
- derleth 15y agoThe idea of a 'fast language' is still nonsense, given how much faster the best interpreters of dynamic languages are compared to the code generated by the worst compilers of static languages.
- dan00 15y ago"A language can't be fast; a language is a notation. Only implementations can be fast or, perhaps, generate fast code." That's not entirely true. A language semantic limits the possible implementations. And yes, I know about the sufficiently smart compiler. ;)
- 1amzave 15y agoThis is true, and I'm often peeved by exactly the same conflation. However, in cases like Clojure, where there's (as far as I know) really only a single implementation out there to be used, it's easy to blur the lines. Python is getting less so thanks to PyPy, though my perception of that is that thus far it's more on an "awareness" level than it actually being in widespread production use (though hopefully as it matures we'll see more of the latter). The languages mentioned in this blog post present an interesting sort of in-between case though, since at some level you could almost regard them as being a collection of implementations of the same language (Lisp), rather than as actually distinct languages. I realize that's stretching some definitions a bit, but I think there's kind of a continuous spectrum there between "different language" and "different implementation of the same language". I mean, GCC implements extensions to C that aren't in the C standard and aren't (to my knowledge) supported in, say, MSVC; some refer to this as "GNU C", though I'd guess most non-language-lawyers looking at it would probably still call it "C" anyway. On the whole though, I agree with you.
- teyc 15y agoGuy Steele said it better though. He wants not the fastest language, but the fastest way to get to the solution. It may mean that the language isn't as fast to run, but faster to program.
- spacemanaki 15y agoThe title is link bait to the point of nearly trolling. He didn't do his research, he should have jumped on the Google group or Stack overflow or something before writing this. Someone named Mike in the comments on the OP comes up with a fix that uses Java primitives and that is very fast, in the range of his Lush examples. http://scottlocklin.wordpress.com/2011/11/30/only-fast-languages-are-interesting/#comment-2529 http://scottlocklin.wordpress.com/2011/11/30/only-fast-langu... There's almost nothing to see here, except maybe that there should be better documentation on this kind of optimization.
- tokipin 15y agoMathematica In[1]:= AbsoluteTiming[ Nest[RandomReal[] + # &, 0, 3000000]] Out[1]= {0.3276005, 1.50067*10^6} In[2]:= AbsoluteTiming[ Nest[RandomReal[] + # &, 0, 30000000]] Out[2]= {2.9952052, 1.5001*10^7} Not bad I think.
- jwr 15y agoThis is flamebait, especially the headline. There are various tools you can use, and using a screwdriver instead of a hammer won't help you much if what you have is nails. From my point of view, you may get back to me when Lush has data structures for concurrent programming and does (nearly) pauseless GC on multicore machines with heaps of 12GB composed of complex data structures, not just vectors of numbers. This is what I get with Clojure and for my applications it works great. Oh, we also do numerics, quite a bit in fact. But whenever I need actual raw-metal double-adding performance, I use an appropriate tool, e.g. an extension/library. For certain string operations we even went down to x86-64 assembly, with great results. What you describe is a JVM limitation, not a Clojure limitation — and you're right in only one thing: Clojure is not the right tool for you.
- ajross 15y agoDigression here, but I like the "(nearly)". This bit always amuses me about garbage collection wonks. The pauseless bit is a real time requirement. Saying your GC has great latencies or is "(nearly) pauseless" is tantamount to telling a real time engineer your system only fails some of the time. It makes you look dumb. GC is great. GC makes a ton of things simpler. GC as implemented in popular environments still sucks for real time use.
- Confusion 15y agoThe 'pauseless' bit is not just a real-time requirement. A website that stalls for 2 seconds once every 10 minutes is unacceptable to me. That application thus requires a garbage collector that is nearly pauseless: the pauses are small enough that your users won't notice them. So I don't think it makes anyone look dumb at all to speak of a 'nearly pauseless' garbage collector.
- carsongross 15y agoInteresting that the people who complain about performance are almost always in, ahem, 'quantitative finance'? The LMAX stuff, this guy... I'm just a simple caveman, but I can't help but think the world wouldn't be dramatically worse off if the HFT bots ran a touch slower.
- mhansen 15y agoVery well-written, but it's a non-story. Check the comments. Mike has contributed a clojure solution using `double-array` that runs fast, without blowing up the heap with a linked list.
- erikb 15y agoLearn to be more calm and patient. Maybe you need to think in another way, if you use another language and another VM. Maybe this language or VM really isn't strong for what you want to do, but for what other tasks. Also you might stop thinking in absoluta. There are no fast languages. Every good and well known language has their strengths and their weaknesses and all optimizations are a tradeoff. If you get stronger in one thing you MUST get worse something else. So if you have a fixed set of problems you really just need to find out which language solves your problemspace best. And if you find it, that doesn't say this language is better then the others.
- phzbOx 15y agoNot sure what he means by "interesting". I'm often delighted to learn new languages.. and even ones that are only on paper.
- weavejester 15y agoThere are a few things wrong with this benchmark. In Lush, he's measuring the time it takes to sum 3 million pre-computed random numbers. In Clojure, he's measuring the time it takes to generate 3 million random numbers, and then sum them. It also looks like he's using two different data structures. In Clojure, its a lazily generated linked list of objects; in Lush, it appears as if he's using a vector pre-initiated to the right size. He also doesn't mention which versions of Clojure and Lush he's using, or how much memory Lush uses. He complains that a JVM with a 130M is too small, but the 30 million long array in Lush would have been almost twice that size if it was populated with 64-bit doubles. For reference, it takes me 571ms to run the 300 thousand number example that took him 861ms. If I factor out the random-number generation from the Clojure benchmark, it cuts the time down to 120ms. If then I use a pre-generated fast array and the specialized areduce function (to match his specialized idx-sum function), I get 16ms for 300k, 22ms for 3m and 230ms for 30m. So performance is on par with Lush, so long as you compare like for like.
- lutorm 15y ago"The point is, Lush has datatypes for fast numerics: it’s designed to do fast numerics. Clojure doesn’t have such datatypes..." He knows he's not comparing like for like. What he apparently doesn't know is that there is a better data type.
- Nrsolis 15y agoWhats the better datatype? I'm looking at doing some numeric programming and I wondered about Clojure for this.
- weavejester 15y agoIf your aim is raw speed, not a lot beats a standard JVM array.
- zanst 15y agoSo I think you should stop with clojure and start to code in asm.
- lloeki 15y agoOh god, you know what's just as bad as a slow language? That Wordpress iPad theme. Please stop using that, it's ridiculously unusable.
- InclinedPlane 15y agoSynthetic benchmarks are rarely useful. If you want to benchmark two languages you need to do this: Find N coders skilled in language X and N coders equally skilled in language Y. Have all of them code several small but realistic systems of various kinds, then benchmark the results and compare. Better yet, benchmark real, live systems of comparable purpose built using different languages. Anything else leaves you open to having your results dominated by differences in level of coding skill, quirks specific to your synthetic benchmark, and effects that are only prominent for tiny, overly simplistic programs.