
Improving Ruby Performance with Rust - yarapavan
https://blog.codeship.com/improving-ruby-performance-with-rust/
======
pmontra
If one needs speed one does everything it takes. That said, I read the code of
the example. It's short and even if I don't know Rust I've been paid to write
C in the 90s. They look similar enough.

I see we're back to stuff like

    
    
        let ptr = path.as_ptr();
    
        let c = unsafe { *ptr.offset(i) };
    
        if c == SEP {
          return &path[(i + 1) as usize..end];
        };
    

It's as low level as it can get. I remember that I wrote a small web app in C
in 1994 but I wrote the next one in Perl and never looked back. It was some
hundreds lines, countless core dumps and many hours vs much less code and pain
and time.

Again, Ruby and Python are all about connecting those small pieces of C code
that implement their builtin and library methods / functions. Writing them in
C or Rust makes no difference. I personally won't write code in those kind of
languages again unless I find myself in a scenario where CPU time is worth
more than my time. Maybe we'll be back to that with functions in the cloud
billed by the millisecond. Programming is going to be a pain again. At least
modern languages like Rust don't crash like C and have a better concurrency
story. Meanwhile I'll keep using Ruby and Python with C or Rust extensions
from GitHub written by somebody else (a big thank you!), and Elixir where
concurrency matters.

~~~
rapsey
Of course it looks like C, it's using a C interface to interface with Ruby.
Normal Rust does not look like that. Your comment is quite frankly entirely
pointless.

~~~
whyever
Your comment is misleading. The code in question is inside a function of the
signature

    
    
        extract_last_path_segment(&str, &str)
    

This is pure Rust, no C interface involved.

~~~
sambe
Per my comment above linking the real implementation: the comment is perhaps
misleading about the cause but its claim is largely true. Rust code tends not
to look like this.

~~~
whyever
Well, unsafe Rust does tend to look like this. You were claiming that it is
only for interfacing with C code, but unsafe is also used for performance, as
in the snippet above. You were IMHO incorrectly dismissing the parent comment
based on that. (On the other hand, the code in question could probably
rewritten in safe Rust while still avoiding the bound checks. It would be less
straight forward though.)

------
vvanders
Not just Ruby, Python too.

Was doing some work of converting [0,1] signals to 16-bit PCM data with 550Hz
tone in numpy.

Python version took ~15 minutes to generate 5,000 4 second files. Broke out
the inner loop into Rust with FFI via ctypes and cut that time to ~10 seconds
with nearly identical code.

~~~
fulafel
A 100x difference to native and having an inner loop in Python code sound
exceptionally slow for a numpy app. I guess you could not leverage numpy array
operations?

~~~
vvanders
Nope, I agree that base numpy operations are snappy. However I needed to both
multiply by a sin wave of 550hz(changing input) and taper the edges of the
signal on the transition from 0->1 to not generate a ton of harmonics.

------
kroltan
If one is to write such simple code in unsafe mode, might as well just use C!

