7 ms·
Why Is JRuby Slow?
- setheron 5y agoThis piece doesn't have much substance. tl;dr; starting a huge VM takes time. JRuby and it's active maintainer @headius have been great to work with. Surprisingly a very vibrant and active community.
- agbell 5y agoThat seems unfair. Even after warm-up time Jruby and truffleruby are still slower and the truffleRuby folks are investigating it. I thought looking into why something was slow was worth writing down as a blog post. https://github.com/agbell/jekyll-perf https://github.com/agbell/jekyll-perf
- airhead969 5y agoThere are/were imperfect cheats to reduce startup times like nailgun, drip, and spork. The biggest gotchas in JRuby are finding alternatives to native extensions and memory use. What's neat is plugging into JMX from Ruby land.
- tmp231 5y agoI remember about 10 years ago the promise was that JRuby was going to let ruby be basically as fast as Java for many things. Invokedynamic and all that.
- jshen 5y agoIt can be with work. This is basically true of all languages. I remember telling my team that I wanted them to use Java instead of python for some new service we were building because I wanted it to scale better. They were not happy about it, and they coded up two versions, one in python and one in Java. I was shocked by the results, they performed roughly equally in terms of latency and scaling. When I dug into it, the very abstract Java library that gave them the power of python ran about as fast as python. They could get rid of that library which made them far more productive, but then the project took longer and cost more. In fairness, the Java version had an easier path for optimization, but there are no free lunches.
- RcouF1uZ4gsC 5y ago> When I dug into it, the very abstract Java library that gave them the power of python ran about as fast as python. They could get rid of that library which made them far more productive, but then the project took longer and cost more. Sounds like they were determined to write Python on Java. Doing it that way likely has a lot of performance costs. However, you can’t assume that idiomatic Java code would take that much longer than Python code for a team that was familiar with Java. Likely it comes down to which languages and frameworks a team is familiar with.
- deleted 5y ago[deleted]
- lf-non 5y agoI am curious what library was this and what was it doing. We used spring expression language for dynamic evaluation of (user-defined) expressions in our code and for most cases we could compile and cache the expressions after first usage and invoking them was really fast and close to pure java expressions. We also had some JVM-python interop which we eventually got rid of (in favor of kotlin) because we were unable to optimize it after a month of effort and it continued to be the biggest bottleneck in the system. So I am not entirely convinced that there could be real-world usage scenarios that inherently demands so much runtime dynamism that most benefits of JVM optimizations are nullified. Of course, I'd love to be enlightened otherwise, but rather happy with JVM as of now.
- kaba0 5y agoThere is nothing at the JVM level that would disallow such dynamism. Clojure, JRuby, JPython all can run on the JVM. Also, if you are looking for interop, then GraalVM might be worth a look — not the better-known AOT part, but the runtime one, which can seamlessly do interop between a number of languages, and it even optimizes between them!
- lf-non 5y ago
- dragonwriter 5y ago> I remember about 10 years ago the promise was that JRuby was going to let ruby be basically as fast as Java I’m pretty sure it was more than 10 years ago that Charles Nutter wrote a detailed description of why that wasn’t going to happen without breaking compatibility with Ruby, identifying the specific language features preventing that.
- jashmatthews 5y agoThe Truffle based implementations of dynamic languages get close depending on what you’re benchmarking. https://stefan-marr.de/papers/dls-marr-et-al-cross-language-compiler-benchmarking-are-we-fast-yet/ https://stefan-marr.de/papers/dls-marr-et-al-cross-language-...
- cutler 5y agoIf you want fast Ruby it seems Crystal is your only option. Ruby is an interpreted language released in 1996 for scripting. Why are we surprised when attempts to shoe-horn it into something else (Ruby 3.0, JRuby, Truffle Ruby) fall flat?
- canadianfella 5y ago> Ruby is an interpreted language released in 1996 for scripting. Why are we surprised when attempts to shoe-horn it into something else (Ruby 3.0, JRuby, Truffle Ruby) fall flat? Because Lua (1993) and JavaScript (1995) can be very fast.
- nine_k 5y agoTo be just, Lua is drastically simpler as a language. A simpler language likely takes less effort to write an efficient JIT for its VM.
- ZeroGravitas 5y agoAnd javascript's speed seems mostly a result of throwing amazing amounts of smart engineers at it over time, rather than anything inherent in the language itself.
- pjmlp 5y agoHow is the state of LuaJIT since Mike's departure? I don't follow Lua, but I got the feeling it is pretty much stuck on its last version.
- zizee 5y agoCould you not make the same argument about JavaScript? JavaScript was pretty slow for many years. Google then put some significant engineering investment into the V8 engine, resulting in a huge increase in JavaScript's performance.
- nine_k 5y ago
- ksec 5y agoNow that this has gotten attention from the creator of both JRuby and TruffleRuby. I would not be surprised if Jekyll runs ridiculously fast on both implementation in the near future.
- crazy5sheep 5y agoThere's once I was running some jruby stuff in jenkins during a build, the job kept on hanging on some stage, I thought there must be bug somewhere, I forced kill it a couple of times with no success, but kept the last one running before I head home. Then after a couple of hours, I found out an email said the built was passed... eventually, I had figured out that jruby was using /dev/random, since jenkins was running in vm, so no enough entropy was generated. after force mounting /dev/random to urandom, the hanging issue just disappeared.
- deleted 5y ago[deleted]
- papercrane 5y agoYou can pass "-Djava.security.egd=/dev/urandom" to the JVM instead of changing the mounts.
- nijave 5y agoSeems like there's obviously a big I/O performance difference. Maybe it's something simple like buffering being setup differently or not at all or sync vs async. It'd be interesting to dig in more Even something like mmap can drastically improve performance since it lets the kernel handle I/O asynchronously from your program execution (so your code doesn't block as much or as easily on I/O)
- tom_mellior 5y agoWe don't know that it's really I/O, as in "pushing some bytes to the system". All we know is that the author saw a hot method called "write" and stopped the analysis there. It might well be something like messing around with character encodings to get those bytes in the first place.
- tom_mellior 5y agoThinking about this more, we know that the author saw a hot "write" method in the profile for the fast run and doesn't have a profile for the slow run. The slow versions could be spending most of their time in a completely different place.
- deleted 5y ago[deleted]
- habibur 5y agoHere's the summary: Ruby like most of all other Linux applications from that age was written to run in multi-process mode for parallelism, instead of being multi threaded. But running multi-process is heavy on the JVM as it has to load the virtual machine and JIT for every process start, special when the processes are short lived. Java solves it by encouraging running things as multi-threaded instead of multi-processed. But Ruby wasn't built to run multi-threaded. And CRuby even if can run threads, does it by implementing a global thread lock. Aka, only one thread can run in a process at a time. The other threads have to wait until it finishes. It looks like the multi-process implementation is good enough for CRuby and JRuby's attempt to turn it into multi threaded application didn't improve things. Solution is to build a JVM that can load the virtual machine, JIT and execute applications fast enough like C programs.
- pjmlp 5y agoWhat makes GraalVM interesting is that it builds upon the research done in JikesRVM and MaximeVM, alongside a free beer offering. Commercial versions of "a JVM that can load the virtual machine, JIT and execute applications fast enough like C programs" have been available since around 2000, like Excelsior JET or WebSphere Real Time JVM among others. The JIT cache used in recent versions of Hotspot started as part of JRockit JVM, also commercial only product.
- dragonwriter 5y ago> Ruby like most of all other Linux applications from that age was written to run in multi-process mode for parallelism, instead of being multi threaded. That’s...not really true. Ruby wasn’t really written with parallelism in mind at all, because it mostly ran on machines that couldn’t run really parallel processes. MRI threads were originally green threads, which allow a high degree of concurrency without parallelism, with less overhead than native threads. When old MRI was replaced with YARV in 1.9 (which became the new MRI), it got native threads with a global VM lock (GVL, similar idea to Python’s GIL) which allowed running thread-safe native code with real parallelism but only having one thread running Ruby code at a time. This made Ruby thread-based concurrency somewhat more expensive, but some parallelism possible in Ruby (as native code can and some basic common processes like waiting on I/O do release the GVL.) And Ruby 3.0 introduces a new parallelism model with Ractors (basically inspired by the Actor model), which are logically above the thread level (each contains its own set of nonshared threads) and below the process, don’t share mutable state within the VM, and each have their own VM lock, allowing a higher degree of Ruby parallelism without going multiprocess.
- phibz 5y agoI was surprised at the level of analysis and optimization here. Just running flame graph and saying "I think IO is slow" isn't going to cut it. Let's break out a profiler and dig in to the output. And also try some Java tools like your kit.
- jillesvangurp 5y agoExactly, this is not jruby being slow but a particular ruby program being slow on jruby. There is a reason for that and it is extremely likely to be a fixable problem. This is code that was never optimized for the JVM so there is probably all sorts of stuff happening that makes a lot of sense on MRI that is maybe a bit sub optimal on the JVM. One thing that comes to mind is that a lot of performance critical stuff in ruby is implemented via native libraries. The Jruby ecosystem has alternate implementations for a lot of that stuff. But it is probably also able to interface with native code directly. That sounds like that might be a little bit of a bottleneck potentially. And any alternate java based replacements for whatever is being called might have its own issues/bugs/etc. But instead of hypothesizing what the problem might be (and getting that wrong repeatedly), profiling tends to be much more effective indeed. I've done this a couple of times to diagnose performance issues and it rarely is anything you'd expect. Once you know where it is spending its time, you can usually mitigate the issues. Use a profiler, add some logging, instrument the jvm, etc. There are lots of ways to do this. Even just knowing how often it starts a new process would be good to know. It's apparently more than once because otherwise you'd expect --dev to not speed things up like it did.
- fulafel 5y agoSo there was no easy way to get a profile from the JVM version, and data came only from looking at the CRuby profiles where file access dominates. Then an informed guess: slower JVM file access. This process and lack of data in itself sounds like a receipe for performance problems in the JVM version, unless there was some low probabvility coincidence that prevented getting the profile data only in this case. Good measurements are a prerequisite for sustainable and maintainable performance work.
- Asmod4n 5y agoThere was (is?) a time, where JRuby couldnt pass the ruby spec because of their IO abstraction written in Java. They changed that code to be written in ruby instead, with FFI to call out to libc, which made it more correct but slower, afaik.
- sandstrom 5y agoMy impression is that interest in JRuby has gradually declined, in favor of CRuby (MRI). I guess both because CRuby has gotten a lot faster, and because in practice any difference is easily paved over with extra hardware (which is cheap).
- lf-non 5y agoIMHO interest in ruby (irrespective of implementation) itself is declining. A lot of people have realised that dynamic typing hinders maintenance of long lived projects and the tooling and dev experience with type safe languages have also gotten much better over last few years. Despite having worked with Ruby for multiple years, I pick Kotlin/C# for new projects. I know ruby has recently introduced support for typing, but until the wider ecosystem embraces type-safety it is gonna be an uphill battle to write type-safe code in ruby.
- jashmatthews 5y agoThe Ruby community continues to grow in absolute terms of you’re looking at conference numbers etc. The rest of the industry exploded!
- rubyfan 5y agoMy biggest problem with JRuby really wasn’t server performance or multithread - it was start time. If you used JRuby for something like scripting then you feel it way more. Try using it for a map reduce process on large data workloads in Hadoop and you see the startup time of JRuby materially. Q: Why would one do that? A: Ability to bundle your code to hadoop machines you don’t control
- agumonkey 5y agoClojure had the same issue until someone made babashka through graalvm. It's now faster to boot than python..
- blablabla123 5y agoAround 2010 I really enjoyed working with JRuby. At that time dynamic languages weren't really a thing but I was able to convince my boss to try it in a project because of Java library compatibility. So I used it for a Swing GUI application doing data processing combined with R, Postgres and Processing. It was really fun to write it. (Apart from that I also used in on Google App Engine) I never had the impression it was slower or less responsive than any other language, however startup times were quite slow. But I think that was due to the JVM. The way Ruby developed - into the language of Rails - I don't see myself writing anything CPU bound with it though. That said, I wish the article would just include numbers without the startup times. I also remember people claiming back at that time JRuby would be much faster than MRI.