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

RailsConf 2022 Keynote

RailsConf 2022 Keynote

This is my RailsConf 2022 keynote

F29327647a9cff5c69618bae420792ea?s=128

Aaron Patterson

June 01, 2022
Tweet

More Decks by Aaron Patterson

Other Decks in Technology

Transcript

  1. None
  2. RailsConf 2022 It's been a minute!

  3. PORTLAND

  4. None
  5. None
  6. None
  7. None
  8. None
  9. Couch Glisan

  10. None
  11. 3 YEARS!

  12. #FridayHug

  13. 🇺🇦🇺🇦🇺🇦

  14. None
  15. None
  16. RIP Little Buddy 😭

  17. None
  18. ~5 Photos / Day 🤣

  19. None
  20. None
  21. None
  22. None
  23. None
  24. None
  25. https://www.youtube.com/ TenderlovesCoolStuff Subscribe

  26. Tenderlove's Cool Stuff

  27. None
  28. ˑˑˑˑˑ 0 Stars: Do Not Recommend

  29. 💨

  30. None
  31. JIT: Generate Machine Code at Runtime

  32. Machine Code: Sequence of Bytes

  33. Ruby: Can Make a Sequence of Bytes ✅

  34. 🤔 Pure Ruby JIT? 🤔

  35. Yes! Yes, we can.

  36. https://github.com/tenderlove/tenderjit

  37. TenderJIT

  38. TenderJIT

  39. "Just because you can, doesn't mean you should!"

  40. Analog Terminal Bell https://analogterminalbell.com

  41. Professional Software Development

  42. None
  43. Apple M1: AArch64

  44. x86_64 Only

  45. LeJIT JITs

  46. AArch64 Gem https://github.com/tenderlove/aarch64

  47. AArch64 Example JIT a function that returns 0xF00DCAFE $ gel

    exec ruby test.rb "f00dcafe" Terminal Output require "aarch64" require "jit_buffer" # create a JIT buffer jit_buffer = JITBuffer.new 4096 asm = AArch64::Assembler.new # Make some instructions asm.pretty do asm.movz x0, 0xCAFE asm.movk x0, 0xF00D, lsl(16) asm.ret end # Write the instructions to a JIT buffer jit_buffer.writeable! asm.write_to jit_buffer jit_buffer.executable! # Execute the JIT buffer p jit_buffer.to_function([], -Fiddle::TYPE_INT).call.to_s(16) # => f00dcafe JIT Code
  48. AArch64 Example JIT a function that returns 0xF00DCAFE $ gel

    exec ruby test.rb "f00dcafe" Terminal Output asm = AArch64::Assembler.new # Make some instructions asm.pretty do asm.movz x0, 0xCAFE asm.movk x0, 0xF00D, lsl(16) asm.ret end JIT Code
  49. Not Amateur Enough

  50. Extremely Amateur

  51. AArch64 Example JIT a function that returns 0xF00DCAFE $ gel

    exec ruby test.rb "f00dcafe" Terminal Output require "aarch64" require "jit_buffer" # create a JIT buffer jit_buffer = JITBuffer.new 4096 asm = AArch64::Assembler.new # Make some instructions asm.pretty do asm.movz x0, 0xCAFE asm.movk x0, 0xF00D, lsl(16) asm.ret end # Write the instructions to a JIT buffer jit_buffer.writeable! asm.write_to jit_buffer jit_buffer.executable! # Execute the JIT buffer p jit_buffer.to_function([], -Fiddle::TYPE_INT).call.to_s(16) # => f00dcafe JIT Code
  52. AArch64 Example JIT a function that returns 0xF00DCAFE $ gel

    exec ruby test.rb "f00dcafe" Terminal Output require "aarch64" require "jit_buffer" # create a JIT buffer jit_buffer = JITBuffer.new 4096 asm = AArch64::Assembler.new # Make some instructions asm.pretty do asm.movz x0, 0xCAFE asm.movk x0, 0xF00D, lsl(16) asm.ret end # Write the instructions to a JIT buffer jit_buffer.writeable! asm.write_to jit_buffer jit_buffer.executable! # Execute the JIT buffer p jit_buffer.to_function([], -Fiddle::TYPE_INT).call.to_s(16) # => f00dcafe JIT Code JITBuffer.new
  53. JITBuffer: Executable Memory

  54. Linux / macOS

  55. Any Platform

  56. Test Code Execution Write some bytes and ensure it can

    execute them # create a JIT buffer jit_buffer = JITBuffer.new 4096 # Write the instructions to a JIT buffer jit_buffer.writeable! jit_buffer.write asm.to_binary jit_buffer.executable! # Execute the JIT buffer p jit_buffer.to_function([], -Fiddle::TYPE_INT).call
  57. Test on x86_64 and ARM

  58. Test Code Execution Add a check for the current platform

    # create a JIT buffer jit_buffer = JITBuffer.new 4096 # Write the instructions to a JIT buffer jit_buffer.writeable! if arm64? jit_buffer.write arm64_bytes else jit_buffer.write x86_bytes end jit_buffer.executable!
  59. Test Code Execution Add a check for the current platform

    # create a JIT buffer jit_buffer = JITBuffer.new 4096 # Write the instructions to a JIT buffer jit_buffer.writeable! if arm64? jit_buffer.write arm64_bytes else jit_buffer.write x86_bytes end jit_buffer.executable!
  60. If Statements Mean "Complexity"

  61. Not "Cool"

  62. Can we ask the CPU itself?

  63. Machine Code is Just Bytes

  64. Bytes Valid for x86_64 and ARM64?

  65. YES!

  66. Worst Best Idea Ever!

  67. x86 Machine Code Very Cool Code (Intel Syntax) .foo: mov

    rax, 0x2b ret jmp foo Put 0x2b in the "RAX" register Return from the current function Jump to the "foo" label???
  68. x86 Machine Code Bytes Bytes are in base 16 48

    C7 C0 2B 00 00 00 mov rax, 0x2b C3 ret EB F6 jmp foo [0x48, 0xc7, 0xc0, 0x2b, 0x00, 0x00, 0x00, 0xc3, 0xeb, 0xf6]
  69. ARM64 Machine Code Very Very Cool Code movz X11, 0x7b7

    movz X0, 0x2b ret Write 0x7b7 to X11 register because??? Write 0x2b to the X0 register Return from the current function
  70. ARM64 Machine Code Bytes Bytes are in base 16 EB

    F6 80 D2 movz x11, 0x7b7 60 05 80 D2 movz x0, 0x2b C0 03 5F D6 ret
  71. Machine Code for x86 and ARM64 x86_64 Machine Code 48

    C7 C0 2B 00 00 00 mov rax, 0x2b C3 ret EB F6 jmp foo ARM64 Machine Code EB F6 80 D2 movz x11, 0x7b7 60 05 80 D2 movz x0, 0x2b C0 03 5F D6 ret
  72. Machine Code for x86 and ARM64 x86_64 Machine Code 48

    C7 C0 2B 00 00 00 mov rax, 0x2b C3 ret EB F6 jmp foo ARM64 Machine Code EB F6 80 D2 movz x11, 0x7b7 60 05 80 D2 movz x0, 0x2b C0 03 5F D6 ret
  73. Machine Code for x86 and ARM64 48 C7 C0 2B

    00 00 00 mov rax, 0x2b C3 ret EB F6 jmp foo EB F6 80 D2 movz x11, 0x7b7 60 05 80 D2 movz x0, 0x2b C0 03 5F D6 ret
  74. Machine Code for x86 and ARM64 48 C7 C0 2B

    00 00 00 mov rax, 0x2b C3 ret EB F6 jmp foo EB F6 80 D2 60 05 80 D2 movz x0, 0x2b C0 03 5F D6 ret
  75. Machine Code for x86 and ARM64 48 C7 C0 2B

    00 00 00 mov rax, 0x2b C3 ret EB F6 EB F6 80 D2 movz x11, 0x7b7 60 05 80 D2 movz x0, 0x2b C0 03 5F D6 ret
  76. Both Return 0x2B

  77. JITBuffer Test Case VCP (Very Cool Program) jit = JITBuffer.new

    4096 bytes = [0x48, 0xc7, 0xc0, 0x2b, 0x00, 0x00, 0x00, # x86_64 mov rax, 0x2b 0xc3, # x86_64 ret 0xeb, 0xf6, # x86 jmp 0x80, 0xd2, # ARM movz X11, 0x7b7 0x60, 0x05, 0x80, 0xd2, # ARM movz X0, #0x2b 0xc0, 0x03, 0x5f, 0xd6] # ARM ret jit.writeable! jit.write bytes.pack("C*") jit.executable! func = Fiddle::Function.new(jit.to_i + 8, [], Fiddle::TYPE_INT) assert_equal 0x2b, func.call Start at offset 8 Offset 8 Alw ays 0x2b!!
  78. Cross Platform!!!!!* *x86_64 and ARM64 only

  79. No If Statements!

  80. Complexity: 0

  81. So Simple!

  82. "Just because you can, doesn't mean you should!"

  83. None
  84. Anatomy of a Tenderlove Talk

  85. Tenderlove Talk Timeline Teardown Jokes about local businesses Pictures of

    Cats W eird code not related to the topic M ain topic Abrupt ending after running out of tim e
  86. Talk Timeline Breakdown Jokes about local businesses Pictures of Cats

    W eird code not related to the topic M ain topic Abrupt ending after running out of tim e
  87. New Features in Rails 7!! Rack

  88. Spring It is gone!

  89. None
  90. New Features in Rails 7 Cool! • Import maps •

    Hotwire Default • Turbo 7 Default • Stimulus 3 Default • JS Bundling • CSS Bundling
  91. None
  92. I am not a Front End Developer

  93. No Idea what these words mean

  94. But It's OK

  95. Import Maps and ES Modules

  96. Sprockets Webpacker ESM + Import Maps

  97. 🫠🫠🫠🫠 "yay"

  98. New application.html.erb javascript_importmap_tags <!DOCTYPE html> <html> <head> <title>Myapp</title> <meta name="viewport"

    content="width=device-width,initial-scale=1"> <%= csrf_meta_tags %> <%= csp_meta_tag %> <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %> <%= javascript_importmap_tags %> </head> <body> <%= yield %> </body> </html>
  99. Define importmaps con fi g/importmap.rb # Pin npm packages by

    running ./bin/importmap pin "application", preload: true pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true pin_all_from "app/javascript/controllers", under: "controllers"
  100. Rendered Application Layout Import map automatically generated <script type="importmap" data-turbo-track="reload">{

    "imports": { "application": "/assets/application-123.js", "@hotwired/turbo-rails": "/assets/turbo.min-123.js", "@hotwired/stimulus": "/assets/stimulus.min-123.js", "@hotwired/stimulus-loading": "/assets/stimulus-loading-123.js", "controllers/application": "/assets/controllers/application-123.js", "controllers/hello_controller": "/assets/controllers/hello_controller-123.js", "controllers": "/assets/controllers/index-123.js" } }</script>
  101. Pinning Libraries

  102. Put a pin in it! Add stu ff to the

    import map $ bin/importmap pin lodash-es@4.17.21/startCase.js Pinning "lodash-es/startCase.js" to https://ga.jspm.io/npm:lodash-es@4.17.21/startCase.js es: Español
  103. New config/importmap.rb With lodash-es # Pin npm packages by running

    ./bin/importmap pin "application", preload: true pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true pin_all_from "app/javascript/controllers", under: "controllers" pin "lodash-es/startCase.js", to: "https://ga.jspm.io/npm:lodash-es@4.17.21/startCase.js"
  104. New config/importmap.rb With lodash-es # Pin npm packages by running

    ./bin/importmap pin "application", preload: true pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true pin_all_from "app/javascript/controllers", under: "controllers" pin "lodash-es/startCase.js", to: "https://ga.jspm.io/npm:lodash-es@4.17.21/startCase.js"
  105. New config/importmap.rb With lodash-es # Pin npm packages by running

    ./bin/importmap pin "application", preload: true pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true pin_all_from "app/javascript/controllers", under: "controllers" pin "@lodash/startCase", to: "https://ga.jspm.io/npm:lodash-es@4.17.21/startCase.js"
  106. New Sourcemap Has Lowdash! <script type="importmap" data-turbo-track="reload">{ "imports": { "application":

    "/assets/application-123.js", "@hotwired/turbo-rails": "/assets/turbo.min-123.js", "@hotwired/stimulus": "/assets/stimulus.min-123.js", "@hotwired/stimulus-loading": "/assets/stimulus-loading-123.js", "@lodash/startCase": "https://ga.jspm.io/npm:lodash-es@4.17.21/startCase.js", "controllers/application": "/assets/controllers/application-123.js", "controllers/hello_controller": "/assets/controllers/hello_controller-123.js", "controllers": "/assets/controllers/index-123.js" } }</script>
  107. New Sourcemap Has Lowdash! <script type="importmap" data-turbo-track="reload">{ "imports": { "application":

    "/assets/application-123.js", "@hotwired/turbo-rails": "/assets/turbo.min-123.js", "@hotwired/stimulus": "/assets/stimulus.min-123.js", "@hotwired/stimulus-loading": "/assets/stimulus-loading-123.js", "@lodash/startCase": "https://ga.jspm.io/npm:lodash-es@4.17.21/startCase.js", "controllers/application": "/assets/controllers/application-123.js", "controllers/hello_controller": "/assets/controllers/hello_controller-123.js", "controllers": "/assets/controllers/index-123.js" } }</script>
  108. Use the new module! <script type="module"> import startCase from '@lodash/startCase';

    const el = document.createElement('h1'); const words = "hello, mom!" const text = document.createTextNode(startCase(words)); el.appendChild(text); document.body.appendChild(el); </script>
  109. None
  110. Great!

  111. Indirect Names Use names rather than fi les <script type="importmap"

    data-turbo-track="reload">{ "imports": { "application": "/assets/application-123.js", "@hotwired/turbo-rails": "/assets/turbo.min-123.js", "@hotwired/stimulus": "/assets/stimulus.min-123.js", "@hotwired/stimulus-loading": "/assets/stimulus-loading-123.js", "controllers/application": "/assets/controllers/application-123.js", "controllers/hello_controller": "/assets/controllers/hello_controller-123.js", "controllers": "/assets/controllers/index-123.js" } }</script>
  112. Indirect Names Use names rather than fi les <script type="importmap"

    data-turbo-track="reload">{ "imports": { "application": "/assets/application-123.js", "@hotwired/turbo-rails": "/assets/turbo.min-123.js", "@hotwired/stimulus": "/assets/stimulus.min-123.js", "@hotwired/stimulus-loading": "/assets/stimulus-loading-123.js", "controllers/application": "/assets/controllers/application-123.js", "controllers/hello_controller": "/assets/controllers/hello_controller-123.js", "controllers": "/assets/controllers/index-123.js" } }</script>
  113. We're Not Bundling JS

  114. Better Caching

  115. More Requests

  116. HTTP2: Concurrent Requests on a Single Socket

  117. Rack

  118. Rack Defines an Interface Easily change webservers Rack Application Webserver

    Falcon
  119. Rack Interface rack_app = lambda do |env| [200, {}, ["neat"]]

    end def webserver(app) status, headers, body = app.call({}) $socket.puts status $socket.puts headers body.each do |chunk| $socket.puts chunk end end webserver(rack_app) Rack App Webserver Request Data Response Data
  120. Rack + HTTP

  121. Rack + TCP Connection

  122. HTTP Timeline + First Commit of Rack Starting with HTTP/0.9

    1990 1998 2006 2014 2022 Websockets 2011 HTTP/3 2020 HTTP/2 2015 Rack 2007 HTTP/1.1 1997 HTTP/1.0 1996 HTTP/0.9 1991
  123. HTTP 0.9 Request / Response Connections were closed Client New

    TCP Connection Server HTTP Request HTTP Response perl ... ENV rack_app = lambda do |env| [200, {}, ["neat"]] end
  124. HTTP/1.0 RFC 1945 1990 1998 2006 2014 2022 Websockets 2011

    HTTP/3 2020 HTTP/2 2015 Rack 2007 HTTP/1.1 1997 HTTP/1.0 1996 HTTP/0.9 1991
  125. Connections are Expensive

  126. Serial Requests

  127. Fetch assets in parallel? <!DOCTYPE html> <html> <body> <img src="wow.gif"

    /> <h1>HELLO!</h1> <p>Happy Friday!!!</p> </body> </html>
  128. HTTP/1.1 RFC 2068 1990 1998 2006 2014 2022 Websockets 2011

    HTTP/3 2020 HTTP/2 2015 Rack 2007 HTTP/1.1 1997 HTTP/1.0 1996 HTTP/0.9 1991
  129. HTTP/1.1: Keep Alive

  130. HTTP 1.1 Request / Response Connections are kept open Client

    New TCP Connection Server HTTP Request HTTP Response ruby ... ruby ... HTTP Request HTTP Response
  131. HTTP/1.1: Have 2 Connections

  132. HTTP 1.1 Multiple Connections Thanks buddy! New TCP Connection Server

    HTTP Request HTTP Response ruby ... ruby ... HTTP Request HTTP Response Client New TCP Connection Server HTTP Request HTTP Response ruby ... ruby ... HTTP Request HTTP Response
  133. Requests Are Still Serial

  134. Bundling Assets

  135. Rack is released! Version 0.1 1990 1998 2006 2014 2022

    Websockets 2011 HTTP/3 2020 HTTP/2 2015 Rack 2007 HTTP/1.1 1997 HTTP/1.0 1996 HTTP/0.9 1991
  136. Based on PEP 333

  137. "HTTP Translation"

  138. Environment Variables -> Hash

  139. stdout -> [200, {}, ["hi"]]

  140. Full Rack Application Example rack_app = lambda do |env| [200,

    # Status Code { "X-Cool-Header" => "Neat" }, # Headers ["Response Body"]] # Response Body end
  141. Websockets Sockets, but for the web! 1990 1998 2006 2014

    2022 Websockets 2011 HTTP/3 2020 HTTP/2 2015 Rack 2007 HTTP/1.1 1997 HTTP/1.0 1996 HTTP/0.9 1991
  142. Bidirectional Communication

  143. Websockets: NOT HTTP

  144. 2013: Rack 1.5

  145. Hijacking: Slap a proc in there! Get access to the

    socket rack_app = lambda do |env| io = env["rack.hijack"].call io.write "neat" io.close # socket is closed # Return stuff? [200, {}, []] end
  146. HTTP Translator + Socket Getter? 🤷

  147. HTTP/2 It's 0.9 better than HTTP 1.1 1990 1998 2006

    2014 2022 Websockets 2011 HTTP/3 2020 HTTP/2 2015 Rack 2007 HTTP/1.1 1997 HTTP/1.0 1996 HTTP/0.9 1991
  148. SSL Encouraged

  149. Compressed Headers

  150. Binary Protocol

  151. Request / Response Multiplexing Concurrent requests on the same socket

    Client New SSL TCP Connection Server H2 Request 1 H2 Response 2 ruby ... ruby ... H2 Request 2 H2 Response 1
  152. Import Maps

  153. Rails 7: We want you to use H2?

  154. How?

  155. https://github.com/socketry/falcon

  156. HTTP2 Proxy Server

  157. HTTP 2 Proxy Server Proxy translates to HTTP 1.1 H2O

    Unicorn Rails HTTP 1.1 Rack HTTP 2
  158. Push Promise

  159. "Take this resource"

  160. What if the client already has the resource cached? 😅

  161. Push Promise in HTTP 1.1

  162. HTTP Status Code 103: Early Hints

  163. Sending Server Push with H2 Proxy HTTP 1.1 server sends

    103, proxy converts to PUSH HTTP/1.1 103 Early Hints Link: </style.css>; rel=preload; as=style Link: </script.js>; rel=preload; as=script HTTP/1.1 200 OK Date: Fri, 26 May 2022 10:02:11 GMT Content-Length: 1234 Content-Type: text/html; charset=utf-8 Link: </style.css>; rel=preload; as=style Link: </script.js>; rel=preload; as=script HTTP 1.1 Response
  164. Sending Server Push with H2 Proxy HTTP 1.1 server sends

    103, proxy converts to PUSH HTTP/1.1 103 Early Hints Link: </style.css>; rel=preload; as=style Link: </script.js>; rel=preload; as=script HTTP 103 Response H2O Unicorn HTTP 1.1 HTTP 2 PUSH /style.css PUSH /script.js
  165. Rails + (Puma || Falcon)

  166. Slap another lambda in the env hash! Great! class Server

    def call env env['rack.early_hints'].call([ { link: "/style.css", as: "style" }, # push /style.css { link: "/script.js" }]) # push /script.js [200, { "X-Hello" => "World" }, ["Hello world!"]] end end
  167. 2020: HTTP/3

  168. Rails 7 is HTTP/2 by default

  169. Do we need H2 servers?

  170. What does H2 mean for Hijack?

  171. Slapping lambdas in a hash seems unsustainable

  172. Next Generation Rack

  173. Easy to Use

  174. Easy to Extend

  175. Easy to Deploy

  176. This Whole Thing Is Moot

  177. Ruby 3.1: WASM support

  178. https://ruby-syntax-tree.github.io

  179. Next Year: Rails in the browser, React on the server

  180. THANKS PORTLAND! See you next year! 😘