Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Turbo Rails with Rust

Turbo Rails with Rust

Ruby is not the fastest language in the world, there is no doubt about it. This doesn't turn out to matter all that much – Ruby and its ecosystem has so much more to offer, making it a worthwhile tradeoff a lot of the times.

However, you might occasionally encounter workloads that are simply not suitable for Ruby. This is especially true for frameworks like Rails, where the overhead wants to be as little as possible.

In this talk, we will explore building a native Ruby extension with Rust to speed up parts of Rails. What does Rust have to offer here over plain-old C? Let's find out!

Godfrey Chan

May 06, 2016
Tweet

More Decks by Godfrey Chan

Other Decks in Programming

Transcript

  1. WELCOME TO KANSAS

    View full-size slide

  2. WIZARD OF orz

    View full-size slide

  3. KANSAS
    MISSOURI

    View full-size slide

  4. KANSAS
    MISSOURI
    KANSAS CITY

    View full-size slide

  5. KANSAS
    MISSOURI
    KANSAS CITY

    View full-size slide

  6. @chancancode
    GODFREY CHAN

    View full-size slide

  7. gem install javascript

    View full-size slide

  8. require "javascript"
    puts "This is totally Ruby"
    javascript {
    console.log("ZOMG JavaScript");
    }
    puts "This is Ruby again"

    View full-size slide

  9. require "javascript"
    puts "This is totally Ruby"
    javascript {
    console.log("ZOMG JavaScript");
    }
    puts "This is Ruby again"

    View full-size slide

  10. require "javascript"
    puts "This is totally Ruby"
    javascript {
    console.log("ZOMG JavaScript");
    }
    puts "This is Ruby again"

    View full-size slide

  11. require "javascript"
    javascript {
    var plusOne = function(x) {
    console.log(x + 1);
    };
    plusOne(3); # => 4
    }

    View full-size slide

  12. Everyone's favorite JavaScript feature?

    View full-size slide

  13. Everyone's favorite Rails feature?

    View full-size slide

  14. require "javascript"
    class Post < ActiveRecord::Base
    before_create &javascript {
    function() {
    this.slug = this.title.parameterize();
    }
    }
    end

    View full-size slide

  15. require "javascript"
    class Post < ActiveRecord::Base
    before_create &javascript {
    function() {
    this.slug = this.title.parameterize();
    }
    }
    end

    View full-size slide

  16. Subscribe Today!
    http://bit.ly/rails-weekly

    View full-size slide

  17. SIGN UP TODAY!
    https://skylight.io

    View full-size slide

  18. SIGN UP TODAY!
    https://skylight.io/r/r1Z8ogwwN05Z

    View full-size slide

  19. Best of both worlds*
    NATIVE EXTENSIONS

    View full-size slide

  20. gem install json

    View full-size slide

  21. gem install json
    JSON::Pure
    Pure Ruby

    View full-size slide

  22. gem install json
    JSON::Ext
    Native Extension
    JSON::Pure
    Pure Ruby

    View full-size slide

  23. Best of both worlds*
    NATIVE EXTENSIONS

    View full-size slide

  24. Best of both worlds*
    NATIVE EXTENSIONS

    View full-size slide

  25. Best of both worlds*
    NATIVE EXTENSIONS
    for the end-user

    View full-size slide

  26. "Here’s a general value for Rails development: We
    will jump through hoops on the implementation to
    make the user-facing API nicer."
    David Heinemeier Hansson

    View full-size slide

  27. "Here’s a general value for Rails development: We
    will jump through hoops on the implementation to
    make the user-facing API nicer."
    David Heinemeier Hansson

    View full-size slide

  28. "Here’s a general value for Rails development: We
    will jump through hoops on the implementation to
    make the user-facing API nicer."
    David Heinemeier Hansson
    developer experience

    View full-size slide

  29. String#blank?

    View full-size slide

  30. class String
    def blank?
    /\A[[:space:]]*\z/ === self
    end
    end

    View full-size slide

  31. ! SamSaffron/fast_blank

    View full-size slide

  32. UP TO 20X FASTER*

    View full-size slide

  33. &
    ME
    knows just enough C to be dangerous

    View full-size slide

  34. &
    ME
    knows just enough C to be dangerous
    ptr

    View full-size slide

  35. &
    ME
    knows just enough C to be dangerous
    *ptr

    View full-size slide

  36. &
    ME
    knows just enough C to be dangerous
    **********ptr

    View full-size slide

  37. &
    ME
    knows just enough C to be dangerous
    &&&&&&&&&&&&&&&&&&&&ptr

    View full-size slide

  38. FEATHERWEIGHT
    AGENT

    View full-size slide

  39. Rust is a systems programming language that runs
    blazingly fast, prevents segfaults, and guarantees
    thread safety.

    View full-size slide

  40. HACK WITHOUT FEAR

    View full-size slide

  41. This is Rust.
    Rust has a compiler.
    Rust's compiler finds errors at compile-
    time.
    Rust's compiler guarantees the compiled
    program cannot crash at runtime.
    Be like Rust.

    View full-size slide

  42. without garbage collection
    MEMORY SAFETY

    View full-size slide

  43. without data races
    CONCURRENCY

    View full-size slide

  44. ZERO-COST ABSTRACTIONS

    View full-size slide

  45. BACK TO FAST_BLANK

    View full-size slide

  46. FAST_BLANK IN C

    View full-size slide

  47. static VALUE rb_str_blank(VALUE str) {
    rb_encoding *enc;
    char *s, *e;
    enc = STR_ENC_GET(str);
    s = RSTRING_PTR(str);
    if (!s || RSTRING_LEN(str) == 0) return Qtrue;
    e = RSTRING_END(str);
    while (s < e) {
    int n;
    unsigned int cc = rb_enc_codepoint_len(s, e, &n, enc);
    switch (cc) {
    case 9:
    case 0xa:
    case 0xb:
    case 0xc:
    case 0xd:
    case 0x20:
    case 0x85:
    case 0xa0:
    case 0x1680:
    case 0x2000:
    case 0x2001:
    case 0x2002:
    case 0x2003:
    case 0x2004:
    case 0x2005:
    case 0x2006:
    case 0x2007:
    case 0x2008:
    case 0x2009:
    case 0x200a:
    case 0x2028:
    case 0x2029:
    case 0x202f:
    case 0x205f:
    case 0x3000:
    /* found */
    break;
    default:
    return Qfalse;
    }
    s += n;
    }
    return Qtrue;
    }

    View full-size slide

  48. static VALUE rb_str_blank(VALUE str) {
    rb_encoding *enc;
    char *s, *e;
    enc = STR_ENC_GET(str);
    s = RSTRING_PTR(str);
    if (!s || RSTRING_LEN(str) == 0) return Qtrue;
    e = RSTRING_END(str);
    while (s < e) {
    int n;
    unsigned int cc = rb_enc_codepoint_len(s, e, &n, enc);
    switch (cc) {
    case 9:
    case 0xa:
    case 0xb:
    case 0xc:
    case 0xd:
    case 0x20:
    case 0x85:
    case 0xa0:
    case 0x1680:
    case 0x2000:
    case 0x2001:
    case 0x2002:
    case 0x2003:
    case 0x2004:
    case 0x2005:
    case 0x2006:
    case 0x2007:
    case 0x2008:
    case 0x2009:
    case 0x200a:
    case 0x2028:
    case 0x2029:
    case 0x202f:
    case 0x205f:
    case 0x3000:
    /* found */
    break;
    default:
    return Qfalse;
    }
    s += n;
    }
    return Qtrue;
    }
    static VALUE rb_str_blank(VALUE str) {
    }

    View full-size slide

  49. static VALUE rb_str_blank(VALUE str) {
    rb_encoding *enc;
    char *s, *e;
    enc = STR_ENC_GET(str);
    s = RSTRING_PTR(str);
    if (!s || RSTRING_LEN(str) == 0) return Qtrue;
    e = RSTRING_END(str);
    while (s < e) {
    int n;
    unsigned int cc = rb_enc_codepoint_len(s, e, &n, enc);
    switch (cc) {
    case 9:
    case 0xa:
    case 0xb:
    case 0xc:
    case 0xd:
    case 0x20:
    case 0x85:
    case 0xa0:
    case 0x1680:
    case 0x2000:
    case 0x2001:
    case 0x2002:
    case 0x2003:
    case 0x2004:
    case 0x2005:
    case 0x2006:
    case 0x2007:
    case 0x2008:
    case 0x2009:
    case 0x200a:
    case 0x2028:
    case 0x2029:
    case 0x202f:
    case 0x205f:
    case 0x3000:
    /* found */
    break;
    default:
    return Qfalse;
    }
    s += n;
    }
    return Qtrue;
    }
    static VALUE rb_str_blank(VALUE str) {
    // …boilerplate…
    }

    View full-size slide

  50. static VALUE rb_str_blank(VALUE str) {
    rb_encoding *enc;
    char *s, *e;
    enc = STR_ENC_GET(str);
    s = RSTRING_PTR(str);
    if (!s || RSTRING_LEN(str) == 0) return Qtrue;
    e = RSTRING_END(str);
    while (s < e) {
    int n;
    unsigned int cc = rb_enc_codepoint_len(s, e, &n, enc);
    switch (cc) {
    case 9:
    case 0xa:
    case 0xb:
    case 0xc:
    case 0xd:
    case 0x20:
    case 0x85:
    case 0xa0:
    case 0x1680:
    case 0x2000:
    case 0x2001:
    case 0x2002:
    case 0x2003:
    case 0x2004:
    case 0x2005:
    case 0x2006:
    case 0x2007:
    case 0x2008:
    case 0x2009:
    case 0x200a:
    case 0x2028:
    case 0x2029:
    case 0x202f:
    case 0x205f:
    case 0x3000:
    /* found */
    break;
    default:
    return Qfalse;
    }
    s += n;
    }
    return Qtrue;
    }
    static VALUE rb_str_blank(VALUE str) {
    // …boilerplate…
    if (!s || RSTRING_LEN(str) == 0) return Qtrue;
    }

    View full-size slide

  51. static VALUE rb_str_blank(VALUE str) {
    rb_encoding *enc;
    char *s, *e;
    enc = STR_ENC_GET(str);
    s = RSTRING_PTR(str);
    if (!s || RSTRING_LEN(str) == 0) return Qtrue;
    e = RSTRING_END(str);
    while (s < e) {
    int n;
    unsigned int cc = rb_enc_codepoint_len(s, e, &n, enc);
    switch (cc) {
    case 9:
    case 0xa:
    case 0xb:
    case 0xc:
    case 0xd:
    case 0x20:
    case 0x85:
    case 0xa0:
    case 0x1680:
    case 0x2000:
    case 0x2001:
    case 0x2002:
    case 0x2003:
    case 0x2004:
    case 0x2005:
    case 0x2006:
    case 0x2007:
    case 0x2008:
    case 0x2009:
    case 0x200a:
    case 0x2028:
    case 0x2029:
    case 0x202f:
    case 0x205f:
    case 0x3000:
    /* found */
    break;
    default:
    return Qfalse;
    }
    s += n;
    }
    return Qtrue;
    }
    static VALUE rb_str_blank(VALUE str) {
    // …boilerplate…
    if (!s || RSTRING_LEN(str) == 0) return Qtrue;
    while(s < RSTRING_END(str)) {
    s++;
    }
    }

    View full-size slide

  52. static VALUE rb_str_blank(VALUE str) {
    rb_encoding *enc;
    char *s, *e;
    enc = STR_ENC_GET(str);
    s = RSTRING_PTR(str);
    if (!s || RSTRING_LEN(str) == 0) return Qtrue;
    e = RSTRING_END(str);
    while (s < e) {
    int n;
    unsigned int cc = rb_enc_codepoint_len(s, e, &n, enc);
    switch (cc) {
    case 9:
    case 0xa:
    case 0xb:
    case 0xc:
    case 0xd:
    case 0x20:
    case 0x85:
    case 0xa0:
    case 0x1680:
    case 0x2000:
    case 0x2001:
    case 0x2002:
    case 0x2003:
    case 0x2004:
    case 0x2005:
    case 0x2006:
    case 0x2007:
    case 0x2008:
    case 0x2009:
    case 0x200a:
    case 0x2028:
    case 0x2029:
    case 0x202f:
    case 0x205f:
    case 0x3000:
    /* found */
    break;
    default:
    return Qfalse;
    }
    s += n;
    }
    return Qtrue;
    }
    static VALUE rb_str_blank(VALUE str) {
    // …boilerplate…
    if (!s || RSTRING_LEN(str) == 0) return Qtrue;
    while(s < RSTRING_END(str)) {
    // …boilerplate…
    s++;
    }
    }

    View full-size slide

  53. static VALUE rb_str_blank(VALUE str) {
    rb_encoding *enc;
    char *s, *e;
    enc = STR_ENC_GET(str);
    s = RSTRING_PTR(str);
    if (!s || RSTRING_LEN(str) == 0) return Qtrue;
    e = RSTRING_END(str);
    while (s < e) {
    int n;
    unsigned int cc = rb_enc_codepoint_len(s, e, &n, enc);
    switch (cc) {
    case 9:
    case 0xa:
    case 0xb:
    case 0xc:
    case 0xd:
    case 0x20:
    case 0x85:
    case 0xa0:
    case 0x1680:
    case 0x2000:
    case 0x2001:
    case 0x2002:
    case 0x2003:
    case 0x2004:
    case 0x2005:
    case 0x2006:
    case 0x2007:
    case 0x2008:
    case 0x2009:
    case 0x200a:
    case 0x2028:
    case 0x2029:
    case 0x202f:
    case 0x205f:
    case 0x3000:
    /* found */
    break;
    default:
    return Qfalse;
    }
    s += n;
    }
    return Qtrue;
    }
    static VALUE rb_str_blank(VALUE str) {
    // …boilerplate…
    if (!s || RSTRING_LEN(str) == 0) return Qtrue;
    while(s < RSTRING_END(str)) {
    // …boilerplate…
    switch (s) {
    }
    s++;
    }
    }

    View full-size slide

  54. static VALUE rb_str_blank(VALUE str) {
    rb_encoding *enc;
    char *s, *e;
    enc = STR_ENC_GET(str);
    s = RSTRING_PTR(str);
    if (!s || RSTRING_LEN(str) == 0) return Qtrue;
    e = RSTRING_END(str);
    while (s < e) {
    int n;
    unsigned int cc = rb_enc_codepoint_len(s, e, &n, enc);
    switch (cc) {
    case 9:
    case 0xa:
    case 0xb:
    case 0xc:
    case 0xd:
    case 0x20:
    case 0x85:
    case 0xa0:
    case 0x1680:
    case 0x2000:
    case 0x2001:
    case 0x2002:
    case 0x2003:
    case 0x2004:
    case 0x2005:
    case 0x2006:
    case 0x2007:
    case 0x2008:
    case 0x2009:
    case 0x200a:
    case 0x2028:
    case 0x2029:
    case 0x202f:
    case 0x205f:
    case 0x3000:
    /* found */
    break;
    default:
    return Qfalse;
    }
    s += n;
    }
    return Qtrue;
    }
    static VALUE rb_str_blank(VALUE str) {
    // …boilerplate…
    if (!s || RSTRING_LEN(str) == 0) return Qtrue;
    while(s < RSTRING_END(str)) {
    // …boilerplate…
    switch (cc) {
    case 9: case 0xa: … case 0x205f: case 0x3000:
    // whitespace
    break;
    }
    s++;
    }
    }

    View full-size slide

  55. static VALUE rb_str_blank(VALUE str) {
    rb_encoding *enc;
    char *s, *e;
    enc = STR_ENC_GET(str);
    s = RSTRING_PTR(str);
    if (!s || RSTRING_LEN(str) == 0) return Qtrue;
    e = RSTRING_END(str);
    while (s < e) {
    int n;
    unsigned int cc = rb_enc_codepoint_len(s, e, &n, enc);
    switch (cc) {
    case 9:
    case 0xa:
    case 0xb:
    case 0xc:
    case 0xd:
    case 0x20:
    case 0x85:
    case 0xa0:
    case 0x1680:
    case 0x2000:
    case 0x2001:
    case 0x2002:
    case 0x2003:
    case 0x2004:
    case 0x2005:
    case 0x2006:
    case 0x2007:
    case 0x2008:
    case 0x2009:
    case 0x200a:
    case 0x2028:
    case 0x2029:
    case 0x202f:
    case 0x205f:
    case 0x3000:
    /* found */
    break;
    default:
    return Qfalse;
    }
    s += n;
    }
    return Qtrue;
    }
    static VALUE rb_str_blank(VALUE str) {
    // …boilerplate…
    if (!s || RSTRING_LEN(str) == 0) return Qtrue;
    while(s < RSTRING_END(str)) {
    // …boilerplate…
    switch (cc) {
    case 9: case 0xa: … case 0x205f: case 0x3000:
    // whitespace
    break;
    default:
    return Qfalse;
    }
    s++;
    }
    }

    View full-size slide

  56. static VALUE rb_str_blank(VALUE str) {
    rb_encoding *enc;
    char *s, *e;
    enc = STR_ENC_GET(str);
    s = RSTRING_PTR(str);
    if (!s || RSTRING_LEN(str) == 0) return Qtrue;
    e = RSTRING_END(str);
    while (s < e) {
    int n;
    unsigned int cc = rb_enc_codepoint_len(s, e, &n, enc);
    switch (cc) {
    case 9:
    case 0xa:
    case 0xb:
    case 0xc:
    case 0xd:
    case 0x20:
    case 0x85:
    case 0xa0:
    case 0x1680:
    case 0x2000:
    case 0x2001:
    case 0x2002:
    case 0x2003:
    case 0x2004:
    case 0x2005:
    case 0x2006:
    case 0x2007:
    case 0x2008:
    case 0x2009:
    case 0x200a:
    case 0x2028:
    case 0x2029:
    case 0x202f:
    case 0x205f:
    case 0x3000:
    /* found */
    break;
    default:
    return Qfalse;
    }
    s += n;
    }
    return Qtrue;
    }
    static VALUE rb_str_blank(VALUE str) {
    // …boilerplate…
    if (!s || RSTRING_LEN(str) == 0) return Qtrue;
    while(s < RSTRING_END(str)) {
    // …boilerplate…
    switch (cc) {
    case 9: case 0xa: … case 0x205f: case 0x3000:
    // whitespace
    break;
    default:
    return Qfalse;
    }
    s++;
    }
    return Qtrue;
    }

    View full-size slide

  57. FAST_BLANK IN RUST

    View full-size slide

  58. pub extern "C" fn fast_blank(buf: Buf) -> bool {
    buf.as_slice().chars().all(|c| c.is_whitespace())
    }

    View full-size slide

  59. pub extern "C" fn fast_blank(buf: Buf) -> bool {
    buf.as_slice().chars().all(|c| c.is_whitespace())
    }

    View full-size slide

  60. pub extern "C" fn fast_blank(buf: Buf) -> bool {
    buf.as_slice().chars().all(|c| c.is_whitespace())
    }

    View full-size slide

  61. pub extern "C" fn fast_blank(buf: Buf) -> bool {
    buf.as_slice().chars().all(|c| c.is_whitespace())
    }

    View full-size slide

  62. pub extern "C" fn fast_blank(buf: Buf) -> bool {
    buf.as_slice().chars().all(|c| c.is_whitespace())
    }

    View full-size slide

  63. pub extern "C" fn fast_blank(buf: Buf) -> bool {
    buf.as_slice().chars().all(|c| c.is_whitespace())
    }

    View full-size slide

  64. pub extern "C" fn fast_blank(buf: Buf) -> bool {
    buf.as_slice().chars().all(|c| c.is_whitespace())
    }

    View full-size slide

  65. pub extern "C" fn fast_blank(buf: Buf) -> bool {
    buf.as_slice().chars().all(|c| c.is_whitespace())
    }

    View full-size slide

  66. =========== Test String Length: 6 ===========
    Rust 11.043M (± 3.5%) i/s - 54.744M
    C 10.583M (± 8.5%) i/s - 52.464M
    Ruby 964.469k (±27.6%) i/s - 4.390M

    View full-size slide

  67. =========== Test String Length: 6 ===========
    Rust 11.043M (± 3.5%) i/s - 54.744M
    C 10.583M (± 8.5%) i/s - 52.464M
    Ruby 964.469k (±27.6%) i/s - 4.390M

    View full-size slide

  68. =========== Test String Length: 6 ===========
    Rust 11.043M (± 3.5%) i/s - 54.744M
    C 10.583M (± 8.5%) i/s - 52.464M
    Ruby 964.469k (±27.6%) i/s - 4.390M

    View full-size slide

  69. pub extern "C" fn fast_blank(buf: Buf) -> bool {
    buf.as_slice().chars().all(|c| c.is_whitespace())
    }

    View full-size slide

  70. pub extern "C" fn fast_blank(buf: Buf) -> bool {
    buf.as_slice().chars().all(|c| c.is_whitespace())
    }
    *

    View full-size slide

  71. RUST C
    // Rust Code
    extern crate libc;
    use std::{ptr, slice};
    use std::marker::PhantomData;
    #[repr(C)]
    #[allow(raw_pointer_derive)]
    #[derive(Copy, Clone)]
    pub struct Buf<'a> {
    ptr: *mut u8,
    len: usize,
    marker: PhantomData<&'a ()>,
    }
    impl<'a> Buf<'a> {
    pub fn as_slice(self) -> &'a str {
    unsafe {
    let s = slice::from_raw_parts(self.ptr as *const u8, self.len);
    std::str::from_utf8_unchecked(s)
    }
    }
    }
    #[no_mangle]
    pub extern "C" fn fast_blank(buf: Buf) -> bool {
    buf.as_slice().unwrap().chars().all(|c| c.is_whitespace())
    }
    // C Code
    #include
    #include
    typedef struct {
    void* data;
    size_t len;
    } fast_blank_buf_t;
    static inline fast_blank_buf_t
    STR2BUF(VALUE str) {
    return (fast_blank_buf_t) {
    .data = RSTRING_PTR(str),
    .len = RSTRING_LEN(str),
    };
    }
    int fast_blank(fast_blank_buf_t);
    static VALUE
    rb_string_blank_p(VALUE str) {
    return fast_blank(STR2BUF(str)) ? Qtrue : Qfalse;
    }
    void Init_fast_blank()
    {
    rb_define_method(rb_cString, "blank?", rb_string_blank_p, 0);
    }
    #include
    #include
    #include
    #include
    #define STR_ENC_GET(str) rb_enc_from_index(ENCODING_GET(str))
    static VALUE
    rb_string_blank(VALUE str)
    {
    rb_encoding *enc;
    char *s, *e;
    enc = STR_ENC_GET(str);
    s = RSTRING_PTR(str);
    if (!s || RSTRING_LEN(str) == 0) return Qtrue;
    e = RSTRING_END(str);
    while (s < e) {
    int n;
    unsigned int cc = rb_enc_codepoint_len(s, e, &n, enc);
    switch (cc) {
    case 9:
    case 0xa:
    case 0xb:
    case 0xc:
    case 0xd:
    case 0x20:
    case 0x85:
    case 0xa0:
    case 0x1680:
    case 0x2000:
    case 0x2001:
    case 0x2002:
    case 0x2003:
    case 0x2004:
    case 0x2005:
    case 0x2006:
    case 0x2007:
    case 0x2008:
    case 0x2009:
    case 0x200a:
    case 0x2028:
    case 0x2029:
    case 0x202f:
    case 0x205f:
    case 0x3000:
    break;
    default:
    return Qfalse;
    }
    s += n;
    }
    return Qtrue;
    }
    void Init_fast_blank()
    {
    rb_define_method(rb_cString, "blank?", rb_string_blank, 0);
    }

    View full-size slide

  72. RUST C
    // Rust Code
    extern crate libc;
    use std::{ptr, slice};
    use std::marker::PhantomData;
    #[repr(C)]
    #[allow(raw_pointer_derive)]
    #[derive(Copy, Clone)]
    pub struct Buf<'a> {
    ptr: *mut u8,
    len: usize,
    marker: PhantomData<&'a ()>,
    }
    impl<'a> Buf<'a> {
    pub fn as_slice(self) -> &'a str {
    unsafe {
    let s = slice::from_raw_parts(self.ptr as *const u8, self.len);
    std::str::from_utf8_unchecked(s)
    }
    }
    }
    #[no_mangle]
    pub extern "C" fn fast_blank(buf: Buf) -> bool {
    buf.as_slice().unwrap().chars().all(|c| c.is_whitespace())
    }
    // C Code
    #include
    #include
    typedef struct {
    void* data;
    size_t len;
    } fast_blank_buf_t;
    static inline fast_blank_buf_t
    STR2BUF(VALUE str) {
    return (fast_blank_buf_t) {
    .data = RSTRING_PTR(str),
    .len = RSTRING_LEN(str),
    };
    }
    int fast_blank(fast_blank_buf_t);
    static VALUE
    rb_string_blank_p(VALUE str) {
    return fast_blank(STR2BUF(str)) ? Qtrue : Qfalse;
    }
    void Init_fast_blank()
    {
    rb_define_method(rb_cString, "blank?", rb_string_blank_p, 0);
    }
    #include
    #include
    #include
    #include
    #define STR_ENC_GET(str) rb_enc_from_index(ENCODING_GET(str))
    static VALUE
    rb_string_blank(VALUE str)
    {
    rb_encoding *enc;
    char *s, *e;
    enc = STR_ENC_GET(str);
    s = RSTRING_PTR(str);
    if (!s || RSTRING_LEN(str) == 0) return Qtrue;
    e = RSTRING_END(str);
    while (s < e) {
    int n;
    unsigned int cc = rb_enc_codepoint_len(s, e, &n, enc);
    switch (cc) {
    case 9:
    case 0xa:
    case 0xb:
    case 0xc:
    case 0xd:
    case 0x20:
    case 0x85:
    case 0xa0:
    case 0x1680:
    case 0x2000:
    case 0x2001:
    case 0x2002:
    case 0x2003:
    case 0x2004:
    case 0x2005:
    case 0x2006:
    case 0x2007:
    case 0x2008:
    case 0x2009:
    case 0x200a:
    case 0x2028:
    case 0x2029:
    case 0x202f:
    case 0x205f:
    case 0x3000:
    break;
    default:
    return Qfalse;
    }
    s += n;
    }
    return Qtrue;
    }
    void Init_fast_blank()
    {
    rb_define_method(rb_cString, "blank?", rb_string_blank, 0);
    }

    View full-size slide

  73. #[macro_use]
    extern crate libcruby;
    declare_types! {
    reopen class RubyString {
    def is_blank(self) -> bool {
    self.chars().all(|c| c.is_whitespace())
    }
    }
    }

    View full-size slide

  74. #[macro_use]
    extern crate libcruby;
    declare_types! {
    reopen class RubyString {
    def is_blank(self) -> bool {
    self.chars().all(|c| c.is_whitespace())
    }
    }
    }

    View full-size slide

  75. #[macro_use]
    extern crate libcruby;
    declare_types! {
    reopen class RubyString {
    def is_blank(self) -> bool {
    self.chars().all(|c| c.is_whitespace())
    }
    }
    }

    View full-size slide

  76. #[macro_use]
    extern crate libcruby;
    declare_types! {
    reopen class RubyString {
    def is_blank(self) -> bool {
    self.chars().all(|c| c.is_whitespace())
    }
    }
    }

    View full-size slide

  77. #[macro_use]
    extern crate libcruby;
    declare_types! {
    reopen class RubyString {
    def is_blank(self) -> bool {
    self.chars().all(|c| c.is_whitespace())
    }
    }
    }

    View full-size slide

  78. TURBO RAILS IN RUST?

    View full-size slide

  79. WHAT ABOUT MY APP?

    View full-size slide

  80. MEAL-MATCHING ALGORITHM

    View full-size slide

  81. meal_tags = [3, 6, 18, 34, 50]
    preference_tags = [6, 18, 42]
    meal_tags.fully_contain?(preference_tags)

    View full-size slide

  82. meal_tags = [3, 6, 18, 34, 50]
    preference_tags = [6, 18, 42]
    meal_tags.fully_contain?(preference_tags)

    View full-size slide

  83. meal_tags = [3, 6, 18, 34, 50]
    preference_tags = [6, 18, 42]
    meal_tags.fully_contain?(preference_tags)

    View full-size slide

  84. meal_tags = [3, 6, 18, 34, 50]
    preference_tags = [6, 18, 42]
    meal_tags.fully_contain?(preference_tags)

    View full-size slide

  85. SET CONTAINMENT

    View full-size slide

  86. KNOWN TRICKTM

    View full-size slide

  87. class Array
    def fully_contain?(other)
    self & other == other
    end
    end

    View full-size slide

  88. class Array
    def fully_contain?(other)
    self & other == other
    end
    end

    View full-size slide

  89. class Array
    def fully_contain?(other)
    self & other == other
    end
    end

    View full-size slide

  90. class Array
    def fully_contain?(other)
    self & other == other
    end
    end

    View full-size slide

  91. WE CAN DO BETTER!

    View full-size slide

  92. class Array
    def fully_contain?(needle)
    return true if needle.empty?
    return false if self.empty?
    needle_length = needle.length
    return false if needle_length > self.length
    needle_position = 0
    needle_item = needle[needle_position]
    self.each do |item|
    if item == needle_item
    needle_position += 1
    if needle_position >= needle_length
    return true
    else
    needle_item = needle[needle_position]
    end
    end
    end
    false
    end
    end

    View full-size slide

  93. class Array
    def fully_contain?(needle)
    return true if needle.empty?
    return false if self.empty?
    needle_length = needle.length
    return false if needle_length > self.length
    needle_position = 0
    needle_item = needle[needle_position]
    self.each do |item|
    if item == needle_item
    needle_position += 1
    if needle_position >= needle_length
    return true
    else
    needle_item = needle[needle_position]
    end
    end
    end
    false
    end
    end

    View full-size slide

  94. class Array
    def fully_contain?(needle)
    return true if needle.empty?
    return false if self.empty?
    needle_length = needle.length
    return false if needle_length > self.length
    needle_position = 0
    needle_item = needle[needle_position]
    self.each do |item|
    if item == needle_item
    needle_position += 1
    if needle_position >= needle_length
    return true
    else
    needle_item = needle[needle_position]
    end
    end
    end
    false
    end
    end

    View full-size slide

  95. class Array
    def fully_contain?(needle)
    return true if needle.empty?
    return false if self.empty?
    needle_length = needle.length
    return false if needle_length > self.length
    needle_position = 0
    needle_item = needle[needle_position]
    self.each do |item|
    if item == needle_item
    needle_position += 1
    if needle_position >= needle_length
    return true
    else
    needle_item = needle[needle_position]
    end
    end
    end
    false
    end
    end

    View full-size slide

  96. class Array
    def fully_contain?(needle)
    return true if needle.empty?
    return false if self.empty?
    needle_length = needle.length
    return false if needle_length > self.length
    needle_position = 0
    needle_item = needle[needle_position]
    self.each do |item|
    if item == needle_item
    needle_position += 1
    if needle_position >= needle_length
    return true
    else
    needle_item = needle[needle_position]
    end
    end
    end
    false
    end
    end

    View full-size slide

  97. UP TO 7X FASTER

    View full-size slide

  98. #[macro_use]
    extern crate libcruby;
    declare_types! {
    reopen class Array {
    def fully_contain(self, needle: &[usize]) -> bool {
    if needle.is_empty() { return true }
    let haystack = self.as_ref();
    if haystack.is_empty() { return false }
    let mut needle = needle.iter();
    let mut needle_item = needle.next().unwrap();
    for item in haystack {
    if item == needle_item {
    match needle.next() {
    None => return true,
    Some(next_item) => needle_item = next_item
    }
    }
    }
    false
    }
    }
    }

    View full-size slide

  99. #[macro_use]
    extern crate libcruby;
    declare_types! {
    reopen class Array {
    def fully_contain(self, needle: &[usize]) -> bool {
    if needle.is_empty() { return true }
    let haystack = self.as_ref();
    if haystack.is_empty() { return false }
    let mut needle = needle.iter();
    let mut needle_item = needle.next().unwrap();
    for item in haystack {
    if item == needle_item {
    match needle.next() {
    None => return true,
    Some(next_item) => needle_item = next_item
    }
    }
    }
    false
    }
    }
    }
    \

    View full-size slide

  100. #[macro_use]
    extern crate libcruby;
    declare_types! {
    reopen class Array {
    def fully_contain(self, needle: &[usize]) -> bool {
    if needle.is_empty() { return true }
    let haystack = self.as_ref();
    if haystack.is_empty() { return false }
    let mut needle = needle.iter();
    let mut needle_item = needle.next().unwrap();
    for item in haystack {
    if item == needle_item {
    match needle.next() {
    None => return true,
    Some(next_item) => needle_item = next_item
    }
    }
    }
    false
    }
    }
    }

    View full-size slide

  101. #[macro_use]
    extern crate libcruby;
    declare_types! {
    reopen class Array {
    def fully_contain(self, needle: &[usize]) -> bool {
    if needle.is_empty() { return true }
    let haystack = self.as_ref();
    if haystack.is_empty() { return false }
    let mut needle = needle.iter();
    let mut needle_item = needle.next().unwrap();
    for item in haystack {
    if item == needle_item {
    match needle.next() {
    None => return true,
    Some(next_item) => needle_item = next_item
    }
    }
    }
    false
    }
    }
    }

    View full-size slide

  102. #[macro_use]
    extern crate libcruby;
    declare_types! {
    reopen class Array {
    def fully_contain(self, needle: &[usize]) -> bool {
    if needle.is_empty() { return true }
    let haystack = self.as_ref();
    if haystack.is_empty() { return false }
    let mut needle = needle.iter();
    let mut needle_item = needle.next().unwrap();
    for item in haystack {
    if item == needle_item {
    match needle.next() {
    None => return true,
    Some(next_item) => needle_item = next_item
    }
    }
    }
    false
    }
    }
    }

    View full-size slide

  103. #[macro_use]
    extern crate libcruby;
    declare_types! {
    reopen class Array {
    def fully_contain(self, needle: &[usize]) -> bool {
    if needle.is_empty() { return true }
    let haystack = self.as_ref();
    if haystack.is_empty() { return false }
    let mut needle = needle.iter();
    let mut needle_item = needle.next().unwrap();
    for item in haystack {
    if item == needle_item {
    match needle.next() {
    None => return true,
    Some(next_item) => needle_item = next_item
    }
    }
    }
    false
    }
    }
    }

    View full-size slide

  104. #[macro_use]
    extern crate libcruby;
    declare_types! {
    reopen class Array {
    def fully_contain(self, needle: &[usize]) -> bool {
    if needle.is_empty() { return true }
    let haystack = self.as_ref();
    if haystack.is_empty() { return false }
    let mut needle = needle.iter();
    let mut needle_item = needle.next().unwrap();
    for item in haystack {
    if item == needle_item {
    match needle.next() {
    None => return true,
    Some(next_item) => needle_item = next_item
    }
    }
    }
    false
    }
    }
    }

    View full-size slide

  105. #[macro_use]
    extern crate libcruby;
    declare_types! {
    reopen class Array {
    def fully_contain(self, needle: &[usize]) -> bool {
    if needle.is_empty() { return true }
    let haystack = self.as_ref();
    if haystack.is_empty() { return false }
    let mut needle = needle.iter();
    let mut needle_item = needle.next().unwrap();
    for item in haystack {
    if item == needle_item {
    match needle.next() {
    None => return true,
    Some(next_item) => needle_item = next_item
    }
    }
    }
    false
    }
    }
    }

    View full-size slide

  106. UP TO 173X FASTER

    View full-size slide

  107. Ruby (Naive) Ruby (Fast) Rust

    View full-size slide

  108. ! rustbridge/helix

    View full-size slide

  109. HACK WITHOUT FEAR

    View full-size slide

  110. RUBY WITHOUT FEAR

    View full-size slide

  111. @chancancode
    GODFREY CHAN

    View full-size slide

  112. #MAKERUBYGREATAGAIN

    View full-size slide