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 <code>GC</code> 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 */
+
+