By Evan Phoenix Employed by EngineYard to work full time on Rubinius Did hangman on the blackboard while waiting for the break to finish. The never guessed word was "Ruby". = Intro Fairly technical talk = Rubinius - New custom VM for running ruby code - Small VM written in not ruby - Kernel and everything else in ruby == Kernel The code to be default available without requiring anything (Hash, Array, etc.) http://rubini.us = Overview - User [Ruby] - Kernel [Ruby] - VM (Primitives, FFI) - OS == VM Services - Object Memory - Generational GC - Virtual CPU - Custom Instruction Set - Primitive operations (stuff, that you can't do in Ruby) === Object Memory - Allocation (I need a new object) - Variable sized objects (I need this many fields (not bytes)) - Opaque objects (ignored by GC, typically used for strings) - Accurate collection of all garbage objects (makes it simple) - No C stack / register walking === Object Layout - Header (3 words) - Flags - Number of fields - Class Pointer (20-30 years of reasearch has gone into reducing the size of the object header) - N Words - Variables sized - Number of fields indicates how many (fields past the header) ==== Object flags - Used by GC - Forwarded, Remember, Mark, ForeverYoung - Allow for opaque objects - StoresBytes - Allow for weak references - RefsAreWeak ==== Opaque objects - Allows GC to raw bytes - ByteArray class used by String primary is the example - Allows GC to store C structs - Used by VM to implement some objects - MethodContext, SendSite, etc. === Generational GC - Young object space (YOS) uses Baker/Cheney copy collector - Made simple using accurate collection - Mature objects pace (MOS) uses simple mark/sweep collector - This is where objects are promoted to when it has lived for 6 GC cycles - Write barrier keeps GC the same ==== Write Barrier - Piece of C code run everytime an object reference is stored into another object - Allows the VM to be sure it knows all objects that point into YOS - Allows GC to collect YOS independently === Accurate collection - VM is able to see every object reference in system - Makes copy collector possible - Collection is only performed at "safe points" - Only one defined currently --> No Platform Specific GC code! = /cpu == Virtual CPU - "Bytecode" interpreter - "Registers" provide abilit to implement flow control == Instructions - 114 instructions - Instructions created as needed - Each instruction is 4 bytes (a 32bit integer) - Allows for up to 2 operands, defined statically per instruction - Most are flow control related, very few manipulate objects directly - Differs from Java in this way Rubinius manipulates objects primarily through primitives == MethodContext - First class "stack frame" objects - Contains all information about the current of a method def silly a = 3 mc = MethodContext.current mc.locals[0] = 18 p a # => 18 end --- def evil_and_silly a = 3 mc = MethodContext. current.sender mc.locals[0] = 18 end def poor_parent a = 3 evil_and_silly() p a # => 18 end --- You can check who is calling you this way == Spaghetti Stack - The "call stack" is a linked list - Each MethodContext has a field called sender - Each sender points to the MC to restore when this MC returns - Toplevel MC has nil sender, causing the VM to exit == Task Objects - Some "registers" of the CPU are global, i.e. not stored in each MC - These are saved and restored from Task objects - Each Task object represents the complete state of the CPU - Used as the muscle in Continuation and Thread classes == Primitives Basic operations that the VM provides class Fixnum def + Ruby.primitive :fixnum_add end end Compiler detects syntax and saves the name of the primitive in the CompiledMethod objects == FFI - Implemented using a couple of primitives module OutsideRuby attach_function 'strlen', [:string], :int end == Threads - Similar to 1.8 - Green threads built on Task objects - Preemption based on simple timer - API directly to VM thread scheduler - Channel objects provide scheduler notifications == Dispatch - Largest amount of time spent when calling method === Caching Spend as little time on lookups as possible ==== Global Cache - For each send, the class and method name are hashed - Hash value is clamped and used as index into large table - Value is validated and used ==== Send Site Cache - Each place where a method is performed is called is called a send site - Observations about code usage find interesting patterns - Most code is NOT polymorphic - Intially, send site is empty - Causes the global cache to be consulted - Information within ===== Locality - Locality contains very rich information - Any exploitation of locality can increase performance == Other VM operations - Ability to directly save, load, and execute a .rbc file - Simple multi-VN spawn and communication - Basic abilities to manipulate builtin classes such as Hash, Array, etc. = Moving up to the actual Ruby code == Kernel-land - All ruby code located within kernel/ - VM loads code directly without require - Phase order: bootstrap, platform, core - Load order of .rb files determed by special dependencies comments == User-land - All code loaded by the kernel = /compiler == VM / Compiler boundry - VM provides ability to execute CompiledMethod objects - CompiledMethod objects are normal, first class objects - Can easily be built up from scratch === Sexp - Wanted to write a Ruby VM, not a Ruby Parser - 2 VM primitives - Will parse the code and give it back to you as a sexp (the code as data) - Provide ability to parse and emit code as data - Sexps are the input to the compiler - Allows for custom sexp composition - Compiler transforms Sexp into internal tree - Compiler wlkas tree, using visitor pattern to generate bytecode === Example Ruby: "aoeu".count ... = Rubinius example - Installed as the command 'rbx' - Runs irb per default '"aoeu".count'.to_sexp => [:newlune, 1, "(eval), [:call, [:str, "aueu"], :count]"]