Slide 1

Slide 1 text

No content

Slide 2

Slide 2 text

JavaScript

Slide 3

Slide 3 text

Everything is broken.

Slide 4

Slide 4 text

Ruby VM Internals The TMI Edition

Slide 5

Slide 5 text

Aaron Patterson

Slide 6

Slide 6 text

@tenderlove

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

Ruby Core Team Rails Core Team

Slide 11

Slide 11 text

I am bad at saying "no"

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

No content

Slide 14

Slide 14 text

Slide 15

Slide 15 text

https://github.com/ManageIQ/ manageiq

Slide 16

Slide 16 text

RubyConf 5k

Slide 17

Slide 17 text

TEXAS!!!

Slide 18

Slide 18 text

No content

Slide 19

Slide 19 text

Stupid Ruby VM Tricks

Slide 20

Slide 20 text

Stupid Ruby VM Tricks

Slide 21

Slide 21 text

Stupid Ruby VM Tricks

Slide 22

Slide 22 text

Ruby’s Virtual Machine and its internals

Slide 23

Slide 23 text

WARNING: THIS IS A TECH TALK

Slide 24

Slide 24 text

&&&&&

Slide 25

Slide 25 text

Ruby’s Virtual Machine and its internals

Slide 26

Slide 26 text

Actually a talk about Failure.

Slide 27

Slide 27 text

Building an AOT Compiler

Slide 28

Slide 28 text

Rebrand Failure as: "Potential For Success"

Slide 29

Slide 29 text

Time Breakdown of "Feeling Successful" 1% 9% 11% 11% 31% 37% Fail Fail Fail Fail Fail Success

Slide 30

Slide 30 text

At least I learned something, right?

Slide 31

Slide 31 text

NO

Slide 32

Slide 32 text

No content

Slide 33

Slide 33 text

ruby myprogram.rb

Slide 34

Slide 34 text

Stuff that happens before the program runs Running the actual program eval

Slide 35

Slide 35 text

Stuff that happens before the program runs Running the actual program Lexer Virtual Machine About 2 Slides Each Parser Compiler Most of Our Time

Slide 36

Slide 36 text

Lexer

Slide 37

Slide 37 text

DOT name: 'each' DO END array.each do; end name: 'array' LEXER

Slide 38

Slide 38 text

Parser

Slide 39

Slide 39 text

Method Call array each block DOT name: 'each' DO END name: 'array' PARSER AST (Abstract Syntax Tree)

Slide 40

Slide 40 text

Hsing-Hui Hsu @SoManyHs 3:05

Slide 41

Slide 41 text

Ruby 1.8 and 1.9 Diverge

Slide 42

Slide 42 text

Ruby 1.8: Interprets AST

Slide 43

Slide 43 text

AST Interpreter Method Call array each block

Slide 44

Slide 44 text

Ruby 1.9+: YARV (VM)

Slide 45

Slide 45 text

put self send array send each Compile Phase Method Call array each block COMPILER

Slide 46

Slide 46 text

What is a Virtual Machine?

Slide 47

Slide 47 text

It’s like a Real Machine, but it’s Virtual.

Slide 48

Slide 48 text

global _start section .text _start: ; write(1, message, 13) mov rax, 1 ; system call 1 is write mov rdi, 1 ; file handle 1 is stdout mov rsi, message ; address of string to output mov rdx, 13 ; number of bytes syscall ; invoke operating system to do the write ; exit(0) mov eax, 60 ; system call 60 is exit xor rdi, rdi ; exit code 0 syscall ; invoke operating system to exit message: db "Hello, World", 10 ; note the newline at the end

Slide 49

Slide 49 text

These are defined by the capabilities of the processor

Slide 50

Slide 50 text

Virtual Machines Give You Freedom

Slide 51

Slide 51 text

What instructions should I dream up today?

Slide 52

Slide 52 text

Two Types of VM: Stack Based & Register Based

Slide 53

Slide 53 text

Stack Based VMs

Slide 54

Slide 54 text

Like a Calculator

Slide 55

Slide 55 text

HP Calculators are RAD

Slide 56

Slide 56 text

9 * 18

Slide 57

Slide 57 text

9㾑 18㾑

