From: authorNari Date: 2008-07-13T14:35:13+09:00 Subject: [ruby-dev:35458] make profiler for gc nariです。 GC用のプロファイラを作りました。 問題が無ければコミットしたのですが、いかがでしょうか? --- nari Index: gc.c =================================================================== --- gc.c (リビジョン 18054) +++ gc.c (作業コピー) @@ -179,6 +179,10 @@ struct gc_list *global_list; unsigned int count; int gc_stress; +#if GC_PROFILE + gc_profile_data *gc_profile; + size_t gc_profile_size; +#endif } rb_objspace_t; #if defined(ENABLE_VM_OBJSPACE) && ENABLE_VM_OBJSPACE @@ -212,6 +216,11 @@ #define need_call_final (finalizer_table && finalizer_table->num_entries) +#if GC_PROFILE +#define gc_profile objspace->gc_profile +#define gc_profile_size objspace->gc_profile_size +#endif + #if defined(ENABLE_VM_OBJSPACE) && ENABLE_VM_OBJSPACE rb_objspace_t * rb_objspace_alloc(void) @@ -1440,6 +1449,7 @@ freed += n; } } + GC_PROF_SET_MALLOC_INFO; if (malloc_increase > malloc_limit) { malloc_limit += (malloc_increase - malloc_limit) * (double)live / (live + freed); if (malloc_limit < GC_MALLOC_LIMIT) malloc_limit = GC_MALLOC_LIMIT; @@ -1453,10 +1463,12 @@ /* clear finalization list */ if (final_list) { + GC_PROF_SET_INFO; deferred_final_list = final_list; return; } free_unused_heaps(objspace); + GC_PROF_SET_INFO; } void @@ -1672,6 +1684,7 @@ { struct gc_list *list; rb_thread_t *th = GET_THREAD(); + INIT_GC_PROF_PARAMS; if (GC_NOTIFY) printf("start garbage_collect()\n"); @@ -1691,6 +1704,8 @@ during_gc++; objspace->count++; + GC_PROF_TIMER_START; + GC_PROF_MARK_TIMER_START; SET_STACK_END; init_mark_stack(objspace); @@ -1731,9 +1746,13 @@ gc_mark_rest(objspace); } } + GC_PROF_MARK_TIMER_STOP; + GC_PROF_SWEEP_TIMER_START; gc_sweep(objspace); + GC_PROF_SWEEP_TIMER_STOP; + GC_PROF_TIMER_STOP; if (GC_NOTIFY) printf("end garbage_collect()\n"); return Qtrue; } @@ -2386,6 +2405,36 @@ } #endif +#if GC_PROFILE +VALUE +gc_profile_report(void) +{ + VALUE prof = 0; + VALUE res = rb_hash_new(); + VALUE datas = rb_ary_new(); + size_t i; + rb_objspace_t *objspace = (&rb_objspace); + + for (i =0; i < objspace->count; i++) { + prof = rb_hash_new(); + rb_hash_aset(prof, rb_str_new2("gc_time"), DOUBLE2NUM(gc_profile[i]._gc_time)); + rb_hash_aset(prof, rb_str_new2("gc_mark_time"), DOUBLE2NUM(gc_profile[i]._gc_mark_time)); + rb_hash_aset(prof, rb_str_new2("gc_sweep_time"), DOUBLE2NUM(gc_profile[i]._gc_sweep_time)); + rb_hash_aset(prof, rb_str_new2("heaps_used"), rb_uint2inum(gc_profile[i]._heaps_used)); + rb_hash_aset(prof, rb_str_new2("live"), rb_uint2inum(gc_profile[i]._live)); + rb_hash_aset(prof, rb_str_new2("freed"), rb_uint2inum(gc_profile[i]._freed)); + rb_hash_aset(prof, rb_str_new2("heap_objects"), rb_uint2inum(gc_profile[i]._heap_objects)); + rb_hash_aset(prof, rb_str_new2("is_exist_finalize"), gc_profile[i]._is_exist_finalize); + rb_hash_aset(prof, rb_str_new2("malloc_increase"), rb_uint2inum(gc_profile[i]._malloc_increase)); + rb_hash_aset(prof, rb_str_new2("malloc_limit"), rb_uint2inum(gc_profile[i]._malloc_limit)); + rb_ary_push(datas, prof); + } + rb_hash_aset(res, rb_str_new2("count"), rb_uint2inum(objspace->count)); + rb_hash_aset(res, rb_str_new2("report"), datas); + return res; +} +#endif + /* * The GC module provides an interface to Ruby's mark and * sweep garbage collection mechanism. Some of the underlying methods @@ -2430,4 +2479,7 @@ rb_define_singleton_method(rb_mGC, "malloc_allocated_size", gc_malloc_allocated_size, 0); rb_define_singleton_method(rb_mGC, "malloc_allocations", gc_malloc_allocations, 0); #endif +#if GC_PROFILE + rb_define_singleton_method(rb_mGC, "profile_report", gc_profile_report, 0); +#endif } Index: gc.h =================================================================== --- gc.h (リビジョン 18054) +++ gc.h (作業コピー) @@ -72,4 +72,102 @@ # define STACK_UPPER(x, a, b) (stack_growup_p(x) ? a : b) #endif + +/* for GC profile */ +#define GC_PROFILE 0 +#if GC_PROFILE + +typedef struct gc_profile_data { + double _gc_time; + double _gc_mark_time; + double _gc_sweep_time; + size_t _heaps_used; + size_t _live; + size_t _freed; + size_t _heap_objects; + int _is_exist_finalize; + size_t _malloc_increase; + size_t _malloc_limit; +} gc_profile_data; + +static double +cputime(void) +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_sec + (double)tv.tv_usec*1e-6; +} + +#define INIT_GC_PROF_PARAMS double gc_time = 0, mark_time = 0, sweep_time = 0;\ + size_t count = objspace->count\ + +#define GC_PROF_TIMER_START do {\ + if (!gc_profile) {\ + gc_profile_size = 1000;\ + gc_profile = malloc(sizeof(gc_profile_data) * gc_profile_size);\ + }\ + if (count >= gc_profile_size) {\ + gc_profile_size += 1000;\ + gc_profile = realloc(gc_profile, sizeof(gc_profile_data) * gc_profile_size);\ + }\ + if (!gc_profile) {\ + rb_bug("gc_profile malloc or realloc miss");\ + }\ + MEMZERO(&gc_profile[count], gc_profile_data, 1);\ + gc_time = cputime();\ +} while(0) + +#define GC_PROF_TIMER_STOP do {\ + gc_time = cputime() - gc_time;\ + gc_profile[count]._gc_time = gc_time;\ +} while(0) + +#define GC_PROF_MARK_TIMER_START do {\ + mark_time = cputime();\ +} while(0) + +#define GC_PROF_MARK_TIMER_STOP do {\ + mark_time = cputime() - mark_time;\ + gc_profile[count]._gc_mark_time = mark_time;\ +} while(0) + +#define GC_PROF_SWEEP_TIMER_START do {\ + sweep_time = cputime();\ +} while(0) + +#define GC_PROF_SWEEP_TIMER_STOP do {\ + sweep_time = cputime() - sweep_time;\ + gc_profile[count]._gc_sweep_time = sweep_time;\ +} while(0) + +#define GC_PROF_SET_MALLOC_INFO do {\ + size_t count = objspace->count - 1;\ + gc_profile[count]._malloc_increase = malloc_increase;\ + gc_profile[count]._malloc_limit = malloc_limit;\ +} while(0) + +#define GC_PROF_SET_INFO do {\ + size_t count = objspace->count - 1;\ + gc_profile[count]._heaps_used = heaps_used;\ + gc_profile[count]._live = live;\ + gc_profile[count]._freed = freed;\ + gc_profile[count]._heap_objects = heaps_used * HEAP_OBJ_LIMIT;\ + gc_profile[count]._is_exist_finalize = final_list ? Qtrue : Qfalse; \ +} while(0) + +#else +#define INIT_GC_PROF_PARAMS +#define GC_PROF_TIMER_START +#define GC_PROF_TIMER_STOP +#define GC_PROF_MARK_TIMER_START +#define GC_PROF_MARK_TIMER_STOP +#define GC_PROF_SWEEP_TIMER_START +#define GC_PROF_SWEEP_TIMER_STOP +#define GC_PROF_SET_MALLOC_INFO +#define GC_PROF_SET_INFO +#endif + + #endif /* RUBY_GC_H */ + +