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

(partially) Non-volatile mruby

(partially) Non-volatile mruby

presentation material of "(partially) Non-volatile mruby" in RubyKaigi 2019
https://rubykaigi.org/2019/presentations/yuri_at_earth.html

Yamanekko

April 20, 2019
Tweet

More Decks by Yamanekko

Other Decks in Programming

Transcript

  1. Note: This is a technical session using C ‣You need

    to know about C language basics ‣ constant (const) ‣ structure ‣ pointers and references
  2. What do we want to do in 10 sec. While

    Executing (current) ROM Ruby’s Objects RAM
  3. What do we want to do in 10 sec. Before

    Executing (current) ROM RAM Ruby’s Objects
  4. What do we want to do in 10 sec. Before

    Executing (ours) ROM RAM Ruby’s Objects
  5. What do we want to do in 10 sec. While

    Executing (ours) RAM new Ruby’s Objects ROM Ruby’s Objects
  6. Contents ‣ Understand this session completely in 10 seconds ‣

    Why ROM and RubyKaigi 2018 wrap up and new our approach ‣ nvgen ‣ Introduction to mruby’s objects in C ‣ Class objects in depth ‣ Fix to work with ROM ‣ Benchmark ‣ Future works
  7. Why QSJDF 3". 30. NSVCZ -&(0 .JOE4UPSNT  ZFO .#

    .# 0, (31&"$)  ZFO .# .# 0, .4UBDL  ZFO ,# .# 0, OVDMFP';(  ZFO ,# ,# 0, OVDMFP'3&  ZFO ,# ,# 0, OVDMFP'3&  ZFO ,# ,# "SEVJOPVOP  ZFO ,# ,# /( ref)
  8. How A B C reduce RAM use ROM tuning by

    #define change implementation
  9. Why do we want to use ROM more ‣RAM is

    small and precious ‣ROM is large, but not used well in dynamic language ‣because ROM cannot be modified in execution time
  10. hello world in mruby #include <mruby.h> #include <mruby/compile.h> int main(void)

    { mrb_state *mrb = mrb_open(); if (!mrb) { /* handle error */ } mrb_load_string(mrb, "puts 'hello world'"); mrb_close(mrb); return 0; } initialize load & run app.c
  11. hello world in mruby #include <mruby.h> #include <mruby/compile.h> #include “bytecode.h”

    int main(void) { mrb_state *mrb = mrb_open(); if (!mrb) { /* handle error */ } mrb_load_irep(mrb, code); mrb_close(mrb); return 0; } initialize load & run const uint8_t code[] = { 0x45,0x54,0x49,0x52, 0x30,0x30,0x30,0x35, 0x3b,0x7c,0x00,0x00, 0x00,0x58,0x4d,0x41, 0x54,0x5a,0x30,0x30, 0x30,0x30,0x49,0x52, 0x45,0x50,0x00,0x00, 0x00,0x3a,0x30,0x30, 0x30,0x32,0x00,0x00, 0x00,0x56,0x00,0x01, 0x00,0x04,0x00,0x00, 0x00,0x00,0x00,0x0c, 0x10,0x01,0x4f,0x02, app.c bytecode.h
  12. mrb_open() ‣Initialize GC and heap memory ‣initialize Symbols and Strings

    ‣Initialize Standard Classes in C ‣initialize Standard Classes in Ruby ‣initialize mrbgems in C and/or Ruby
  13. mrb_open() ‣Initialize GC and heap memory ‣initialize Symbols and Strings

    ‣Initialize Standard Classes in C ‣initialize Standard Classes in Ruby ‣initialize mrbgems in C and/or Ruby
  14. Our approach (in Ruby) A = Array.new A << 1

    if A.size >= 1 A += [2, 3] end A = [1, 2, 3] original generated
  15. Our approach (in Ruby) A = Array.new A << 1

    if A.size >= 1 A += [2, 3] end A = [1, 2, 3] original generated A : [1, 2, 3] Internal data structure
  16. Our approach ‣ build generator with mruby ‣ executing mrb_open()

    ‣ generate C files using mruby ‣ build new executable (binary) ‣ execute mrb_load_state_allocf()ɹ in runtime *.c mruby mruby app.c runtime generator Executable
  17. mrb_load_state_allocf() ‣ mrb_open() ‣ normal VM initializer ‣mrb_open_allocf(mrb_allocf f, void

    *ud) ‣use custom allocation function f ‣ mrb_load_state_allocf(mrb_state *mrb, mrb_allocf f, void *ud) ‣ use custom allocation function and initialized VM image
  18. How to put them on ROM ‣ use structures ‣

    use const const char nv_string_0[] = "mruby - Copyright (c) …"; const struct RString nv_object_141 = { .tt=MRB_TT_STRING, .color=7, .flags=141, .c=(struct RClass *)&nv_object_186, .gcnext=NULL, .as = { .heap = { .ptr=(char *)&nv_string_0 } } }; generated.c
  19. How to put Symbols on ROM ‣ All Symbols should

    be defined ‣ to use structures in ROM ‣ Access to Symbols should be fast ‣ → We use gperf (perfect hash Generator)
  20. How to put Symbols on ROM ‣ generate *.key file

    from symbol objects ‣ generate *.c file with gperf presym.key Symbols presym.c
  21. nvgen ‣ execute mrb_open() ‣ generate Objects, strings, symbols as

    C structures ‣ dump all symbols into presym.key ‣ dump all structures into generated.c mruby nvgen generated.c presym.c presym.key
  22. Can we put them all on ROM? ‣Of course, No

    ‣Many methods to update pre-defined objects ‣Open classes ‣Object#freeze ‣String#reverse!
  23. mruby’s Objects in C ‣mrb_value ‣small objects data represented in

    4 bytes ‣ex) Integer, Symbol, true/false/nil ‣struct RBasic* ‣objects data larger than 4 bytes ‣ex) String, Array, Class, user-defined classes
  24. mrb_value ‣ Object itself ‣ mrb_vtype: value type ‣ value:

    i (Integer), sym (Symbol), p (others) ‣ p is pointer to RXXX typedef struct mrb_value { union { void *p; mrb_int i; mrb_sym sym; } value; enum mrb_vtype tt; } mrb_value;
  25. struct RBasic ‣ body of Object ‣ color: for GC

    ‣ flags: attrs of the object ‣ c: class of the object ‣ gcnext: for GC #define MRB_OBJECT_HEADER \ enum mrb_vtype tt:8;\ uint32_t color:3;\ uint32_t flags:21;\ struct RClass *c;\ struct RBasic *gcnext struct RBasic { MRB_OBJECT_HEADER; };
  26. other struct RXXXX ‣ RObject is Object class ‣ RClass

    is Class class ‣ RClass have super class and method tables ‣ Similar to RArray, RHash, and so on struct RObject { MRB_OBJECT_HEADER; struct iv_tbl *iv; }; struct RClass { MRB_OBJECT_HEADER; struct iv_tbl *iv; struct kh_mt *mt; struct RClass *super; };
  27. mruby VM has class objects Proc String Float Fixnum Symbol

    Kernel Object Class Array Hash TrueClass FalseClass Module Range NilClass mrb_state (mruby VM)
  28. class has ivars and methods iv mt internal data methods

    class object (RClass) iv: instance variable mt: method table
  29. Ruby objects are struct in C struct RClass { MRB_OBJECT_HEADER;

    struct iv_tbl *iv; struct kh_mt *mt; struct RClass *super; };
  30. Memory Usage ‣Flash (ROM) ‣Heap (RAM) ‣use with malloc() ‣target

    of GC ‣Global variables (RAM) ‣use without malloc()
  31. Which’s in ROM, Which’s in RAM ‣ROM: Immutable things ‣RAM:

    Mutable things ‣a member of Immutable structure can be Mutable ‣and vice versa
  32. What’s structures in RAM ‣body of Hash ‣body of instance

    variables ‣methods defined at executing time ‣context ‣instance_vars and methods ‣current VM (mrb_state)
  33. Is it work? ‣You’ll get many problems ‣cannot define new

    classes ‣cannot use mrb_free() ‣cannot do GC ‣cannot use Object#freeze
  34. Is it work? ‣You’ll get many problems ‣cannot find (function|variable)

    names of C from (function|variable) addresses ‣need to generate C code
  35. Function Name Resolution mrb_method_t vals = mrb_malloc(mrb, /* ... */);

    vals[0].func_p = 1; vals[0].func = gc_start; dump_vals(vals); const mrb_method_t nv_mt_vals_1[] = { { .func_p=1, {.func=(mrb_func_t)&gc_start} }, /* ... */ };
  36. Function Name Resolution ‣use nm command to save symbol table

    of nvgen ‣nvgen parses the file and get address of functions ‣This method is used by TOPPERS configurator. $ nm nvgen > nvgen.syms $ ./nvgen nvgen.syms > generated.c
  37. String’s pointer to char* ‣String object has a pointer to

    C string (char*) ‣But String is not mutable ‣To fix this, String used in mruby/mrbgems should be freezed
  38. Using TLSF malloc ‣We want to know if a structure

    is in global variable area or in the heap area ‣Global variable should not be free() ‣Use TLSF malloc instead of standard malloc() ‣With TLSF malloc, we can know the object is in heap or not ‣see github.com/yamanekko/mruby-tlsf for detail
  39. GC supporting ROM and RAM ‣ mruby uses tri-color GC

    (Black, White, Gray) ‣ We use quad-color GC, add Red ‣ Red are never GCed; objects in ROM are Red ‣ At first of GC cycle, all children of Red objects are marked ‣ VM has a list of Red objects
  40. support flags in RAM ‣ obj->flags is used as status

    of objects ‣ flags should be mutable ‣ So an array of flags for ROM’s objects are located in RAM ‣ add macro to read and write them ‣ obj->flags in ROM are indexes of the array
  41. Finding methods ‣ method tables are in both ROM and

    RAM ‣ tables for pre-defined methods are in ROM ‣ using iv_tbl to store pointer of pre-defined method tables ‣ key is __mt2__, which is not used as instance variables