Slide 58

Slide 58 text

No content

Slide 59

Slide 59 text

*

Slide 60

Slide 60 text

No content

Slide 61

Slide 61 text

"That’s so much work, why bother?"

Slide 62

Slide 62 text

GO BACK TO YOUR TI-83 Plus

Slide 63

Slide 63 text

Instructions 9㾑 18㾑 * Stack 1: 9 2: 9 1: 18 1: 162

Slide 64

Slide 64 text

HP 49g+ 9㾑 18㾑 * YARV putobject 9 putobject 18 opt_mult

Slide 65

Slide 65 text

Virtual Machines Should Not Intimidate You

Slide 66

Slide 66 text

C Code Should

Slide 67

Slide 67 text

YARV Code putobject 9 putobject 18 opt_mult Program Just an Array Instruction Parameters

Slide 68

Slide 68 text

So how do we get this "machine code"?

Slide 69

Slide 69 text

Compiler

Slide 70

Slide 70 text

Mult-Pass Compiler

Slide 71

Slide 71 text

AST Linked List Optimizations Byte Code

Slide 72

Slide 72 text

RubyVM::InstructionSequence

Slide 73

Slide 73 text

RubyVM::InstructionSequence ins = RubyVM::InstructionSequence.new <<-eoruby def foo "hello " end puts foo + "world" eoruby puts ins.disasm

Slide 74

Slide 74 text

RubyVM::InstructionSequence == disasm: #@>================================ 0000 trace 1 ( 1) 0002 putspecialobject 1 0004 putspecialobject 2 0006 putobject :foo 0008 putiseq foo 0010 opt_send_without_block , 0013 pop 0014 trace 1 ( 5) 0016 putself 0017 putself 0018 opt_send_without_block , 0021 putstring "world" 0023 opt_plus , 0026 opt_send_without_block , 0029 leave == disasm: #>======================================= 0000 trace 8 ( 1) 0002 trace 1 ( 2) 0004 putstring "hello " 0006 trace 16 ( 3) 0008 leave ( 2)

Slide 75

Slide 75 text

AST

Slide 76

Slide 76 text

What is an AST?

Slide 77

Slide 77 text

"Abstract Syntax Tree"

Slide 78

Slide 78 text

Get the AST NODE* rb_compile_string(const char *f, VALUE s, int line)

Slide 79

Slide 79 text

NODE is an AST Node Method Call array each block

Slide 80

Slide 80 text

Linked List

Slide 81

Slide 81 text

What is a Linked List (LL)?

Slide 82

Slide 82 text

Invented in 1836

Slide 83

Slide 83 text

Battle of the à la Mode

Slide 84

Slide 84 text

Alexander Graham Link

Slide 85

Slide 85 text

Item 1 Item 2 Item 3

Slide 86

Slide 86 text

Linked List Generation VALUE rb_iseq_compile_node(rb_iseq_t *iseq, NODE *node) Our Node Final Product

Slide 87

Slide 87 text

AST Linked List Optimizations Byte Code

Slide 88

Slide 88 text

