Slide 1

Slide 1 text

Crystal & Ruby Applying learnings from a compiled language

Slide 2

Slide 2 text

Luis Lavena Twitter: @luislavena GitHub: luislavena

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

area17.com

Slide 6

Slide 6 text

WARNING: Technical talk ahead Important announcement follows...

Slide 7

Slide 7 text

vs.

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

Compiled vs. Interpreted

Slide 10

Slide 10 text

Ruby VM require Read source Tokenize (lexer) Build AST (parser) Build ISeq Execute ISeq Ruby

Slide 11

Slide 11 text

OS Read source Tokenize (lexer) Build AST (parser) Codegen Execute Native Code (obj) Link Compilers Analyze/Infer/Optimize

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

No content

Slide 14

Slide 14 text

No content

Slide 15

Slide 15 text

Crystal Language ● Ruby-like syntax ● Types and Inference ● Generics ● Macros ● Self-hosted ● LLVM-powered ● Native, single-executable

Slide 16

Slide 16 text

Crystal Language ● Ruby-like syntax ● Types and Inference ● Generics ● Macros ● Self-hosted ● LLVM-powered ● Native, single-executable

Slide 17

Slide 17 text

Types and Inference

Slide 18

Slide 18 text

Types and Inference a = [1, 2, 3].map { |x| x + 3 } pp a # => a = [4, 5, 6] pp typeof(a) # => Array(Int32) b = [1, "a", 'b'] pp typeof(b) # => Array(String | Int32 | Char) # b << 12.5 # => Compile error (Float32)

Slide 19

Slide 19 text

Methods and inference def add(one, two) one + two end add 30, 12 # Int32, Int32 => Int32 add "a", "b" # String, String => String add "a", 'b' # Compile error

Slide 20

Slide 20 text

Methods and inference def work(worker) worker.something! end work Worker.new # OK work nil # Compile error (no method `something!` for `nil`)

Slide 21

Slide 21 text

Methods and inference def work(worker) if worker worker.something! end end work Worker.new # OK work nil # => nil

Slide 22

Slide 22 text

Methods & signatures def add(one : Int32, two : Int32) one + two end add 30, 12 # Int32, Int32 => Int32 add "a", "b" # Compile error

Slide 23

Slide 23 text

Ruby ● Types syntax will be hard to implement ● Probably method signatures will be added in Ruby 3x3 ● Type checkers possible (ie. Sorbet by Stripe) https://sorbet.run/

Slide 24

Slide 24 text

No content

Slide 25

Slide 25 text

Code techniques

Slide 26

Slide 26 text

Code techniques Acknowledge that nil exists

Slide 27

Slide 27 text

def work(option) return option + 10 end work 5 # => 15 work nil # => Runtime Exception def work(option) if option return option + 10 end end work 5 # => 15 work nil # => nil Acknowledge that nil exists

Slide 28

Slide 28 text

Code techniques Acknowledge that nil exists ✔ Accessing variables across threads

Slide 29

Slide 29 text

class Worker def work if @condition @condition.something! end end end class Worker def work if value = @condition value.something! end end end Accessing variables across threads

Slide 30

Slide 30 text

Code techniques Acknowledge that nil exists ✔ Accessing variables across threads ✔

Slide 31

Slide 31 text

Coding resources ● Ruby typechecker: Sorbet https://sorbet.run/

Slide 32

Slide 32 text

Optimization techniques

Slide 33

Slide 33 text

Optimization techniques Load time

Slide 34

Slide 34 text

require Read source Tokenize (lexer) Build AST (parser) Build ISeq Execute ISeq Ruby VM Memory I/O

Slide 35

Slide 35 text

$ cat example.rb a = 30 puts a + 12 $ ruby -e 'File.binwrite("example.yarb", RubyVM::InstructionSequence.compile_file("example.rb").to_ binary)' $ ls -l total 4 -rw-r--r-- 1 luis luis 19 Jun 28 10:07 example.rb -rw-r--r-- 1 luis luis 818 Jun 28 10:07 example.yarb

Slide 36

Slide 36 text

$ ruby -e 'RubyVM::InstructionSequence.load_from_binary(File.binread ("example.yarb")).eval' 42

Slide 37

Slide 37 text

require Read source Tokenize (lexer) Build AST (parser) Build ISeq Execute ISeq Ruby VM I/O Read bytecode

Slide 38

Slide 38 text

Bootsnap ● Compiles .rb into YARB bytecode ● Hooks require and loads instead of parsing ● Optimizes lookup of $LOAD_PATH

Slide 39

Slide 39 text

~35% Load time speed up using Bootsnap Mid-size Rails app (~6k LOC)

Slide 40

Slide 40 text

Optimization techniques Load time ✔

Slide 41

Slide 41 text

Optimization techniques Load time ✔ Installation time and dependencies

Slide 42

Slide 42 text

No content

Slide 43

Slide 43 text

UPDATING NOKOGIRI!!!

Slide 44

Slide 44 text

But, what if there is an alternative?

Slide 45

Slide 45 text

$ time gem install nokogiri-1.8.3.gem … real 5m34.507s $ gem compile nokogiri-1.8.3.gem … $ time gem install nokogiri-1.8.3-x86_64-linux.gem … 1 gem installed real 0m4.947s

Slide 46

Slide 46 text

gem-compiler ● RubyGems plugin (`gem compile ...`) ● Pre-compile gems for the current platform ● Plays nice with Bundler (`bundle package` and `bundle install --local`) ● Reduces install time (compile nokogiri from ~5 mins to ~4 seconds) ● Reduces bandwidth usage (nokogiri from ~8.8MB to ~700KB)

Slide 47

Slide 47 text

Optimization techniques Load time ✔ Installation time and dependencies ✔ Code-reviews and code style

Slide 48

Slide 48 text

“This PR is excellent! Event the inconsistent style and spaces used for your hashes, the lack of parenthesis on methods and let’s not forget the usage of tabs!” - Anonymous Ruby developer on GitHub

Slide 49

Slide 49 text

Formatting code ● Reduces bikeshedding ● Consistent readability following official styleguides ● Improves contribution ● Broad implementation and success on other languages: Golang, Rust, Elixir ● Proactive vs. Passive

Slide 50

Slide 50 text

$ cat example.rb def hello name puts "Hello #{name}!!!" end $ rufo example.rb $ cat example.rb def hello(name) puts "Hello #{name}!!!" end rufo: Ruby formatter

Slide 51

Slide 51 text

Optimization techniques Load time ✔ Installation time and dependencies ✔ Code-reviews and code style ✔

Slide 52

Slide 52 text

Optimization resources ● Load time: Bootsnap http://github.com/Shopify/bootsnap ● Pre-compiled extensions: gem-compiler https://github.com/luislavena/gem-compiler ● Code formatting: rufo https://github.com/ruby-formatter/rufo

Slide 53

Slide 53 text

No content

Slide 54

Slide 54 text

Thank you!