Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?

CRubyのGCの構造改革

やりたいこと

以下の6つの機能を分離し、簡単に切り替えられるようにしたい。

  1. OSからのメモリアロケータ
  • OSからメモリアロケートする部分のAPI
  1. Rubyのヒープ管理
  • ヒープの設定を保持
    • e.g. malloc_limit,heap_min_slots...
  • オブジェクトの情報を提供
    • e.g. is_pointer_heap(), is_dead/live_object()...
  • ObjectSpaceモジュールのAPI提供
    • e.g. ObjectSpace.count_objects(),_id2ref(),each_object()...
  1. GCアルゴリズム
  • マークフェーズの関数
  • スイープフェーズの関数
  • GCモジュール用のstart,enable,disable,count
    • e.g. GC.start(),enable(),disable(),count()..
      • ヒープとGCは一体にする
  1. オブジェクトのアロケータ
  • オブジェクトの型を受け取って割り当てたオブジェクトを返す
    • こっちもヒープ管理のAPIで提供
  1. オブジェクトのfree
  • オブジェクトを受け取ってfreeするもの
  1. GCプロファイラのAPI
  • もうちょっと切り替えやすいもの

gc.cが抱えるAPI

gc.c上でstaticではない関数

API化が不要なものは(不要)マークを付けておく。

  • rb_newobj
    • rb_node_newnode
    • rb_data_object_alloc
    • rb_data_typed_object_alloc
  • rb_gc_set_params
  • rb_objspace_alloc
  • rb_objspace_free
  • rb_memerror
  • ruby_xmalloc2
  • ruby_xcalloc
  • ruby_xrealloc
  • ruby_xrealloc2
  • ruby_xfree
  • ruby_mimmalloc (不要)
  • rb_gc_register_mark_object (不要)
  • rb_gc_register_address (不要)
    • rb_global_variable (不要)
  • rb_gc_unregister_address (不要)
  • rb_during_gc
  • ruby_get_stack_grow_direction (不要)
  • ruby_stack_length (不要)
  • ruby_stack_check (不要)
  • rb_gc_mark_locations
  • rb_mark_set
  • rb_mark_hash
  • rb_mark_method_entry
  • rb_free_m_table
  • rb_free_const_table
  • rb_mark_tbl
  • rb_gc_mark_maybe
  • rb_gc_mark
  • rb_garbage_collect
  • rb_gc_mark_machine_stack
  • rb_gc_start
  • rb_gc_force_recycle
  • Init_stack (不要)
  • Init_heap
  • rb_objspace_each_objects
  • rb_undefine_final (不要)
  • rb_define_final (不要)
  • rb_gc_copy_finalizer (不要)
  • rb_gc_finalize_deferred (不要)
  • rb_gc_call_finalizer_at_exit (不要)
  • rb_gc
  • rb_obj_id

Ruby上で公開

  • GC

    • gc_stress_get
    • gc_stress_set
    • rb_gc_enable
    • rb_gc_disable
    • gc_count
    • gc_stat
    • rb_gc_start
    • gc_malloc_allocated_size
    • gc_malloc_allocations
  • GC::Profiler

    • gc_profile_enable_get
    • gc_profile_enable
    • gc_profile_disable
    • gc_profile_clear
    • gc_profile_record_get
      • gc_profile_total_time
    • gc_profile_result
      • gc_profile_report
    • gc_profile_disable
    • gc_profile_clear
  • Objspace

    • os_each_obj
    • rb_gc_start
    • define_final
    • undefine_final
    • rb_objspace_data_type_memsize
    • rb_objspace_data_type_name
    • id2ref
    • count_objects
  • Objspace::WeakMap (独立しているため共通のもので良さそう)

    • wmap_allocate
    • wmap_aset
    • wmap_aref
    • wmap_finalize

どんなかんじのインターフェイスにしよう?

typedef struct {
  union { 
    struct rb_ms_objspace_t ms;
    struct rb_bitmap_ms_objspace_t bitmap;
  } as;
} rb_objspace_t;

#define RB_OBJSPACE_API\
  共通API群;
#define RB_OBJSPACE_VALUES\
  共通の変数群;

typedef struct {
  RB_OBJSPACE_API;
  RB_OBJSPACE_VALUES;
  MSの変数群;
} rb_ms_objspace_t;
  
typedef struct {
  RB_OBJSPACE_API;
  RB_OBJSPACE_VALUES;
  BitmapMS用フラグ群;
} rb_bitmap_ms_objspace_t;

共通API群

自分の初期化・後始末

  • void alloc(rb_objspace_t *objspace);
    • rb_objspace_alloc
  • void free(rb_objspace_t *objspace);
    • rb_objspace_free

オブジェクトとるやつ

  • VALUE obj_alloc(VALUE klass);
    • klassが指定されなかったらT_NONEとする
  • void obj_free(VALUE);

OSからめもりとるやつ

  • void * xcalloc(size_t count, size_t elsize);
  • void * xmalloc(size_t size)
  • void xfree(ptr)
    • ruby_xmalloc2, ruby_xcalloc, ruby_xrealloc, ruby_xrealloc2, ruby_xfree

ヒープ初期化

  • void init_heap(rb_objspace_t *objspace)

GC起動

  • int garbage_collect(rb_objspace_t *objspace)

each_object系

  • VALUE objspace_each_objects(VALUE arg)
  • int count_objects(int argc, VALUE *argv, VALUE os)

オブジェクトに関する操作

  • void gc_mark(rb_objspace_t *objspace, VALUE ptr, int lev);
  • int is_pointer_to_heap(rb_objspace_t *objspace, ptr);
  • int is_dead_object(rb_objspace_t *objspace, ptr);
  • int is_live_object(rb_objspace_t *objspace, ptr);
  • VALUE id2ref(rb_objspace_t *objspace, VALUE obj, VALUE objid);
  • void force_recycle(rb_objspace_t *objspace, VALUE p);
  • void finish_finalize(rb_objspace_t *objspace, VALUE p);
    • finalizeしたあとでslotのlimit削る

共通変数

  • flags
    • int dont_gc;
    • int during_gc;
    • int stress;
    • rb_atomic finalizing;
    • size_t count;
    • int gc_stress;
  • malloc_params
    • size_t limit;
    • size_t increase;
    • size_t allocated_size;
    • size_t allocations;
  • struct gc_list *global_list;

Profiler

  • gc_profiler *profiler

実装イメージ:

profiler系は別ファイルの構造体で管理する。
typedef struct gc_profile_record {
  ...;
} gc_profile_record;

typedef struct gc_profiler {
  gc_profile_record *records;
  void (*profile) (rb_objspace_t objspace, enum gc_profile_type);
} gc_profiler_t;
  • gc_profile(rb_objspace_t objspace, enum gc_profile_type, ...)
    • 上記の関数だけ提供

Finalizer

  • final
    • st_table *table;
    • RVALUE *deferred;

Finalizer系は共通の関数でよいだろう。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment