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

Crystal & Ruby - Applying learnings from a compiled language

Crystal & Ruby - Applying learnings from a compiled language

We all enjoy the flexibility and expressiveness that languages like Ruby give us, but such flexibility is also paired with the unknown. Dealing with dynamic data and runtime conditions can lead to unknown scenarios and runtime errors.

Compiled languages, specially today offer a fresh perspective and help us developers better understand and shape our code.

This talk aims to discover certain techniques transferred from compiled languages’ realm and transfer those to dynamic languages like Ruby. Will talk from my experience working on mid-size Ruby projects (8K+ LOC).

Presented at Paris.rb Conference, June 2018

Luis Lavena

June 28, 2018
Tweet

More Decks by Luis Lavena

Other Decks in Programming

Transcript

  1. Crystal & Ruby Applying learnings from a compiled language

  2. Luis Lavena Twitter: @luislavena GitHub: luislavena

  3. None
  4. None
  5. area17.com

  6. WARNING: Technical talk ahead Important announcement follows...

  7. vs.

  8. None
  9. Compiled vs. Interpreted

  10. Ruby VM require Read source Tokenize (lexer) Build AST (parser)

    Build ISeq Execute ISeq Ruby
  11. OS Read source Tokenize (lexer) Build AST (parser) Codegen Execute

    Native Code (obj) Link Compilers Analyze/Infer/Optimize
  12. None
  13. None
  14. None
  15. Crystal Language • Ruby-like syntax • Types and Inference •

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

    Generics • Macros • Self-hosted • LLVM-powered • Native, single-executable
  17. Types and Inference

  18. 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)
  19. 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
  20. Methods and inference def work(worker) worker.something! end work Worker.new #

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

    work Worker.new # OK work nil # => nil
  22. Methods & signatures def add(one : Int32, two : Int32)

    one + two end add 30, 12 # Int32, Int32 => Int32 add "a", "b" # Compile error
  23. 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/
  24. None
  25. Code techniques

  26. Code techniques Acknowledge that nil exists

  27. 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
  28. Code techniques Acknowledge that nil exists ✔ Accessing variables across

    threads
  29. 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
  30. Code techniques Acknowledge that nil exists ✔ Accessing variables across

    threads ✔
  31. Coding resources • Ruby typechecker: Sorbet https://sorbet.run/

  32. Optimization techniques

  33. Optimization techniques Load time

  34. require Read source Tokenize (lexer) Build AST (parser) Build ISeq

    Execute ISeq Ruby VM Memory I/O
  35. $ 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
  36. $ ruby -e 'RubyVM::InstructionSequence.load_from_binary(File.binread ("example.yarb")).eval' 42

  37. require Read source Tokenize (lexer) Build AST (parser) Build ISeq

    Execute ISeq Ruby VM I/O Read bytecode
  38. Bootsnap • Compiles .rb into YARB bytecode • Hooks require

    and loads instead of parsing • Optimizes lookup of $LOAD_PATH
  39. ~35% Load time speed up using Bootsnap Mid-size Rails app

    (~6k LOC)
  40. Optimization techniques Load time ✔

  41. Optimization techniques Load time ✔ Installation time and dependencies

  42. None
  43. UPDATING NOKOGIRI!!!

  44. But, what if there is an alternative?

  45. $ 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
  46. 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)
  47. Optimization techniques Load time ✔ Installation time and dependencies ✔

    Code-reviews and code style
  48. “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
  49. Formatting code • Reduces bikeshedding • Consistent readability following official

    styleguides • Improves contribution • Broad implementation and success on other languages: Golang, Rust, Elixir • Proactive vs. Passive
  50. $ 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
  51. Optimization techniques Load time ✔ Installation time and dependencies ✔

    Code-reviews and code style ✔
  52. 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
  53. None
  54. Thank you!