1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

GC measurement feature

* `GC.measure_total_time = true` enables total time measurement (default: true)
* `GC.measure_total_time` returns current flag.
* `GC.total_time` returns measured total time in nano seconds.
* `GC.stat(:time)` (and Hash) returns measured total time in milli seconds.
This commit is contained in:
Koichi Sasada 2021-11-10 16:52:22 +09:00
parent 349a179782
commit c347038d4e
Notes: git 2021-11-19 08:32:29 +09:00
3 changed files with 106 additions and 37 deletions

View file

@ -5852,6 +5852,7 @@ gc.$(OBJEXT): {$(VPATH)}encoding.h
gc.$(OBJEXT): {$(VPATH)}eval_intern.h gc.$(OBJEXT): {$(VPATH)}eval_intern.h
gc.$(OBJEXT): {$(VPATH)}gc.c gc.$(OBJEXT): {$(VPATH)}gc.c
gc.$(OBJEXT): {$(VPATH)}gc.h gc.$(OBJEXT): {$(VPATH)}gc.h
gc.$(OBJEXT): {$(VPATH)}gc.rb
gc.$(OBJEXT): {$(VPATH)}gc.rbinc gc.$(OBJEXT): {$(VPATH)}gc.rbinc
gc.$(OBJEXT): {$(VPATH)}id.h gc.$(OBJEXT): {$(VPATH)}id.h
gc.$(OBJEXT): {$(VPATH)}id_table.h gc.$(OBJEXT): {$(VPATH)}id_table.h

83
gc.c
View file