Which Branch To Follow? VALUE rb_iseq_compile_node(rb_iseq_t *iseq, NODE *node) { DECL_ANCHOR(ret); INIT_ANCHOR(ret); if (node == 0) { COMPILE(ret, "nil", node); iseq_set_local_table(iseq, 0); } else if (nd_type(node) == NODE_SCOPE) {

Slide 89

Slide 89 text

Which Branch To Follow? #define nd_type(n) ((int) (((RNODE(n))->flags & NODE_TYPEMASK)>>NODE_TYPESHIFT))

Slide 90

Slide 90 text

LETS DO SOMETHING DANGEROUS

Slide 91

Slide 91 text

Reach Inside require 'fiddle' include Fiddle func = Function.new Handle::DEFAULT['rb_compile_string'], [TYPE_VOIDP, TYPE_VOIDP, TYPE_INT], TYPE_VOIDP node = func.call "", Fiddle.dlwrap("def foo; end"), 0 p Fiddle.dlunwrap node

Slide 92

Slide 92 text

MUWAHAHAHA $ ruby test2.rb test2.rb:9:in `p': method `inspect' called on unexpected T_NODE object (0x007facab8e6570 flags=0x1) (NotImplementedError) from test2.rb:9:in `'

Slide 93

Slide 93 text

Which Branch To Follow? VALUE rb_iseq_compile_node(rb_iseq_t *iseq, NODE *node) { DECL_ANCHOR(ret); INIT_ANCHOR(ret); if (node == 0) { COMPILE(ret, "nil", node); iseq_set_local_table(iseq, 0); } else if (nd_type(node) == NODE_SCOPE) {

Slide 94

Slide 94 text

Which Branch To Follow? enum node_type { NODE_SCOPE,

Slide 95

Slide 95 text

THIS IS IT!!! default: { COMPILE(ret, "scoped node", node->nd_body); break; }

Slide 96

Slide 96 text

iseq_compile_each

Slide 97

Slide 97 text

iseq_compile_each /** compile each node self: InstructionSequence node: Ruby compiled node poped: This node will be poped */ static int iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)

Slide 98

Slide 98 text

> 2300 lines

Slide 99

Slide 99 text

`true` case NODE_TRUE:{ if (!poped) { ADD_INSN1(ret, line, putobject, Qtrue); } break; } AST Node Type Add New LL Item Instruction Name Instruction Arg

Slide 100

Slide 100 text

`p true` $ ruby -e'puts RubyVM::InstructionSequence.new("p true").disasm' == disasm: #@>================================ 0000 trace 1 ( 1) 0002 putself 0003 putobject true 0005 opt_send_without_block , 0008 leave `putobject` Instruction `true` Parameter

Slide 101

Slide 101 text

If Statements then_label = NEW_LABEL(line); else_label = NEW_LABEL(line); end_label = NEW_LABEL(line); compile_branch_condition(iseq, cond_seq, node->nd_cond, then_label, else_label); COMPILE_(then_seq, "then", node->nd_body, poped); COMPILE_(else_seq, "else", node->nd_else, poped); ADD_SEQ(ret, cond_seq); ADD_LABEL(ret, then_label); ADD_SEQ(ret, then_seq); ADD_INSNL(ret, line, jump, end_label); ADD_LABEL(ret, else_label); ADD_SEQ(ret, else_seq); ADD_LABEL(ret, end_label);

Slide 102

Slide 102 text

COMPILE recurses, ADD_* adds a new LL Item

Slide 103

Slide 103 text

Node insn: putself Node insn: putobject arg: true Node insn: opt_send_without_block arg: "p"

Slide 104

Slide 104 text

Why a Linked List?

Slide 105

Slide 105 text

Optimizations

Slide 106

Slide 106 text

rb_iseq_compile_node

Slide 107

Slide 107 text

iseq_setup

Slide 108

Slide 108 text

AST Linked List Optimizations Byte Code

Slide 109

Slide 109 text

iseq_optimize Compile time optimizations

Slide 110

Slide 110 text

Optimization Settings $ ruby -e'p RubyVM::InstructionSequence.compile_option' {:inline_const_cache=>true, :peephole_optimization=>true , :tailcall_optimization=>false, :specialized_instructio n=>true, :operands_unification=>true, :instructions_unif ication=>false, :stack_caching=>false, :trace_instructio n=>true, :frozen_string_literal=>false, :frozen_string_l iteral_debug=>false, :debug_level=>0}

Slide 111

Slide 111 text

Optimization Settings $ ruby -e'p RubyVM::InstructionSequence.compile_option' {:inline_const_cache=>true, :peephole_optimization=>true , :tailcall_optimization=>false, :specialized_instructio n=>true, :operands_unification=>true, :instructions_unif ication=>false, :stack_caching=>false, :trace_instructio n=>true, :frozen_string_literal=>false, :frozen_string_l iteral_debug=>false, :debug_level=>0}

Slide 112

Slide 112 text

Peephole Optimizations: Eliminating Dead Code

Slide 113

Slide 113 text

Dead Code == Useless Instructions

Slide 114

Slide 114 text

Remove Useless Instructions /* * useless jump elimination: * jump LABEL1 * ... * LABEL1: * jump LABEL2 * * => in this case, first jump instruction should jump to * LABEL2 directly */

Slide 115

Slide 115 text

Specialized Instructions

Slide 116

Slide 116 text

For All Your Special Occasions

Slide 117

Slide 117 text

`foo.bar` vs `foo + bar`

Slide 118

Slide 118 text

Regular Method Dispatch (foo.bar) $ ruby -e"puts RubyVM::InstructionSequence.new(\"foo.bar\", nil, nil, 0, :specialized_instruction => false).disasm" == disasm: #@>================================ 0000 putself 0001 send , , nil 0005 send , , nil 0009 leave send

Slide 119

Slide 119 text

Regular Method Dispatch (foo + bar) $ ruby -e"puts RubyVM::InstructionSequence.new(\"foo + bar\", nil, nil, 0, :specialized_instruction => false).disasm" == disasm: #@>================================ 0000 putself 0001 send , , nil 0005 putself 0006 send , , nil 0010 send , , nil 0014 leave send

Slide 120

Slide 120 text

Specialized Instructions $ ruby -e"puts RubyVM::InstructionSequence.new(\"foo + bar\", nil, nil, 0, :specialized_instruction => true).disasm" == disasm: #@>================================ 0000 putself 0001 opt_send_without_block , 0004 putself 0005 opt_send_without_block , 0008 opt_plus , 0011 leave send

Slide 121

Slide 121 text

Specialized Instructions do less work

Slide 122

Slide 122 text

Reminder: We haven’t even run any Ruby code yet.

Slide 123

Slide 123 text

Linked Lists:

Slide 124

Slide 124 text

Byte Code

Slide 125

Slide 125 text

iseq_setup

Slide 126

Slide 126 text

iseq_set_sequence

Slide 127

Slide 127 text

Byte Code: A list of integers*. *The integers are addresses

Slide 128

Slide 128 text

This fact will come back to "byte" us.

Slide 129

Slide 129 text

Raw Instructions /** ruby insn object list -> raw instruction sequence */ static int iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *anchor) { LABEL *lobj; INSN *iobj; struct iseq_line_info_entry *line_info_table; unsigned int last_line = 0; LINK_ELEMENT *list; VALUE *generated_iseq; List of actual instructions and parameters

Slide 130

Slide 130 text

Adds instruction to the list generated_iseq[code_index] = insn; … case TS_VALUE: /* VALUE */ { VALUE v = operands[j]; generated_iseq[code_index + 1 + j] = v; /* to mark ruby object */ iseq_add_mark_object(iseq, v); break; } Put the instruction in Add Parameter

Slide 131

Slide 131 text

puts 'foo' $ ruby -e"puts RubyVM::InstructionSequence.new(\"puts 'foo'\").disasm" == disasm: #@>================================ 0000 trace 1 ( 1) 0002 putself 0003 putstring "foo" 0005 opt_send_without_block , 0008 leave insn "foo"

Slide 132

Slide 132 text

We finally have our byte code!

Slide 133

Slide 133 text

Virtual Machine

Slide 134

Slide 134 text

insns.def

Slide 135

Slide 135 text

Instruction Format @c: category @e: english description @j: japanese description instruction form: DEFINE_INSN instruction_name (instruction_operands, ..) (pop_values, ..) (return value) { .. // insn body } Byte Code Stack

Slide 136

Slide 136 text

putstring Instruction /** @c put @e put string val. string will be copied. @j จࣈྻΛίϐʔͯ͠ελοΫʹϓογϡ͢Δɻ */ DEFINE_INSN putstring (VALUE str) () (VALUE val) { val = rb_str_resurrect(str); }

Slide 137

Slide 137 text

VM Optimizations

Slide 138

Slide 138 text

VM Optimization List $ ruby -e'p RubyVM::OPTS' ["direct threaded code", "operands unification", "inline method cache"]

Slide 139

Slide 139 text

Check vm_core.h

Slide 140

Slide 140 text

Native Execution

Slide 141

Slide 141 text

Code

Slide 142

Slide 142 text

Decode and Dispatch

Slide 143

Slide 143 text

Code Loop Routine Routine

Slide 144

Slide 144 text

Threaded Interpretation

Slide 145

Slide 145 text

Code Loop Routine Routine

Slide 146

Slide 146 text

Code Dispatch Routine Routine Dispatch

Slide 147

Slide 147 text

1. Our VM is Generated 2. Eliminates the Loop

Slide 148

Slide 148 text

Code Dispatch Routine Routine Dispatch

Slide 149

Slide 149 text

We can do better!

Slide 150

Slide 150 text

What if: The instruction was a function address?

Slide 151

Slide 151 text

Direct Threaded Interpretation

Slide 152

Slide 152 text

Code Dispatch Routine Routine Dispatch

Slide 153

Slide 153 text

Failing at AOT Compiling

Slide 154

Slide 154 text

Byte Code: A list of integers*. *The integers are addresses

Slide 155

Slide 155 text

AHA! If it’s just a list of integers, we should be able to save that list to a file, then load it again.

Slide 156

Slide 156 text

Past Aaron was not too bright.

Slide 157

Slide 157 text

Adds instruction to the list generated_iseq[code_index] = insn; … case TS_VALUE: /* VALUE */ { VALUE v = operands[j]; generated_iseq[code_index + 1 + j] = v; /* to mark ruby object */ iseq_add_mark_object(iseq, v); break; } Add Parameter

Slide 158

Slide 158 text

VALUE is a heap allocated Ruby object.

Slide 159

Slide 159 text

The Pointer Always Changes

Slide 160

Slide 160 text

The Object must be written to disk.

Slide 161

Slide 161 text

END STUFF

Slide 162

Slide 162 text

Practical Applications

Slide 163

Slide 163 text

Tweak Optimizations

Slide 164

Slide 164 text

Understand what your Ruby code is *really* doing.

Slide 165

Slide 165 text

Interpolation vs String#+ # "#{foo}#{bar}" vs foo + bar z = RubyVM::InstructionSequence.new <<-eoruby x = "\#{foo}\#{bar}" eoruby puts z.disasm z = RubyVM::InstructionSequence.new <<-eoruby x = foo + bar eoruby puts z.disasm

Slide 166

Slide 166 text

Browse `iseq_compile_each`

Slide 167

Slide 167 text

Which is faster? TABLE = { 'foo' => 'bar'.freeze, 'bar' => 'baz'.freeze } def table_lookup x TABLE[x] end def case_lookup x case x when 'foo' then 'bar'.freeze when 'bar' then 'baz'.freeze end end

Slide 168

Slide 168 text

Now which is faster? TABLE = { 'foo' => 'bar'.freeze, 'bar' => 'baz'.freeze } def table_lookup x TABLE[x] || 'omg'.freeze end def case_lookup x case x when 'foo' then 'bar'.freeze when 'bar' then 'baz'.freeze when nil then 'omg'.freeze end end

Slide 169

Slide 169 text

Why?

Slide 170

Slide 170 text

REMEMBER THESE:

Slide 171

Slide 171 text

insns.def iseq_compile_each rb_iseq_compile_node

Slide 172

Slide 172 text

Where to learn more

Slide 173

Slide 173 text

yarvarch.ja #title YARVΞʔΩςΫνϟ #set author ೔ຊ Ruby ͷձ ͩ͜͞͞͏͍ͪ - 2005-03-03(Thu) 00:31:12 +0900 ͍Ζ͍Ζͱॻ͖௚͠ ---- * ͜Ε͸ʁ [[YARV: Yet Another RubyVM|http://www.atdot.net/yarv]] ͷ ઃܭϝϞͰ͢ɻ YARV ͸ɺRuby ϓϩάϥϜͷͨΊͷ࣍ͷػೳΛఏڙ͠·͢ɻ - Compiler - VM Generator - VM (Virtual Machine) - Assembler - Dis-Assembler - (experimental) JIT Compiler - (experimental) AOT Compiler ݱࡏͷ YARV ͸ Ruby ΠϯλϓϦλͷ֦ுϥΠϒϥϦͱ࣮ͯ͠૷͍ͯ͠·͢ɻ͜

Slide 174

Slide 174 text

yarvarch.en #title YARV: Yet another RubyVM - Software Architecture maybe writing. * YARV instruction set <%= d %>

Slide 175

Slide 175 text

No content

Slide 176

Slide 176 text

Thank You!!!