Live data from Hacker News

Ruby 2.6.0-preview2 released with JIT

ruby-lang.org

61–70 of 136 posts

Re: Ruby 2.6.0-preview2 released with JIT

#61

Everything in Ruby happens at runtime. Even the definition `class X` becomes a runtime `Class.new` invocation. It's imperative from the inside out. Even a JIT is compiler won't and can't solve the fundamental flaws permanently baked into the language. If you want performance, Ruby probably shouldn't be the first tool you reach for.

TruffleRuby + Graal brings full Java equivalent JVM performance to Ruby. It can even AOT compile a class definition like you described as impossible by using Partial Evaluation.

Oracle's plan for world domination via JVM is completely changing the performance landscape for dynamic languages.

Re: Ruby 2.6.0-preview2 released with JIT

#62

Earlier quoted context omitted.

MemSQL and Smalltalk generate C at runtime.

Which Smalltalk?

Smalltalk/X can fileout packages as C projects that are then compiled by C compiler. But AFAIK this was never meant to be used as JIT and is primarily an deployment mechanism and non-ancient versions use in-process code generator implemented in Smalltalk as JIT backend.

There are Common Lisp implementations that support similar mechanism of generating C code (ECL, Kyoto CL...), but I don't think any of then compiles C into .so which then gets dlopened right away as poor-mans JIT.

Re: Ruby 2.6.0-preview2 released with JIT

#63
> Add a new alias then to Kernel#yield_self. [Feature #14594]

It might seem strange that they lead with this new feature, but `yield_self` can greatly improve the Ruby chainsaw, and `then` makes it more accessible.

The style of writing a Ruby method as series of chained statements has a non-trivial effect on readability and conciseness. `then` lets you stick an arbitrary function anywhere in the chain. They become more flexible and composable, with less need to interrupt them with intermediate variables that break the flow.

I've been using `ergo` from Ruby Facets for years ... largely the same thing ... and the more I used it the more readable I find my old code now. Funny how adding one very simple method can have more effect than so many other complex, high effort changes.

Re: Ruby 2.6.0-preview2 released with JIT

#64
post #44
post #37

Earlier quoted context omitted.

Having used LLVM for precisely that for both Open Shading Language (OSL) and my startup’s runtime, LLVM’s JIT was pretty good. It’s certainly not optimized for “just throw everything at it function at a time and pray” like a more custom-built JIT (like in HHVM) or even nanojit. But it’s backend output is beyond compare, and you instantly get cross-platform compatibility. As a runtime implementer, it (was) phenomenal.…

Safari's last-layer used LLVM until 2016; it's switched to a custom JIT since then (ref: https://webkit.org/blog/5852/introducing-the-b3-jit-compiler... ). Since you have some experience - do you think shelling out would have been much more painful?

See, I’m out of date! :).

Shelling out (which I’ve also done) is okay, but you never get to really teach the backend what you know. That is, no matter how hard you try, you can’t teach gcc, icc, or clang that you know it’s safe to just fetch this function pointer off a struct and that it’s stable. Writing a simple pass in LLVM though is incredibly straightforward. You can even do a simple inliner, that knows how to inline just the runtime callsites you care about.

Like the WebKit folks and the HHVM folks before them: dynamic languages have enough complexity that you often get most of the win from a “basic compilation” (compared to say C/C++) so after you’ve proven out what you need, you roll your own.

Shelling out though would be strictly worse than the LLVM in-memory approach, since it gets you no additional benefit (in some ways it’s harder, since you can’t just say “jump to this address”), you lose a lot of upside (custom passes, letting you tune optimizations and instruction selection beyond simply -O0, -O1, etc.), and then you get to require users to have a compiler on their box.

I’d personally look at nanojit or the other JIT libraries before shelling out to a regular compiler.

Re: Ruby 2.6.0-preview2 released with JIT

#65
post #60

Everything in Ruby happens at runtime. Even the definition `class X` becomes a runtime `Class.new` invocation. It's imperative from the inside out. Even a JIT is compiler won't and can't solve the fundamental flaws permanently baked into the language. If you want performance, Ruby probably shouldn't be the first tool you reach for.

This isn’t a flaw. It’s a tradeoff. And one that buys an incredibly useful amount of flexibility.

It's a "tradeoff" in the way that not writing tests is a tradeoff

Re: Ruby 2.6.0-preview2 released with JIT

#66
post #21

