News for hackers. Stuff that matters.
Re:On Distributing Command Line Applications: Why I Switched... by izietto on Monday July 22, 2013 @09:40AM (#6083530)
Why don't compile Ruby using f.e. LLVM?
Re:On Distributing Command Line Applications: Why I Switched... by TylerE on Monday July 22, 2013 @10:00AM (#6083635)
Because it's kinda pointless. The thing that makes Ruby (very) slow isn't the runtime (there might be at most a 1.5x - 2x speedup there), it's the language design. All that dynamism and runtime reflection is really a performance killer.
Re:On Distributing Command Line Applications: Why I Switched... by vidarh on Monday July 22, 2013 @11:27AM (#6084295)
The dynamism and runtime reflection can be a performance killer. It only is a performance killer in most cases because the Ruby runtimes are still extremely simple as dynamic language implementations go.

Method dispatch in Ruby, for example, can for the typical case (where nobody redefines random methods on all your classes on a regular basis once initial startup has been done) be little more expensive than a C++ virtual member function dispatch, but this requires work to either cache (and invalidate) method lookups, or pre-building vtables (and waste a bit of memory as well as being prepared to propagate updates to them if methods are redefined) for known methods.

Inlining of code is also possible, for example, but requires more work to add "guards" to protect against less common patterns that are still valid.

99% of Ruby is reasonably simple to optimise well. The problem is that the 1% can change the whole world for you and force you to either be able to selectively detect what changes are made or throw out all your optimisations), and so to get fast Ruby implementations will either need to ask for guarantees from the developer (pragma's to promise you'll never alter Fixnum, even in eval() blocks, or flags to by default freeze all standard classes, for example), or they'll need to start employing all the dirty tricks in the book.

Thankfully, the Smalltalk, Lisp, Self and Javascript worlds, amongst others, have pioneered plenty of dirty tricks that'll do just fine (e.g. consider Polymorphic Inline Caches, pioneered in Self, I believe, that are now quite widely applied for dynamic languages)

I think we'll eventually head towards a bit of both: Optional ways of making the job easier, such as promising not to much with things we normally don't muck with anyway, combined with new meta programming libraries to remove the reasons for a lot of the eval() usage, that will allow implementations to make much better judgements about what can actually change (instead of assuming all bets are off after an eval()), and a large number of dirty tricks (in the form of caches + heuristics + guards in various variations, to be able to take fast paths most of the time and "just" fall back to slow code for pathological cases).

Re:On Distributing Command Line Applications: Why I Switched... by ef4 on Monday July 22, 2013 @10:09AM (#6083706)
The article isn't about performance difference, it's about ease of distribution.
Re:On Distributing Command Line Applications: Why I Switched... by gngeal on Monday July 22, 2013 @10:06AM (#6083678)
it's the language design. All that dynamism and runtime reflection is really a performance killer.

So why are Lisp and Smalltalk, which are equally dynamic and reflective, even faster?

Re:On Distributing Command Line Applications: Why I Switched... by greiskul on Monday July 22, 2013 @10:54AM (#6084015)
Ruby does have support for callcc, which most lisps, specially the fast ones like Common Lisp, don't, since it is a pain to implement a fast one without implications in your whole language. Can't comment on smalltalk, since I never programmed in it.
Re:On Distributing Command Line Applications: Why I Switched... by vidarh on Monday July 22, 2013 @11:07AM (#6084131)
But nobody uses call/cc in Ruby in practice. The issue with Ruby performance is immature VM's/compilers.

There are tons of performance killers in Ruby, but most of them can be "worked around" by making assumptions about the common case and "just" spend some cycles verifying when the assumptions are violated.

E.g. 99.99% of the time, noone are stupid enough to redefine integer addition (because, besides the fact there aren't many good reasons to, all kinds of hell breaks loose if you do, for obvious reasons), so you can usually check the type tag on integers and inline the addition.

The problem is that edge case. But the edge case can be detected when someone tries to redefine '+' and you'd still save massive amounts of time if you were to set a flag at that point and still inline the additions, just with a test and branch to a slow path if people does something stupid (or in the case of JIT's, "just" re-jit everything at that point - yes, then overriding '+' will be slow, but common code will be much faster).

Re:On Distributing Command Line Applications: Why I Switched... by gngeal on Monday July 22, 2013 @02:19PM (#6085488)
Ruby does have support for callcc, which most lisps, specially the fast ones like Common Lisp, don't, since it is a pain to implement a fast one without implications in your whole language.

Gambit-C is one of the fastest Lisp implementations around, and it does have a full and performant implementations of call/cc. And I'm afraid that Common Lisp happens to be "fast by sccident" rather than "fast by design" - there's probably more extra speed to be gained by having a better implementable standard (which CL isn't, or at least not that much) than by omitting call/cc.

(Also note that call/cc is sort of out of favour these days, delimited continuations is where it's at.)

Re:On Distributing Command Line Applications: Why I Switched... by lispm on Monday July 22, 2013 @04:24PM (#6086234)
Well, actually speed was a design goal for Common Lisp. Thus the standard defines the semantics of a compiler, including a file compiler. The CL standard provides stuff like stack allocation, inlining, fast function calling, compiler macros, type system, type declarations, compiler strategy annotations, lots of primitive constructs (like GO in TAGBODY). Most real implementations add several more speed-relevant features.

If you say that Common Lisp 'happens to be fast by accident', then this is completely misleading. 'fast' was a design goal and many implementors have done quite a lot to make demanding applications (like huge object-oriented CAD systems) to be fast.

Re:On Distributing Command Line Applications: Why I Switched... by gngeal on Tuesday July 23, 2013 @08:06AM (#6089466)
Well, actually speed was a design goal for Common Lisp.

It might have been one of the design goals, but it wasn't the ultimate design goal (which, I believe, was making a convergent compromise dialect to rule them all), otherwise certain parts of the language would have been most likely simpler. The things you mention are the good stuff. The problem is that there's also some bad stuff that hindered performance of CL on stock hardware until quality compilers were written. Yes, SBCL is quite fast. But you'd have a hard time trying to convince me that even modern implementations of Common Lisp represent some sort of performance optimum in the same way that, say, Stalin Scheme represents an optimum.

Also, wouldn't TAGBODY/GO be replaceable in presence of proper tail recursion by structuring the "TAGBODY basic blocks" into mutually recursive function calls? The same argument could be applied to inlining: Yes, the Common Lisp standard mentions inlining. But did you notice how nowadays, that's more or less considered an implementation issue rather than a language issue? How JVMs are capable of inlining just fine without having to be hinted what to inline and what not to inline?

Now I'd understand some of the hints, perhaps DYNAMIC-EXTENT makes somewhat more sense if the purpose is to say "It's my intent to create this value for a limited period of time, yell at me if I pass it out", but in general, from where I stand, a lot of the design decisions seem to make writing Common Lisp compilers actually more complicated to modern compiler writers.

Re:On Distributing Command Line Applications: Why I Switched... by lispm on Monday July 29, 2013 @06:25AM (#6120179)
Common Lisp had several design goals. Speed was an important design goal. The users of Common Lisp wanted to be able to deploy and run large and demanding software on stock hardware.

> ultimate design goal (which, I believe, was making a convergent compromise dialect to rule them all), otherwise certain parts of the language would have been most likely simpler.

No, there was no ultimate design goal and Common Lisp was not designed as a compromise dialect at all. Common Lisp was designed to be a modern replacement for Maclisp and was incorporating several projects working on a modern Maclisp variant, for example NIL, Spicelisp and Zetalisp. Common Lisp was not designed to compromise over Emacs Lisp, Franz Lisp, Portable Standard Lisp, Lelisp, UCI Lisp, Interlisp, Scheme and whatever dialect was there at that time (Common Lisp first round of design was between 1982 and 1984. Then the standardization of ANSI CL was worked on until the early 90s.) If Common Lisp was compromise, then mostly between Maclisp successors. Later during standardization the Condition System of ZetaLisp was integrated, the LOOP of Maclisp and CLOS was developed as a new object system (based on New Flavors from Zetalisp and Portable Common Loops from Xerox). CLOS was also not as a compromise, but a radically new system optionally based on a Meta-Object System.

CLtL1 says: Common Lisp is a new dialect of Lisp, a successor to Maclisp. Influenced strongly by ZetaLisp and to some extent by Scheme and Interlisp.

> that hindered performance of CL on stock hardware until quality compilers were written

Quality compilers were written almost from day one: CMUCL, Allegro CL, LispWorks, Lucid CL. Of those Lucid CL was the fastest with the most optimizations.

Note that there are two or three different types of speed that needs to be addressed in CL: 1) the speed of unoptimized, fully dynamic and safe Lisp. 2) the speed of optimized, non-dynamic, and possibly unsafe Lisp. 3) the speed of production applications which need to be safe, somewhat dynamic, but optimized.

> Also, wouldn't TAGBODY/GO be replaceable in presence of proper tail recursion by structuring the "TAGBODY basic blocks" into mutually recursive function calls?

TCO is done by some implementations. Generally it is not seen as a useful default feature. Common Lisp favors iteration over TCO-based recursion. That's also what I favor. Since there are some implementation targets which don't support TCO (or where TCO makes implementations more difficult, one went to the simpler language). For example some of the Lisp Machines at that time did not support TCO. Today for example the JVM (where Common Lisp also runs on) does not support TCO. Scheme is seen as a different Lisp dialect and CL mostly learned from Scheme the default lexical binding - not more. Remember Scheme is much older than CL. CL did not adopt Scheme's one namespace, its calling semantics and conventions, not its naming conventions, not CALL/CC, not its macro system, ... This again shows that CL is not a compromise dialect - it simply does not support a lot Scheme-style programming - even though Scheme is a decade older than CL. By design.

> Yes, the Common Lisp standard mentions inlining. But did you notice how nowadays, that's more or less considered an implementation issue rather than a language issue? How JVMs are capable of inlining just fine without having to be hinted what to inline and what not to inline?

Common Lisp was not designed for one special implementation platform. Some CL compilers may honor INLINE declarations (many do), others do inlining automatically (like Allegro CL). If an implementation of Common Lisp on the JVM uses its inlining capabilities, fine. But there are many other implementations. ECL for example compiles to C and nobody is would say it is a good thing to stop working on that, just because the JVM exists. The JVM is a language issue. It's the Java Virtual Machine. It was not designed to host Lisp or support efficient implementations of Lisp. One can implement Lisp on the JVM, but it is a not so good fit.

> a lot of the design decisions seem to make writing Common Lisp compilers actually more complicated to modern compiler writers.

You are vague on these issues. Sure a good Common Lisp compiler is complex, but because the complexity is in things like the type system, type inferencing, etc etc. The base language itself is relatively simple. The result is that CL compilers still are producing faster code than most dynamic languages. Native implementations are still much faster and tighter than what the JVM can offer.

Re:On Distributing Command Line Applications: Why I Switched... by izietto on Monday July 22, 2013 @10:19AM (#6083778)
As the Topaz guy said, the slowness is due to the abuse of hash table lookups, allocation and copying (HTLAC) https://speakerdeck.com/alex/why-python-ruby-and-javascript-...

And the abuse of HTLAC patterns is due to the lack of non-HTLAC APIs; the syntax is trascurable

Re:On Distributing Command Line Applications: Why I Switched... by james_madison on Monday July 22, 2013 @10:09AM (#6083709)
Ruby without dynamism runs a bit faster: http://rubyluwak.com/pages/benchmarks
Re:On Distributing Command Line Applications: Why I Switched... by vidarh on Monday July 22, 2013 @03:16PM (#6085871)
WTF.... I just looked a bit closer at this, and for starters the graphs are for startup times compared to JRuby, and isn labelled with any values. They say nothing about the performance of the actual code.

Furthermore, it appears to be a commercial product, yet searching for it reveals only a handful of link to their own site, and hardly no mentions elsewhere and the examples/docs are pitiful at best.

Looking quite shady to me.

Re:On Distributing Command Line Applications: Why I Switched... by ruby_luwak on Thursday July 25, 2013 @02:05AM (#6101154)
Hi, thanks for your frank comments.

the graphs are for startup times compared to JRuby

The first graph compares startup times. The second graph compares execution times. This has now been clarified on the site.

the examples/docs are pitiful at best

Working on it! What would you like the docs to address specifically?

Looking quite shady to me

I'm sorry you feel that way. Would you like a free evaluation license?

Re:On Distributing Command Line Applications: Why I Switched... by vidarh on Monday July 22, 2013 @11:01AM (#6084073)
That's interesting, but if you start type annotating the code, then you lose a large part of what makes Ruby attractive to a lot of people.
Re:On Distributing Command Line Applications: Why I Switched... by james_madison on Monday July 22, 2013 @11:06AM (#6084122)
people write annotations already anyway, in the comments or documentation: see the method signatures at http://ruby-doc.org/core-2.0/Object.html
Re:On Distributing Command Line Applications: Why I Switched... by vidarh on Monday July 22, 2013 @11:35AM (#6084363)
Yes, take a look at those method signatures. They are imprecise and incomplete. And that is for MRI itself. Very few Ruby projects has documentation anywhere near what MRI does, whether separate or in the comments.

But even if it did: Rubyists are far more likely to be tolerant about documenting type information in optional comments than including them in the code.

I think there's room for experimenting with Ruby's typing, but I also think the most that project can hope to achieve would be extensions that could see some limited use in the odd little piece of performance critical code. It's best hope of success, actually, would be that JRuby and other alternative implementations grow more popular and make it harder for people to rely on C-extensions.