Add basic calculation of the total full-collection time
authorPaul Bone <paul@bone.id.au>
Fri, 10 Nov 2017 07:12:57 +0000 (10:12 +0300)
committerIvan Maidanski <ivmai@mail.ru>
Fri, 10 Nov 2017 07:19:04 +0000 (10:19 +0300)
Issue #139 (bdwgc).

New API functions: GC_start_performance_measurement,
GC_get_full_gc_total_time.

This patch is based on code originally written by Zoltan Somogyi
on 2008-03-18.

* alloc.c [!NO_CLOCK] (full_gc_total_time, measure_performance): New
static variable definition; add comment.
* alloc.c [!NO_CLOCK] (GC_start_performance_measurement,
GC_get_full_gc_total_time): New API function definition.
* alloc.c [!NO_CLOCK] (GC_try_to_collect_inner): Declare
start_time_valid local variable; set start_time_valid to true if
GET_TIME(start_time) is called; call GET_TIME(start_time) also if
measure_performance; declare time_diff local variable (used to store
the result of MS_TIME_DIFF());  GET_TIME(current_time) is called only
if start_time_valid; update full_gc_total_time if measure_performance.
* include/gc.h (GC_start_performance_measurement,
GC_get_full_gc_total_time): New API function declaration.
* tests/test.c (INIT_PERF_MEASUREMENT): New macro.
* tests/test.c (GC_COND_INIT): Call INIT_PERF_MEASUREMENT.
* tests/test.c [!NO_CLOCK] (check_heap_stats): Call
GC_get_full_gc_total_time() and print the total time of full
collections.

alloc.c
include/gc.h
tests/test.c

diff --git a/alloc.c b/alloc.c
index 551092f..bca4f02 100644 (file)
--- a/alloc.c
+++ b/alloc.c
@@ -63,6 +63,23 @@ word GC_non_gc_bytes = 0;  /* Number of bytes not intended to be collected */
 
 word GC_gc_no = 0;
 
+#ifndef NO_CLOCK
+  static unsigned long full_gc_total_time = 0; /* in msecs, may wrap */
+  static GC_bool measure_performance = FALSE;
+                /* Do performance measurements if set to true (e.g.,    */
+                /* accumulation of the total time of full collections). */
+
+  GC_API void GC_CALL GC_start_performance_measurement(void)
+  {
+    measure_performance = TRUE;
+  }
+
+  GC_API unsigned long GC_CALL GC_get_full_gc_total_time(void)
+  {
+    return full_gc_total_time;
+  }
+#endif /* !NO_CLOCK */
+
 #ifndef GC_DISABLE_INCREMENTAL
   GC_INNER GC_bool GC_incremental = FALSE; /* By default, stop the world. */
 #endif
@@ -442,6 +459,7 @@ GC_INNER GC_bool GC_try_to_collect_inner(GC_stop_func stop_func)
 {
 #   ifndef NO_CLOCK
       CLOCK_TYPE start_time = 0; /* initialized to prevent warning. */
+      GC_bool start_time_valid;
 #   endif
 
     ASSERT_CANCEL_DISABLED();
@@ -463,9 +481,12 @@ GC_INNER GC_bool GC_try_to_collect_inner(GC_stop_func stop_func)
     }
     GC_notify_full_gc();
 #   ifndef NO_CLOCK
-      if (GC_print_stats) {
+      start_time_valid = FALSE;
+      if ((GC_print_stats | (int)measure_performance) != 0) {
+        if (GC_print_stats)
+          GC_log_printf("Initiating full world-stop collection!\n");
+        start_time_valid = TRUE;
         GET_TIME(start_time);
-        GC_log_printf("Initiating full world-stop collection!\n");
       }
 #   endif
     GC_promote_black_lists();
@@ -504,12 +525,16 @@ GC_INNER GC_bool GC_try_to_collect_inner(GC_stop_func stop_func)
     }
     GC_finish_collection();
 #   ifndef NO_CLOCK
-      if (GC_print_stats) {
+      if (start_time_valid) {
         CLOCK_TYPE current_time;
+        unsigned long time_diff;
 
         GET_TIME(current_time);
-        GC_log_printf("Complete collection took %lu msecs\n",
-                      MS_TIME_DIFF(current_time,start_time));
+        time_diff = MS_TIME_DIFF(current_time, start_time);
+        if (measure_performance)
+          full_gc_total_time += time_diff; /* may wrap */
+        if (GC_print_stats)
+          GC_log_printf("Complete collection took %lu msecs\n", time_diff);
       }
 #   endif
     if (GC_on_collection_event)
index 956458d..6b50d29 100644 (file)
@@ -395,6 +395,20 @@ GC_API unsigned long GC_CALL GC_get_time_limit(void);
 
 /* Public procedures */
 
+/* Tell the collector to start various performance measurements.        */
+/* Only the total time taken by full collections is calculated, as      */
+/* of now.  And, currently, there is no way to stop the measurements.   */
+/* The function does not use any synchronization.  Defined only if the  */
+/* library has been compiled without NO_CLOCK.                          */
+GC_API void GC_CALL GC_start_performance_measurement(void);
+
+/* Get the total time of all full collections since the start of the    */
+/* performance measurements.  The measurement unit is one millisecond.  */
+/* Note that the returned value wraps around on overflow.               */
+/* The function does not use any synchronization.  Defined only if the  */
+/* library has been compiled without NO_CLOCK.                          */
+GC_API unsigned long GC_CALL GC_get_full_gc_total_time(void);
+
 /* Set whether the GC will allocate executable memory pages or not.     */
 /* A non-zero argument instructs the collector to allocate memory with  */
 /* the executable flag on.  Must be called before the collector is      */
index 453bfe7..71a04b5 100644 (file)
 #  define GC_OPT_INIT /* empty */
 #endif
 
+#ifdef NO_CLOCK
+# define INIT_PERF_MEASUREMENT (void)0
+#else
+# define INIT_PERF_MEASUREMENT GC_start_performance_measurement()
+#endif
+
 #define GC_COND_INIT() \
-    INIT_FORK_SUPPORT; GC_OPT_INIT; CHECK_GCLIB_VERSION; INIT_PRINT_STATS
+    INIT_FORK_SUPPORT; GC_OPT_INIT; CHECK_GCLIB_VERSION; \
+    INIT_PRINT_STATS; INIT_PERF_MEASUREMENT
 
 #define CHECK_OUT_OF_MEMORY(p) \
             if ((p) == NULL) { \
@@ -1726,6 +1733,9 @@ void check_heap_stats(void)
       GC_unregister_my_thread(); /* just to check it works (for main) */
 #   endif
     GC_printf("Completed %u collections", (unsigned)GC_get_gc_no());
+#   ifndef NO_CLOCK
+      GC_printf(" in %lu msecs", GC_get_full_gc_total_time());
+#   endif
 #   ifdef PARALLEL_MARK
       GC_printf(" (using %d marker threads)", GC_get_parallel() + 1);
 #   endif