We have [https://doc.rust-
lang.org/std/primitive.str.html#method.as_b...](https://doc.rust-
lang.org/std/primitive.str.html#method.as_bytes) for accessing a str bytewise.
No need for pointers and `unsafe`.

The whole point of Rust is being safer and higher-level than C, and if you
don't want to use its features, there is no improvement over C.

~~~
kzrdude
There is plenty of improvement over C: generics, ADTs, type inference, and
toolchain. Which you can combine with raw pointer hackery if you want.

~~~
kroltan
Yes, I'm just nitpicking his example code. It might pass the wrong impression
to people who never saw Rust before.

------
rqs
Sorry for my ignorance, but since you already doing Rust, why not just use
Rust?

I mean, what's the benefit you could get from FFI Rust code into Ruby that you
cannot get by directly writing Rust?

~~~
rapsey
Rust is a fantastic C replacement. It is nowhere near being a replacement for
higher level languages in terms of productivity and ease of use.

~~~
bluejekyll
I think you might have that slightly incorrect. Rust is a high level language,
capable of C and C++ speeds. As you get to know the language, I find it just
as productive as Java, Ruby and Python. In some ways more so, because the
compiler catches so many bugs before you even run (not to imply you won’t have
bugs, just different ones).

YMMV.

~~~
Thaxll
Rust as productive as Python lol ... things you read on HN. Even if the
compiler catches more bugs it doesn't mean it's more productive, I give you a
problem and you have to solve it in Python and Rust, Python problem will be
solved much faster because the language has higher construct for day to day
programming.

~~~
steveklabnik
It depends on what you mean. You're optimizing for time to first solve; what
about when you find bugs that would have been caught at compile-time in Rust?
What about existing codebases, rather than writing new code? It's not that
simple of a question.

I find personally that if there are libraries to help me out, I'm only
slightly slower in Rust than I am in Ruby, but then there's no debugging time
in Rust, and invariably, there will be tons of work I have to do later with
the Ruby to shake out bugs.

YMMV.

------
Doctor_Fegg
In a similar vein:
[https://github.com/phoffer/crystalized_ruby](https://github.com/phoffer/crystalized_ruby)

Write the speed-sensitive bits in Crystal. Aka 'Improving Ruby Performance
with Almost-Ruby'.

(Unfortunately development seems to have stopped.)

~~~
prh8
Author here. Development is indeed on hold for now. However, Crystal core team
has plans to build a DSL into Crystal to allow for creating Ruby native
extensions.

There are some technical limitations with how I handled defining functions,
which has also become obsolete with recent changes to Crystal. However, there
is another approach (macro based) demonstrated at
[https://github.com/spalladino/crystal-
ruby_exts](https://github.com/spalladino/crystal-ruby_exts). The macro
approach is what is needed.

At time of first development, this was just an experiment and I didn't feel
like redo-ing it this way. Now that core team has plans for similar
functionality, I'd rather not finish a half baked solution that distracts from
what theirs will likely be.

~~~
Doctor_Fegg
Very intriguing. Look forward to seeing what the core team do.

------
turboladen
Does anyone know of ruru’s status? I’ve used it quite a bit and love it, but
haven’t seen any action in the repo in a while. I also pinged the Gitter
channel a few weeks ago on the topic and got no response. Its a great tool;
would hate to see it die.

~~~
steveklabnik
The maintainer is in and out, life happens.

------
rubyfan
I might have missed it but is there a particular advantage Rust has over C in
this specific use case? Performance, ease of interoperability, etc. or is it
preference for Rust over C?

~~~
unrealhoang
Safety, I guess. A Ruby developer will likely to shoot themselves in the foot
with C's freedom. They can use help from Rust's guidance system.

~~~
vvanders
Rust also has a much nicer package ecosystem. It's pretty trivial to wrap
existing C libraries as well so I find that I can get up and running much
faster rather than messing around with makefiles.

~~~
bpicolo
Yeah, this is the killer feature. cmake is a nightmare. Cargo does what it
needs to do and gets out of your way, and provides easy access to the whole
ecosystem.

------
yarapavan
github code repo used in the article:
[https://github.com/danielpclark/bench_ruby_ffi_example](https://github.com/danielpclark/bench_ruby_ffi_example)

------
andrewfromx
where is rust full implementation of ruby on this list?
[https://github.com/cogitator/ruby-
implementations/wiki/List-...](https://github.com/cogitator/ruby-
implementations/wiki/List-of-Ruby-implementations) does such a thing just not
exist yet? Let's built it tonight!

~~~
kibwen
Rather than building from scratch, you'd probably just want to start by
replacing bits of MRI with Rust one component at a time (assuming MRI is
appropriately modularized) and see how far that takes you. :P

~~~
steveklabnik
Hi [http://www.codemesh.io/codemesh2016/steve-
klabnik](http://www.codemesh.io/codemesh2016/steve-klabnik) ;)

[https://github.com/steveklabnik/ruby/tree/rust](https://github.com/steveklabnik/ruby/tree/rust)

------
jlebrech
I wrote a script in Go and used it in ruby with popen3.

------
iamleppert
Am I really reading it right that he went to the trouble of doing all this to
speed up the string parsing of file paths??? Talk about barking up the wrong
tree.

I guess its cool to use something as hipster as Rust to speed up as something
as hipster (albeit waning in popularity these days) as Ruby.

However, a better approach would be a straight forward implementation of
caching (memcache/redis/static CDN) or to simply rewrite that part of the code
to not rely on constantly parsing file paths? Or use something like
[https://github.com/google/re2](https://github.com/google/re2), you probably
won't be able to do any better than that when doing any kind of string
matching and parsing.

If you're concerned at all about performance you shouldn't be doing anything
with the file system period.

