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. View Slide

  2. WELCOME TO KANSAS

    View Slide

  3. WIZARD OF orz

    View Slide

  4. View Slide

  5. View Slide

  6. KANSAS
    MISSOURI

    View Slide

  7. KANSAS
    MISSOURI
    KANSAS CITY

    View Slide

  8. KANSAS
    MISSOURI
    KANSAS CITY

    View Slide

  9. @chancancode
    GODFREY CHAN

    View Slide

  10. View Slide

  11. View Slide

  12. View Slide

  13. ! " # $ %

    View Slide

  14. RAILS 5

    View Slide

  15. PHP

    View Slide

  16. JavaScript

    View Slide

  17. gem install javascript

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  22. Everyone's favorite JavaScript feature?

    View Slide

  23. Callbacks

    View Slide

  24. Everyone's favorite Rails feature?

    View Slide

  25. Callbacks

    View Slide

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

    View Slide

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

    View Slide

  28. View Slide

  29. View Slide

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

    View Slide

  31. View Slide

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

    View Slide

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

    View Slide

  34. RUBY

    View Slide

  35. C

    View Slide

  36. Best of both worlds*
    NATIVE EXTENSIONS

    View Slide

  37. gem install json

    View Slide

  38. gem install json
    JSON::Pure
    Pure Ruby

    View Slide

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

    View Slide

  40. Best of both worlds*
    NATIVE EXTENSIONS

    View Slide

  41. Best of both worlds*
    NATIVE EXTENSIONS

    View Slide

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

    View Slide

  43. "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 Slide

  44. "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 Slide

  45. "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 Slide

  46. @SamSaffron

    View Slide

  47. String#blank?

    View Slide

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

    View Slide

  49. ! SamSaffron/fast_blank

    View Slide

  50. UP TO 20X FASTER*

    View Slide

  51. &
    ME

    View Slide

  52. &
    ME
    knows just enough C to be dangerous

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  57. '
    SEGV

    View Slide

  58. View Slide

  59. FEATHERWEIGHT
    AGENT

    View Slide

  60. RUST

    View Slide

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

    View Slide

  62. HACK WITHOUT FEAR

    View Slide

  63. 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 Slide

  64. without garbage collection
    MEMORY SAFETY

    View Slide

  65. without data races
    CONCURRENCY

    View Slide

  66. ZERO-COST ABSTRACTIONS

    View Slide

  67. View Slide

  68. BACK TO FAST_BLANK

    View Slide

  69. FAST_BLANK IN C

    View Slide

  70. 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 Slide

  71. 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 Slide

  72. 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 Slide

  73. 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 Slide

  74. 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 Slide

  75. 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 Slide

  76. 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 Slide

  77. 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 Slide

  78. 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 Slide

  79. 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 Slide

  80. FAST_BLANK IN RUST

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  89. =========== 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 Slide

  90. =========== 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 Slide

  91. =========== 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 Slide

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

    View Slide

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

    View Slide

  94. View Slide

  95. 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 Slide

  96. 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 Slide

  97. HELIX

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  103. TURBO RAILS IN RUST?

    View Slide

  104. WHAT ABOUT MY APP?

    View Slide

  105. View Slide

  106. MEAL-MATCHING ALGORITHM

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  111. SET CONTAINMENT

    View Slide

  112. KNOWN TRICKTM

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  117. SORTED

    View Slide

  118. UNIQUE

    View Slide

  119. (

    View Slide

  120. WE CAN DO BETTER!

    View Slide

  121. 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 Slide

  122. 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 Slide

  123. 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 Slide

  124. 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 Slide

  125. 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 Slide

  126. UP TO 7X FASTER

    View Slide

  127. RUST?

    View Slide

  128. #[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 Slide

  129. #[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 Slide

  130. #[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 Slide

  131. #[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 Slide

  132. #[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 Slide

  133. #[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 Slide

  134. #[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 Slide

  135. #[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 Slide

  136. UP TO 173X FASTER

    View Slide

  137. Ruby (Naive) Ruby (Fast) Rust

    View Slide

  138. ! rustbridge/helix

    View Slide

  139. HACK WITHOUT FEAR

    View Slide

  140. RUBY WITHOUT FEAR

    View Slide

  141. @chancancode
    GODFREY CHAN

    View Slide

  142. #MAKERUBYGREATAGAIN

    View Slide