@ -732,6 +732,7 @@ typedef struct rb_objspace {
#if GC_ENABLE_INCREMENTAL_MARK #if GC_ENABLE_INCREMENTAL_MARK
unsigned int during_incremental_marking : 1; unsigned int during_incremental_marking : 1;
#endif #endif
unsigned int measure_gc : 1;
} flags; } flags;
rb_event_flag_t hook_events; rb_event_flag_t hook_events;
@ -807,7 +808,7 @@ typedef struct rb_objspace {
size_t total_freed_objects; size_t total_freed_objects;
size_t total_allocated_pages; size_t total_allocated_pages;
size_t total_freed_pages; size_t total_freed_pages;
size_t total_time_ns; uint64_t total_time_ns;
struct timespec start_time; struct timespec start_time;
} profile; } profile;
struct gc_list *global_list; struct gc_list *global_list;
@ -1718,6 +1719,7 @@ rb_objspace_t *
rb_objspace_alloc(void) rb_objspace_alloc(void)
{ {
rb_objspace_t *objspace = calloc1(sizeof(rb_objspace_t)); rb_objspace_t *objspace = calloc1(sizeof(rb_objspace_t));
objspace->flags.measure_gc = 1;
malloc_limit = gc_params.malloc_limit_min; malloc_limit = gc_params.malloc_limit_min;
for (int i = 0; i < SIZE_POOL_COUNT; i++) { for (int i = 0; i < SIZE_POOL_COUNT; i++) {
@ -9165,10 +9167,12 @@ gc_enter_count(enum gc_enter_event event)
} }
} }
#define MEASURE_GC 1 #ifndef MEASURE_GC
#define MEASURE_GC (objspace->flags.measure_gc)
#endif
static bool static bool
gc_enter_event_measure_p(enum gc_enter_event event) gc_enter_event_measure_p(rb_objspace_t *objspace, enum gc_enter_event event)
{ {
if (!MEASURE_GC) return false; if (!MEASURE_GC) return false;
@ -9178,35 +9182,49 @@ gc_enter_event_measure_p(enum gc_enter_event event)
case gc_enter_event_sweep_continue: case gc_enter_event_sweep_continue:
case gc_enter_event_rest: case gc_enter_event_rest:
return true; return true;
case gc_enter_event_finalizer:
case gc_enter_event_rb_memerror: default:
// case gc_enter_event_finalizer:
// case gc_enter_event_rb_memerror:
return false; return false;
} }
} }
static bool current_process_time(struct timespec *ts);
static void static void
gc_enter_clock(rb_objspace_t *objspace, enum gc_enter_event event) gc_enter_clock(rb_objspace_t *objspace, enum gc_enter_event event)
{ {
if (gc_enter_event_measure_p(event)) { if (gc_enter_event_measure_p(objspace, event)) {
clock_gettime(CLOCK_MONOTONIC, &objspace->profile.start_time); if (!current_process_time(&objspace->profile.start_time)) {
objspace->profile.start_time.tv_sec = 0;
objspace->profile.start_time.tv_nsec = 0;
}
} }
} }
static void static void
gc_exit_clock(rb_objspace_t *objspace, enum gc_enter_event event) gc_exit_clock(rb_objspace_t *objspace, enum gc_enter_event event)
{ {
if (gc_enter_event_measure_p(event)) { if (gc_enter_event_measure_p(objspace, event)) {
struct timespec end_time; struct timespec end_time;
clock_gettime(CLOCK_MONOTONIC, &end_time); if ((objspace->profile.start_time.tv_sec > 0 ||
objspace->profile.start_time.tv_nsec > 0) &&
current_process_time(&end_time)) {
if (end_time.tv_sec < objspace->profile.start_time.tv_sec) { if (end_time.tv_sec < objspace->profile.start_time.tv_sec) {
return; // ignore return; // ignore
} }
size_t ns = (end_time.tv_sec - objspace->profile.start_time.tv_sec) * 1000 * 1000 * 1000 + end_time.tv_nsec - objspace->profile.start_time.tv_nsec; else {
uint64_t ns =
(uint64_t)(end_time.tv_sec - objspace->profile.start_time.tv_sec) * (1000 * 1000 * 1000) +
(end_time.tv_nsec - objspace->profile.start_time.tv_nsec);
objspace->profile.total_time_ns += ns; objspace->profile.total_time_ns += ns;
} }
} }
}
}
static inline void static inline void
gc_enter(rb_objspace_t *objspace, enum gc_enter_event event, unsigned int *lock_lev) gc_enter(rb_objspace_t *objspace, enum gc_enter_event event, unsigned int *lock_lev)
@ -12411,16 +12429,14 @@ wmap_size(VALUE self)
#define GC_PROFILE_RECORD_DEFAULT_SIZE 100 #define GC_PROFILE_RECORD_DEFAULT_SIZE 100
/* return sec in user time */ static bool
static double current_process_time(struct timespec *ts)
getrusage_time(void)
{ {
#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_PROCESS_CPUTIME_ID) #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_PROCESS_CPUTIME_ID)
{ {
static int try_clock_gettime = 1; static int try_clock_gettime = 1;
struct timespec ts; if (try_clock_gettime && clock_gettime(CLOCK_PROCESS_CPUTIME_ID, ts) == 0) {
if (try_clock_gettime && clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts) == 0) { return true;
return ts.tv_sec + ts.tv_nsec * 1e-9;
} }
else { else {
try_clock_gettime = 0; try_clock_gettime = 0;
@ -12434,7 +12450,9 @@ getrusage_time(void)
struct timeval time; struct timeval time;
if (getrusage(RUSAGE_SELF, &usage) == 0) { if (getrusage(RUSAGE_SELF, &usage) == 0) {
time = usage.ru_utime; time = usage.ru_utime;
return time.tv_sec + time.tv_usec * 1e-6; ts->tv_sec = time.tv_sec;
ts->tv_nsec = time.tv_usec * 1000;
return true;
} }
} }
#endif #endif
@ -12443,28 +12461,33 @@ getrusage_time(void)
{ {
FILETIME creation_time, exit_time, kernel_time, user_time; FILETIME creation_time, exit_time, kernel_time, user_time;
ULARGE_INTEGER ui; ULARGE_INTEGER ui;
LONG_LONG q;
double t;
if (GetProcessTimes(GetCurrentProcess(), if (GetProcessTimes(GetCurrentProcess(),
&creation_time, &exit_time, &kernel_time, &user_time) != 0) { &creation_time, &exit_time, &kernel_time, &user_time) != 0) {
memcpy(&ui, &user_time, sizeof(FILETIME)); memcpy(&ui, &user_time, sizeof(FILETIME));
q = ui.QuadPart / 10L; #define PER100NSEC (uint64_t)(1000 * 1000 * 10)
t = (DWORD)(q % 1000000L) * 1e-6; ts->tv_nsec = (long)(ui.QuadPart % PER100NSEC);
q /= 1000000L; ts->tv_sec = (time_t)(ui.QuadPart / PER100NSEC);
#ifdef __GNUC__ return true;
t += q;
#else
t += (double)(DWORD)(q >> 16) * (1 << 16);
t += (DWORD)q & ~(~0 << 16);
#endif
return t;
} }
} }
#endif #endif
return false;
}
static double
getrusage_time(void)
{
struct timespec ts;
if (current_process_time(&ts)) {
return ts.tv_sec + ts.tv_nsec * 1e-9;
}
else {
return 0.0; return 0.0;
} }
}
static inline void static inline void
gc_prof_setup_new_record(rb_objspace_t *objspace, unsigned int reason) gc_prof_setup_new_record(rb_objspace_t *objspace, unsigned int reason)

45
gc.rb
View file

@ -266,6 +266,51 @@ module GC
def self.using_rvargc? def self.using_rvargc?
GC::INTERNAL_CONSTANTS[:SIZE_POOL_COUNT] > 1 GC::INTERNAL_CONSTANTS[:SIZE_POOL_COUNT] > 1
end end
# call-seq:
# GC.measure_total_time = true/false
#
# Enable to measure GC time.
# You can get the result with `GC.stat(:time)`.
# Note that the GC time measurement can introduce the performance regression.
def self.measure_total_time=(flag)
Primitive.cstmt! %{
rb_objspace.flags.measure_gc = RTEST(flag) ? TRUE : FALSE;
return flag;
}
end
# call-seq:
# GC.measure_total_time -> true/false
#
# Return measure_total_time flag (default: true).
# Note that measurement can affect the application performance.
def self.measure_total_time
Primitive.cexpr! %{
RBOOL(rb_objspace.flags.measure_gc)
}
end
Primitive.cinit! %{
#if SIZEOF_LONG == 8
#define UINT64_2NUM RB_ULONG2NUM
#elif SIZEOF_LONG_LONG == 8
#define UINT64_2NUM RB_ULL2NUM
#else
#error Can not make UINT64_2NUM
#endif
}
# call-seq:
# GC.total_time -> int
#
# Return measured GC total time in nano seconds.
def self.total_time
Primitive.cexpr! %{
UINT64_2NUM(rb_objspace.profile.total_time_ns)
}
end
end end
module ObjectSpace module ObjectSpace