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

Ruby Internals

Ruby Internals

Updated slides from my talk Ruby Internals at MagmaConf

Mario Alberto Chávez

June 17, 2013
Tweet

More Decks by Mario Alberto Chávez

Other Decks in Technology

Transcript

  1. Mario A Chávez // @mario_chavez // RUBY INTERNALS The path

    to get our code executed on Ruby Monday, June 17, 13
  2. Teoría de la computación 101 Esa materia que en la

    “Uni” decíamos no sirve para nada Monday, June 17, 13
  3. irb(main): 001:0> 2 + 2 => 4 CÓDIGO DE RUBY

    TOKENIZER PARSE COMPILAR EJECUCIÓN Monday, June 17, 13
  4. 10.times do|i| puts i end tInteger 10 . tIndetifier “times”

    keyword_do | tIndetifier “i” | tIndetifier “puts” tIndetifier “i” keyword_end Monday, June 17, 13
  5. TOKENIZE •Se base en el uso de Autómatas Finitos Deterministas

    •LEX es un analizador léxico popular Monday, June 17, 13
  6. TOKENIZE •Se base en el uso de Autómatas Finitos Deterministas

    •LEX es un analizador léxico popular •Lex genera un programa en C que realiza el análisis léxico Monday, June 17, 13
  7. TOKENIZE •Se base en el uso de Autómatas Finitos Deterministas

    •LEX es un analizador léxico popular •Lex genera un programa en C que realiza el análisis léxico •La reglas se expresan en forma de Expresiones Regulares Monday, June 17, 13
  8. TOKENIZE •Se base en el uso de Autómatas Finitos Deterministas

    •LEX es un analizador léxico popular •Lex genera un programa en C que realiza el análisis léxico •La reglas se expresan en forma de Expresiones Regulares •Matz escribió su propio lexer Monday, June 17, 13
  9. TOKENIZE •Se base en el uso de Autómatas Finitos Deterministas

    •LEX es un analizador léxico popular •Lex genera un programa en C que realiza el análisis léxico •La reglas se expresan en forma de Expresiones Regulares •Matz escribió su propio lexer •parse.y contiene las reglas léxicas del lenguaje Monday, June 17, 13
  10. TOKENIZE •Se base en el uso de Autómatas Finitos Deterministas

    •LEX es un analizador léxico popular •Lex genera un programa en C que realiza el análisis léxico •La reglas se expresan en forma de Expresiones Regulares •Matz escribió su propio lexer •parse.y contiene las reglas léxicas del lenguaje •Podemos utilizar Ruby para hacer el análisis léxico de código en Ruby Monday, June 17, 13
  11. require ‘ripper’ code = <<STR 10.times do |i| puts i

    end STR Ripper.lex(code) [[[1, 0], :on_int, "10"], [[1, 2], :on_period, "."], [[1, 3], :on_ident, "times"], [[1, 8], :on_sp, " "], [[1, 9], :on_kw, "do"], [[1, 11], :on_sp, " "], [[1, 12], :on_op, "|"], [[1, 13], :on_ident, "i"], [[1, 14], :on_op, "|"], [[1, 15], :on_ignored_nl, "\n"], [[2, 0], :on_sp, " "], [[2, 2], :on_ident, "puts"], [[2, 6], :on_sp, " "], [[2, 7], :on_ident, "i"], [[2, 8], :on_nl, "\n"], [[3, 0], :on_kw, "end"], [[3, 3], :on_nl, "\n"]] Monday, June 17, 13
  12. RIPPER •Ripper es parte de la librería estándar de Ruby

    •Esta presente a partir de Ruby 1.9 y 2.0 Monday, June 17, 13
  13. RIPPER •Ripper es parte de la librería estándar de Ruby

    •Esta presente a partir de Ruby 1.9 y 2.0 •Ripper no sólo ‘tokeniza’, puede hacer otras cosas interesantes Monday, June 17, 13
  14. irb(main): 001:0> 2 + 2 => 4 CÓDIGO DE RUBY

    TOKENIZER PARSE COMPILAR EJECUCIÓN Monday, June 17, 13
  15. PARSE Agrupar los tokens a manera de frases que Ruby

    comprenda “ ” Monday, June 17, 13
  16. 10.times do|i| puts i end var ref command args add

    block indetifier “puts” indetifier “i” Monday, June 17, 13
  17. PARSE •GNU YACC es un generador de parser muy popular

    •GNU YACC genera un LALR parser (Look Ahead LR) Monday, June 17, 13
  18. PARSE •GNU YACC es un generador de parser muy popular

    •GNU YACC genera un LALR parser (Look Ahead LR) •La reglas gramaticales siguen una variación de la notación “Backus-Naur Form” (BNF) Monday, June 17, 13
  19. PARSE •GNU YACC es un generador de parser muy popular

    •GNU YACC genera un LALR parser (Look Ahead LR) •La reglas gramaticales siguen una variación de la notación “Backus-Naur Form” (BNF) •Ruby usa RACC que esta escrito en Ruby! Monday, June 17, 13
  20. PARSE •GNU YACC es un generador de parser muy popular

    •GNU YACC genera un LALR parser (Look Ahead LR) •La reglas gramaticales siguen una variación de la notación “Backus-Naur Form” (BNF) •Ruby usa RACC que esta escrito en Ruby! •parse.y contiene las reglas gramaticales del lenguaje Monday, June 17, 13
  21. PARSE •GNU YACC es un generador de parser muy popular

    •GNU YACC genera un LALR parser (Look Ahead LR) •La reglas gramaticales siguen una variación de la notación “Backus-Naur Form” (BNF) •Ruby usa RACC que esta escrito en Ruby! •parse.y contiene las reglas gramaticales del lenguaje •Podemos utilizar Ruby para hacer el análisis gramatical de código en Ruby Monday, June 17, 13
  22. require ‘ripper’ code = <<STR 10.times do |i| puts i

    end STR Ripper.sexp(code) [:program, [[:method_add_block, [:call, [:@int, "10", [1, 0]], :".", [:@ident, "times", [1, 3]]], [:do_block, [:block_var, [:params, [[:@ident, "i", [1, 13]]], nil, nil, nil, nil], nil], [[:command, [:@ident, "puts", [2, 0]], [:args_add_block, [[:var_ref, [:@ident, "i", [2, 5]]]], false]]]]]]] Monday, June 17, 13
  23. $ ruby dump -- parsetree my_program.rb ######################################################### ## ## Do

    NOT use this node dump for any purpose other than ## ## debug and research. Compatibility is not guaranteed. ## ######################################################### ## # @ NODE_SCOPE (line: 3) # +- nd_tbl: (empty) # +- nd_args: # | (null node) # +- nd_body: # @ NODE_ITER (line: 1) # +- nd_iter: # | @ NODE_CALL (line: 1) # | +- nd_mid: :times # | +- nd_recv: # | | @ NODE_LIT (line: 1) # | | +- nd_lit: 10 # | +- nd_args: # | (null node) # +- nd_body: # @ NODE_SCOPE (line: 3) # +- nd_tbl: :i # +- nd_args: # | @ NODE_ARGS (line: 1) # | +- nd_frml: 1 # | +- nd_next: # | | @ NODE_ARGS_AUX (line: 1) # | | +- nd_rest: (null) # | | +- nd_body: (null) # | | +- nd_next: # | | (null node) # | +- nd_opt: # | (null node) # +- nd_body: # @ NODE_FCALL (line: 2) # +- nd_mid: :puts # +- nd_args: # @ NODE_ARRAY (line: 2) # +- nd_alen: 1 # +- nd_head: # | @ NODE_DVAR (line: 2) # | +- nd_vid: :i # +- nd_next: # (null node) Monday, June 17, 13
  24. irb(main): 001:0> 2 + 2 => 4 CÓDIGO DE RUBY

    TOKENIZER PARSE COMPILAR EJECUCIÓN Monday, June 17, 13
  25. COMPILAR A partir de AST generar las instrucciones para YARV

    (Yet Another Virtual Machine) “ ” Monday, June 17, 13
  26. A partir del AST se generan instrucciones para el YARV

    YARV instructions set Monday, June 17, 13
  27. YARV es una VM de stack Así pasa parámetros, calcula

    valores intermedios y coloca valores de retorno Monday, June 17, 13
  28. puts 2 + 2 NODE_SCOPE table: [] args: [] NODE_FCALL

    method_id: “puts” NODE_CALL method_id: “+” NODE_LITERAL “2” NODE_LITERAL “2” INSTRUCCIONES YARV putself putobject 2 putobject 2 send :+, 1 send :puts, 1 Monday, June 17, 13
  29. puts 2 + 2 NODE_SCOPE table: [] args: [] NODE_FCALL

    method_id: “puts” NODE_CALL method_id: “+” NODE_LITERAL “2” NODE_LITERAL “2” INSTRUCCIONES YARV putself putobject 2 putobject 2 send :+, 1 send :puts, 1 opt_plus Monday, June 17, 13
  30. COMPILAR •El código se convierte en instrucciones YARV •El código

    generado está optimizado Monday, June 17, 13
  31. COMPILAR •El código se convierte en instrucciones YARV •El código

    generado está optimizado •Vemos el patrón: empujar receptor, empujar argumentos, llamar función Monday, June 17, 13
  32. COMPILAR •El código se convierte en instrucciones YARV •El código

    generado está optimizado •Vemos el patrón: empujar receptor, empujar argumentos, llamar función •El listado de instrucciones de YARV están en el archivo insns.def Monday, June 17, 13
  33. COMPILAR •El código se convierte en instrucciones YARV •El código

    generado está optimizado •Vemos el patrón: empujar receptor, empujar argumentos, llamar función •El listado de instrucciones de YARV están en el archivo insns.def •Podemos utilizar Ruby para compilar/decompilar las instrucciones YARV Monday, June 17, 13
  34. require ‘ripper’ code = <<STR 2 + 2 STR RubyVM::Instruct

    ionSequence.comp ile(code).disam == disasm: <RubyVM::InstructionSequence:< compiled>@<compiled>>===== ===== 0000 trace 1 ( 1) 0002 putself 0003 putobject 2 0005 putobject 2 0007 opt_plus <ic:2> 0009 send :puts, 1, nil, 8, <ic:1> 0015 leave => nil Monday, June 17, 13
  35. irb(main): 001:0> 2 + 2 => 4 CÓDIGO DE RUBY

    TOKENIZER PARSE COMPILAR EJECUCIÓN Monday, June 17, 13
  36. Las instrucciones de YARV mapean a código en C Este

    código esta en el Runtime de Ruby Monday, June 17, 13
  37. Hay una estructura rb_control_frame_t por cada cambio de contexto Esta

    estructura almacena el Stack Pointer (SP) y el Program Pointer (PC) Monday, June 17, 13
  38. puts 2 + 2 rb_control_frame_t Instrucciones YARV stack interno de

    YARV rb_control_frame_t PC SP self type rb_control_frame_t Monday, June 17, 13
  39. puts 2 + 2 rb_control_frame_t Instrucciones YARV putself stack interno

    de YARV self rb_control_frame_t PC SP self type SP PC rb_control_frame_t Monday, June 17, 13
  40. puts 2 + 2 rb_control_frame_t Instrucciones YARV putself putobject 2

    stack interno de YARV self rb_control_frame_t PC SP self type SP PC rb_control_frame_t 2 Monday, June 17, 13
  41. puts 2 + 2 rb_control_frame_t Instrucciones YARV putself putobject 2

    putobject 2 stack interno de YARV self rb_control_frame_t PC SP self type SP PC rb_control_frame_t 2 2 Monday, June 17, 13
  42. puts 2 + 2 rb_control_frame_t Instrucciones YARV putself putobject 2

    putobject 2 opt_plus stack interno de YARV self rb_control_frame_t PC SP self type SP PC rb_control_frame_t 2 2 Monday, June 17, 13
  43. puts 2 + 2 rb_control_frame_t Instrucciones YARV putself putobject 2

    putobject 2 opt_plus stack interno de YARV self rb_control_frame_t PC SP self type SP PC rb_control_frame_t 4 Monday, June 17, 13
  44. puts 2 + 2 rb_control_frame_t Instrucciones YARV putself putobject 2

    putobject 2 opt_plus send :puts, 1 stack interno de YARV self rb_control_frame_t PC SP self type SP PC rb_control_frame_t 4 Monday, June 17, 13
  45. YARV intructions rb_control_frame [BLOCK] trace putself putstring “Hola” send :puts,

    1 leave [C Function - “times”] trace putobject 10 send :times, 0, block leave rb_control_frame [FINISH] rb_control_frame [C_FUNC] rb_control_frame [EVAL] rb_control_frame [FINISH] Control Frame Pointer (CFP) Monday, June 17, 13
  46. Ejecución •YARV mejora el tiempo de ejecución •Tiene un “warm

    up” más lento que Ruby 1.8 pero a largo tiempo es más rápido Monday, June 17, 13
  47. Ejecución •YARV mejora el tiempo de ejecución •Tiene un “warm

    up” más lento que Ruby 1.8 pero a largo tiempo es más rápido •Solo “rascamos” YARV ligeramente Monday, June 17, 13
  48. irb(main): 001:0> 2 + 2 => 4 CÓDIGO DE RUBY

    TOKENIZER PARSE COMPILAR EJECUCIÓN Monday, June 17, 13
  49. RECURSOS •Libro “Ruby Under Microscope” http:// patshaughnessy.net/ruby-under- a-microscope •Blog de

    Pat Shaughnessy http://patshaughnessy.net •Videos de RubyConf 2012 http://confreaks.com/events/ rubyconf2012 (En espacial Kichi Sasada , Aaron Paterson) •El repositorio de Ruby en Github https://github.com/ruby/ ruby Monday, June 17, 13