> We’re going to implement method iniling in JIT compiler, which is expected to increase Ruby’s performance in order of magnitude An order of magnitude as in .. 10x? This seems too good to be true. Half the arguments against Rails melt away like butter if that's truly the case. Anyone with a better understanding of the details care to comment on the likelihood of these performance gains being actually realised, and i…

Sinatra + Sequel is already very competitive in web performance with Go + Gin[1]. It's the Rails convenience stuff which slows things down massively. MJIT could probably bring Rails in line with Sinatra though. Between Ruby 1.8 and 2.5, performance has improved around 13x in tight loops[2]. The Rails performance issue has been massively overblown since 1.9 was released. Ruby 1.8 was a tree walking interpreter, so the…

What I remember reading from the Graal folks a while back was that Rails performance issues revolves around the amount of object creation and destruction.

Re: Ruby 2.6.0-preview2 released with JIT

#67
post #63

> Add a new alias then to Kernel#yield_self. [Feature #14594] It might seem strange that they lead with this new feature, but `yield_self` can greatly improve the Ruby chainsaw, and `then` makes it more accessible. The style of writing a Ruby method as series of chained statements has a non-trivial effect on readability and conciseness. `then` lets you stick an arbitrary function anywhere in the chain. They become mo…

I really would have preferred `pipe` as an alias.

Re: Ruby 2.6.0-preview2 released with JIT

#68
post #48
post #30

Earlier quoted context omitted.

LLVM clearly wasn't designed for JIT. Don't let those letters "VM" confuse you; it's more like a machine abstraction than a virtual machine. And even that is far from water-tight. But that doesn't mean you can't use a conventional compiler stack like LLVM as a JIT and get excellent code - it' just going to take its own sweet time doing so. Can anyone think of any reasonably common stacks using LLVM as a JIT? There's…

> Can anyone think of any reasonably common stacks using LLVM as a JIT? PostgreSQL - although i doubt that's the sort of thing you had in mind!

That's a great example! It's pretty much exactly what I was looking for (well, except that it's probably going to be niche, at least for a while?) Still - good example.

Re: Ruby 2.6.0-preview2 released with JIT

#69
post #63

> Add a new alias then to Kernel#yield_self. [Feature #14594] It might seem strange that they lead with this new feature, but `yield_self` can greatly improve the Ruby chainsaw, and `then` makes it more accessible. The style of writing a Ruby method as series of chained statements has a non-trivial effect on readability and conciseness. `then` lets you stick an arbitrary function anywhere in the chain. They become mo…

Good article about yield_self: https://zverok.github.io/blog/2018-01-24-yield_self.html

Re: Ruby 2.6.0-preview2 released with JIT

#70
post #51

Earlier quoted context omitted.

>Doing _anything_ to reduce this improves performance dramatically It does if you ignore the overhead of JIT compilation itself. However, my understanding is that writing a JIT implementation that performs better than a good interpreter is surprisingly difficult. You have to have a lot of complicated logic for tracking hotspots and using JIT judiciously in short-running scripts.

Hmmm, not really. I think that's somewhat true for JS and webpages, but they're not the same as server-side apps. It's the instruction dispatch overhead that's the real unavoidable problem. LuaJIT, for example, uses a bunch of tricks to minimize it in the bytecode VM, and it's significantly faster than the standard Lua VM but still far, far slower than basic JIT compilation.

Right, but historically there are lots of instances of projects that abandoned JITs because they didn't get a performance improvement. JIT compilation reduces instruction dispatch overhead, but it also, unless accompanied by sophisticated profiling techniques, adds the overhead of JIT compilation time, which can easily swamp the improvements.

Lua JIT is one of the most sophisticated dynamic language JITs out there, so it's hardly evidence that a simple implementation of a JIT will perform better than a good bytecode interpreter.

The problem is less acute for server side apps because the programs run for a long time, so that the initial compilation overhead is insignificant. However, there's a reason that you need a JIT to make Ruby fast rather than an ahead of time compiler. Ruby has so few compile-time guarantees that you need to do a lot of dynamic specialization to get really significant performance improvements. So compilation might still be triggered even after a script has been running for a long time.

I'd add that PyPy, which is also very sophisticated, is often not much faster than CPython, and in fact is slower for some types of code. Writing good JIT-based implementations for dynamic languages is really a tough problem. See e.g. the following post for some explanation of why:

http://faster-cpython.readthedocs.io/notes_2017.html

Post reply on HN