
Ruby vs. Crystal Performance - ptcodes
https://ptimofeev.com/ruby-vs-crystal-performance/
======
chrisseaton
Crystal and Ruby do not even remotely have the same semantics. They look
similar but the similarity is extremely superficial. The extra semantics that
Ruby has have a _massive_ impact on performance, for even the most optimising
implementations. This is not a reasonable comparison if you do not consider
the 'slick as Ruby' part. No runtime metaprogramming! The entire Ruby
ecosystem is built on runtime metaprogramming!

(Crystal is a fine language and compiler - but it's nothing to do with Ruby.)

~~~
eggsnbacon1
Metaprogramming doesn't need to have a performance impact. VM languages like
Java, C#, and JS allow you do define and modify code at runtime

JS you can redefine anything, Java support is pretty good, C# better support
is coming with generators

~~~
YorickPeterse
Ruby's meta-programming capabilities is why optimising Ruby is so darn
difficult. It's extremely powerful, but also complicates a lot of things.

~~~
eggsnbacon1
I reason the difference is mostly when metaprogramming can happen. In Java,
redefining or adding code is very explicit, it can't just happen whenever.
Same with C#. And there are lots of rules. A class can't modify itself, and
there's limits to what changes you can make. And to make changes you must have
control over the "classloader" that loaded that code.

JS on the other hand can do most of what ruby does, to my knowledge. Objects
are key value pairs so you're free to mess with them in virtually any way you
please. You can also mess with the inheritance by altering JS prototype
chains.

I don't think metaprogramming itself has much to do with the speed of Ruby,
with my admittedly limited knowledge of this stuff

~~~
hosh
However, JS does not treat everything as an object as pervasively as Ruby
does. A JS object and a JS integer are two different abstract data types. A
Ruby number literal "1" is treated as an object of the Integer class. There
are no separate abstract data type other than an object. All operators for an
Integer can be overriden (in runtime), or perhaps, a specific object's methods
can be overridden. Now granted, the runtime cheats and implements certain
things in C ... but those can be overriden during runtime ...

If you want to find out more about the limits of what can be done to optimize
Ruby, check out the Truffle project. That came out of someone's PhD
dissertation on novel methods for doing JIT optimization for Ruby. It is
sufficiently difficult and novel to warrant awarding a PhD for. Last time I
heard, Truffle still could not run Rails.

~~~
gavinray
A JS integer is actually an instance of the "Number" object, you can to a
small degree alter fundamental behavior even with primitive types

~~~
jiofih
No it is not. It is a primitive, this difference is well defined in the
ECMAScript spec. Same for strings. An instanceof String is strictly _not_ the
same as a string primitive (and there are runtime consequences).

------
timon999
This article by one of the Crystal developers explains why this comparison is
not fair: [https://crystal-lang.org/2016/07/15/fibonacci-
benchmark.html](https://crystal-lang.org/2016/07/15/fibonacci-benchmark.html).

> When doing operations between integers, Ruby will make sure to create a
> Bignum in case of overflow, to give a correct result.

> Now we can understand why Ruby is slower: it has to do this overflow check
> on every operation, preventing some optimizations. Crystal, on the other
> hand, can ask LLVM to optimize this code very well, sometimes even letting
> LLVM compute the result at compile time. However, Crystal might give
> incorrect results, while Ruby makes sure to always give the correct result.

~~~
ksec
You may want to edit the link.

~~~
timon999
Whoops. Thank you!

------
bovermyer
Coming at Crystal from a Go and PHP development background, here are my
thoughts:

\- The syntax is lovely. No, really.

\- I hate waiting for it to compile, especially compared to Go's compile time.

\- It's really young yet, and the ecosystem is just getting started.

~~~
tluyben2
> \- The syntax is lovely. No, really.

Matter of taste; I find it horrible, just as I find Ruby syntax horrible. And
I do not care about syntax too much generally (among my production langs are k
and clojure) but I find this an eye sore; don’t know why but it is what it is.

Edit: aaah downvotes for an opinion :) Anyway, background; I maintained a huge
Rails codebase for years; it was pretty much the worst thing I ever did (in 30
years of production coding) and that was pretty much down because how much I
don't like the syntax. That doesn't happen often.

~~~
mekster
I don't like rails either but I like ruby syntax. You're sure you don't like
ruby and not rails?

You got downvoted for not liking something "just because". (I didn't
downvote.)

~~~
tluyben2
Is that not a matter of taste means? I even say I do not know why; I just
break out in hives when I open a ruby file. When I open up a file to start
programming, I have to have some feeling of ‘let’s do it’; I have that with
most languages but with ruby I just think ‘crap this is ugly’. Not sure how to
explain that; it is more like trying to explain why I prefer Klee over
Rembrandt. I guess some people can but I cannot put that into words.

~~~
mekster
I share the same feeling when opening up rails project because of their own
weird rules and files being all over the place. I don't feel that when writing
a single ruby file and feel elegant.

~~~
tluyben2
I don't know...

[http://www.rosettacode.org/wiki/Mutual_recursion#Ruby](http://www.rosettacode.org/wiki/Mutual_recursion#Ruby)

I find almost every other implementation better on the eyes; picolisp,
pari/gp, nim and some others I would call elegant; the Ruby version looks like
overly noisy and fairly ugly to me. Not so much (but still) the function defs,
but p (Array.new(20) {|n| F(n) }) I find pretty nasty for some reason (again,
not sure why).

Randomly picked example.

------
ukd1
A massive point for me missing in this article is the experience of having to
wait for Crystal to compile - especially for development, this sucks. Try
timing puts "Hello, world" on both:

    
    
      ~ % cat test.rb 
      puts "Hello, world"
      ~ % time ruby test.rb
      ruby test.rb  0.03s user 0.05s system 25% cpu 0.312 total
      ~ % time crystal run test.rb
      Hello, world
      crystal run test.rb  1.48s user 1.09s system 72% cpu 3.559 total

~~~
coder543
I find this perspective kind of funny, because every medium-sized Ruby project
I've worked on takes 10+ seconds just to load and start rspec (before it runs
any of my tests) or a REPL.

Because of this, working with Ruby is actually substantially _slower_ than
working with a similarly sized Go project, in my experience, even though Go
theoretically has the disadvantage of needing to compile things before running
them.

Plus, Go's type system will catch tons of bugs that Ruby optimistically treats
as "maybe you _meant_ to do this". Obviously Rust's type system is really
awesome, but Go's is still really helpful, while compiling much faster than
Rust.

Since Rust and Crystal are both LLVM-based, I would guess that Crystal's
compile times also tend to be a bit painful on medium-sized projects, like
Rust, but I've never actually used Crystal for anything more than "Hello,
World".

(I've been paid to work full time with Ruby, Rust, and Go over the years, and
they each have pros and cons, but I just don't think I would ever personally
choose Ruby for any new project in 2020, if I had a say in it, given how great
Go is for web development.)

------
frizkie
I'm excited to see Crystal grow - I think it has a lot to offer. I know it's
not an intended consequence or a goal of Crystal's development, but you'd be
surprised how easily plain Ruby maps to Crystal's syntax and method name
choices. It can come in handy when you're looking for an easy performance
boost.

~~~
remmargorp64
Actually, I would argue that mapping Ruby's syntax and method name choices to
Crystal _has_ been a major goal in Crystal's development. It's practically in
the tag line for the language, "Fast as C, Slick as Ruby"

------
hosh
The thing I like about Crystal is that I can generate statically-linked
binaries and distribute them. I can quickly build utilities for people to use
and don't have to distribute the runtime. The type system wasn't as bad as I
thought (especially with type inferencing). Having written Ruby professionally
for 14 years, Crystal was relatively easy to pick up. There are a many
libraries that hew closely to their Ruby counterparts.

There are some amazing things I love about Ruby, like the metaprogramming and
monkeypatching ... but I have moved on. I do my server work with Elixir these
days.

------
devmunchies
Neat seeing crystal on here.

I built my startup using crystal and now I write crystal full time.

I’m still only using a single dedicated server with a SQLite database.

~~~
cutler
Why SQLite?

~~~
devmunchies
That’s how I started it. I didn’t want to start a company with a complex
setup. The whole service runs on a single machine so it’s very fast and
simple. I just back up the SQLite file every hour to s3. Its easy to download
that file from s3 to use production data in development.

If I start getting scaling problems I’d have to start using multiple servers
and at that point I’d need Postgres.

But we’re at ~36 employees and 8 figures of revenue on a single server.

I can probably go another year or 2 without a big database.

------
ksec
I sort of felt compelled to post this. An OS written in Crystal. [1] Which got
quite a bit of discussion last time it was on HN [2] .

I know this may sounds strange but I would not be surprised once Crystal
reached 1.0 there will be a Ruby implementation written in Crystal.

[1] [https://github.com/ffwff/lilith](https://github.com/ffwff/lilith) [2]
[https://news.ycombinator.com/item?id=21860713](https://news.ycombinator.com/item?id=21860713)

------
zucker42
The recursively written Fibonacci benchmark is terrible for comparing a
interpreted language to a compiled language. When compiling with optimizations
in C, for example, the compiler significantly transforms the program. [1] It's
possible Crystal does too.

[1] [https://godbolt.org/z/vaESBG](https://godbolt.org/z/vaESBG)

~~~
eeereerews
Recursive fibonacci is highly artificial, but the fact that a compiler can
greatly transform the program is an important advantage of compilers over
interpreters.

~~~
zucker42
I had the (mistaken) impression that the GCC code signicantly transformed the
algorithm because there is only one recursive call instruction, but looking at
it closer it's still exponential. I still think it's a pretty silly idea to
use such an unrealistic benchmark. Especially since it might be possible to
make a compiler turn the exponential algorithm linear.

~~~
nv-vn
I'd be very surprised if there's a commercial compiler that would
automatically optimize this into linear time. Maybe some obscure functional
research language? But certainly don't think LLVM is capable of dynamic
programming.

------
G4BB3R
I really liked Crystal some years ago, even used in a prototype. Soon I
realized that despite its cute syntax and good performance, coming from Elm, I
would prefer a simpler language with type safety, good error messages and very
fast compile time. Maybe in the next years I'll experiment again, especially
due to Lucky web framework.

~~~
BeKindAndLearn
May I ask what language you decided to move to instead?

~~~
G4BB3R
In my current job I use Node/Koa with Js, and I am not happy with it. Every
side project that I start I use Elixir, and I plan working with it next year,
or maybe with Clojure. It's not exactly what I was looking for, but while I
can't find one that matches the criteria in the next years, these are pleasant
options for me.

------
cosenal
Some underrated live coding tutorials on Crystal on this YT channel:
[https://youtu.be/Q1wOFmt3yng](https://youtu.be/Q1wOFmt3yng)

~~~
nodefourtytwo
I knew who it was before clicking. I learnt to love Crystal from this channel.
Great content.

------
mauricio
Here are thorough benchmarks of Crystal and C up against other interpreted
languages: [https://github.com/kostya/jit-
benchmarks](https://github.com/kostya/jit-benchmarks). It has other examples
besides Fibonacci.

~~~
tmikaeld
I'm quite surprised to see Javascript / Node so high up the ranks, quite
impressive the amount of optimizations that's been done to the engine
throughout the years.

~~~
nicoburns
Yeah, a lot of people don't realise that JavaScript performance is closer to
Java than Python, even though the semantics are much closer to Python.

~~~
qeternity
We recently finished our first node backend (typescript) and I have to say it
was an amazing experience. The npm ecosystem definitely attracts its fair
share of undesirables, but I struggle to see why JS full stack isn’t the
default for 99% of people.

~~~
joelbluminator
Lots of people are sure their stack provides an amazing experience, for
example I don't get why you would choose node over rails. As for the
performance: looks good for js, but quite a memory hog or am I missing
something?

~~~
qeternity
I'm not expert in Ruby, but I'd say there's definitely a few wins in
typescript (types, performance, async). But the biggest wins have nothing to
do with language differences, but rather not having to worry about that
because you're using one language. If teams are large and split between api
and frontend, then perhaps less of an issue. But if you're doing anything
fullstack, to be able to use one language, everywhere with mostly the same
ecosystem...well it's kind of what Java wanted to be.

And I say this coming from a python/django background. And ORM is one big
thing that Rails/Django definitely are clear leaders in.

~~~
joelbluminator
I like the one language thing. I think it's overrated a bit (most syntax
mistakes I make are caught by linters) but it's nice. However, it's still js.
Lots of warts still. Also - for a normal web app I really don't see how async
is a plus. I don't like promises or async that much, especially with so much
db access - why would i want everything to go to an event loop?

It's a never ending discussion, I think all of these stacks are "good enough"
to get the job done, a lot of it is how your brain ticks and what you're
experienced with...

------
jimbob45
What’s the elevator pitch on Crystal? Seems like typed Ruby with performance
considerations.

~~~
Trasmatta
Exactly, it's basically statically typed, compiled Ruby with really good
performance.

Also it has really powerful type inference, which allows you to write really
Ruby-like code while maintaining your static typing. The downside of that is
that it makes the compiler kind of slow.

~~~
ngngngng
Does go-to definition work in crystal? That was always my least favorite thing
about ruby, I find it very awkward to write code without being able to jump to
definitions.

~~~
gavinray
This is the reason why I have to keep IntelliJ Idea Ultimate/RubyMine laying
around.

I don't have the greatest laptop, and run a lot of containers, so Jetbrains
IDE's are generally a no-go for me and I stick to VS Code, but trying to
develop Ruby/Rails without a Jetbrains IDE Is crippling.

I think ctrl+click definition jump does work in Crystal and it has a decent
language server.

~~~
ngngngng
That's good to keep in mind, I strictly write code in vim but if I have to
pick up ruby again I'll give those IDEs a shot.

I really like the sales pitch of rails, but as someone that almost exclusively
writes in modern, compiled languages, it's really tough to adjust to ruby and
be productive with it.

~~~
gavinray
I wrote Rails professionally for a few years back when that was THE de-facto
tech for greenfield projects and new startups.

At the time, I really enjoyed it, but I'd never written a modern-feeling
typed/gradually typed language yet (C#, which I found really verbose and ugly,
a lot of pre-ES6 JS and Ruby, some Lua + Python).

I would never voluntarily write it again. Ruby/Rails completely falls apart in
larger projects if the entire team doesn't stick to best practices + stringent
code and doc standards with stuff like YARD or Sorbet. And so many magic,
implicit methods injected everywhere.

Super happy writing Typescript and occasional Rust/Go now. I thought Kotlin
was pretty nice too, the one small project I had to implement in it.

~~~
d3nj4l
Another issue with writing rails is the "bug in production that would've been
caught at compile" issue lots of people have. There's been multiple instances
early in my career when I missed a nil guard somewhere that broke months later
because someone else used the method and sent it a nil. Types are super useful
because they also work as on-demand accessible documentation, and the compiler
helps you figure out well in advance when you're sending an invalid input etc.

~~~
jmchuster
I mean, the answer is just, more tests. If you choose a dynamic language over
a compiled one, then you've in essence chosen to eliminate type definitions in
code and replace them with type verifications in tests. Of course, if you
switch to a typed language that doesn't have nil types, that doesn't help...

------
choxi
If `brew install crystal` is taking more than an hour to complete, in my case
it was because llvm is a dependency and for some reason it was trying to
compile it from source. If you manually install a precompiled llvm (you can do
`brew install llvm` with some flags) it should skip that step during the
crystal installation.

~~~
AlchemistCamp
I installed crystal almost instantly with ASDF. Just:

    
    
        asdf plugin-add crystal https://github.com/asdf-community/asdf-crystal.git
        asdf global crystal 0.34.0 
    
    

It was painless. Other languages I've installed with ASDF must have had the
same dependencies.

------
pansa2
_> If you recall Crystal is a statically typed language but you can omit
explicit type restriction and the compiler will try to infer the type of
variable. In our code Crystal uses the Int32 type for the n variable which has
the maximum value of 2,147,483,647 but the 47th number is higher. In this case
we need to specify the type of n. We can use Unsigned Int 64._

Both Ruby and Python use arbitrary-precision integers throughout. I thought
Crystal would do the same, given the frequent comparisons to Ruby. I assume
the reason it doesn't is to enable AOT compilation (i.e. not only because
Crystal is statically-typed)?

Is there a statically-typed language that _does_ use arbitrary-precision
integers throughout?

More generally, what other features are common in dynamic languages and rare
in static ones, that aren't directly related to type-safety?

~~~
phamilton
Haskell has arbitrary precision.

JavaScript has fixed precision.

~~~
mumblemumble
Well, they both have both. More or less. Haskell has Integer for arbitrary and
int for fixed precision integers, for example. JavaScript has arbitrary
precision integers (via BigInt), and fixed precision floats.

------
squarefoot
Crystal would benefit a lot if ported and packaged for ARM architectures.
Those small embedded boards seem the best place to make use of a system
language that produces tight and fast native executables. I know there's
something for the Raspberry-PI+Raspbian, however I hoped there was an official
port for other boards on a more generic distro such as Debian, Armbian or
DietPI. Being a bootstrapped compiler (a compiler written in the same language
it compiles) makes this even harder to accomplish.

------
megavolcano
Huge ruby ran here. Ruby on Rails has been my day job for a decade, and it
pays the bills well enough. Crystal has always been a curiosity to me, but I
have never been as productive in any other language or framework as I have
been with Rails. I dont' care if you're language is "ruby-like" or "go fast"
\- if I'm not as productive with it as I am with Ruby, I'm not gonna touch it.

Get back to me when there is Crystal on Rails.

~~~
kimburgess
Here you go: [https://spider-gazelle.net/](https://spider-gazelle.net/)

~~~
megavolcano
Does it also have a massive community and thousands of libraries(rubygems) for
almost every possible thing you can possibly think of?

~~~
kimburgess
It's a significantly younger language than Ruby, so no, the ecosystem is not
as diverse (yet). It is quite populated though - for a taste you can browse at
sites like [https://crystalshards.xyz/](https://crystalshards.xyz/) or
[https://shards.info/](https://shards.info/).

------
kipply
I think language-to-language comparisons are often "unfair". Comparing Ruby
and Crystal is probably not much better than comparing Ruby to C. Syntax is
not an absolute signal for language similarity, and the most important parts
of Ruby (in my opinion) more or less require it to be interpreted and not
compiled. One can always make languages look the same (which Facebook did with
OCaml <-> JS transpiler) without the languages being similar by other
measures.

tl;dr "yes, that one is faster and looks similar to the other one so our
developers will be less scared and this is good" is important and valid but
"that one is just the fast language but slower" is not a valid comparison,
which I'm kind of getting.

------
deedubaya
I really like Crystal and have had good success using it for web stuffs. I
still reach for ruby first, because the dev feedback loop is a lot faster and
the ecosystem is more vibrant.

The biggest gain I've found in using Crystal vs Ruby is reduced memory usage.

Most often my code isn't slow because of the language, but the data access
behind it, so counting CPU cycles is less of a priority for me.

------
mhd
Crystal is quite interesting, but a fib benchmark?

~~~
WJW
I mean it's kinda trivial but it does have the added benefit of also showing
off that it's statically typed. For a more real world example check out the
website of Kemal (Crystals Sinatra equivalent) at
[https://kemalcr.com/](https://kemalcr.com/), where it benchmarks at ~46k
requests per second in a HTTP benchmark vs 4k in Ruby.

~~~
ksec
Or something like Sidekiq in Crystal [1], which was 7 times faster. But now I
wonder how much faster would Sidekiq run on TuffleRuby.

[1]
[https://github.com/mperham/sidekiq.cr](https://github.com/mperham/sidekiq.cr)

~~~
WJW
I'd be amazed if TruffleRuby could beat Crystal, given the problems most JITs
have had with Ruby. By restricting the extreme dynamism of Ruby a bit, Crystal
can allow for optimalisations that no Ruby implementation will be able to
match. It's just really difficult to properly compile languages where it's
possible to dynamically redefine the + operator based on input from HTTP
requests. That said, Truffle is looking super cool and Crystal does not have
anything near the size and quality of the Ruby ecosystem yet.

------
danielsokil
Here is a much more fair comparison: Ocaml vs Crystal vs Go:

[https://gist.github.com/s0kil/155b78580d1b68768a6c601a66f8e2...](https://gist.github.com/s0kil/155b78580d1b68768a6c601a66f8e29b)

------
kotutku
I would like Crystal to include some modern features instead just being a fast
and nice language. How about stuff like: immutability, good functional
programming support, complex union types, compile-time guarantees?

~~~
brokencode
Check out Crystal’s official website sometime to learn more about its modern
features. Crystal doesn’t have immutability, but does have support for
functional programming and union types. “Compile-time guarantees” is such a
vaguely specified feature that virtually all programming languages have that,
but its type system is actually pretty strong despite how it looks with all
the type inference, and even prevents null pointer exceptions unlike a lot of
static languages.

It also has a lightweight threading system that makes it work well as a web
server. Not quite as elegant as something like Elixir, with its isolated
processes, but still nice. It also supports macros, which can make the code a
lot more elegant and performant compared to similar languages.

------
tobyhinloopen
It’s cool if it looks like Ruby but if it doesn’t run Ruby gems, it’s kinda
pointless to consider it a Ruby alternative. Might as well consider Rust or
C++

------
dzonga
some of these benchmarks Fibonacci etc are superficial. don't reflect real
world use. people need to start comparing languages on ecosystems etc, error
reporting e.g Elm | Rust have beautiful error messages. time to write a
feature and deployment. Your language | framework could run in microseconds
etc but if getting started is a nightmare or it has cryptic error messages,
then it's non starter

