[project @ 2001-08-07 09:20:52 by simonmar]
authorsimonmar <unknown>
Tue, 7 Aug 2001 09:20:52 +0000 (09:20 +0000)
committersimonmar <unknown>
Tue, 7 Aug 2001 09:20:52 +0000 (09:20 +0000)
- Allow RTS options to be given using the GHCRTS environment variable.

- Fix the heap size calculation to take into account all generations.
  It's more conservative than it used to be, but now it is less likely
  that the maximum heap size will be exceeded.

- Compacting collection is turned on automatically when residency
  reaches 30% of the maximum heap size, tunable with +RTS -c<n>.
  +RTS -c turns off compaction altogether.

- The maximum heap size is off by default.  NOTE: this also means no
  compaction by default.  It is recommended that people enable a maximum
  heap size for their system using the GHCRTS environment var; eg:
  GHCRTS=-M128m.

ghc/rts/GC.c
ghc/rts/RtsFlags.c
ghc/rts/RtsFlags.h
ghc/rts/Storage.c

index 1568f46..2712dba 100644 (file)
@@ -1,5 +1,5 @@
 /* -----------------------------------------------------------------------------
- * $Id: GC.c,v 1.113 2001/08/04 06:07:22 ken Exp $
+ * $Id: GC.c,v 1.114 2001/08/07 09:20:52 simonmar Exp $
  *
  * (c) The GHC Team 1998-1999
  *
@@ -601,7 +601,7 @@ GarbageCollect ( void (*get_roots)(evac_fn), rtsBool force_major_gc )
 
   // NO MORE EVACUATION AFTER THIS POINT!
   // Finally: compaction of the oldest generation.
-  if (major_gc && RtsFlags.GcFlags.compact) {
+  if (major_gc && oldest_gen->steps[0].is_compacted) {
       // save number of blocks for stats
       oldgen_saved_blocks = oldest_gen->steps[0].n_blocks;
       compact(get_roots);
@@ -609,46 +609,6 @@ GarbageCollect ( void (*get_roots)(evac_fn), rtsBool force_major_gc )
 
   IF_DEBUG(sanity, checkGlobalTSOList(rtsFalse));
 
-  /* Set the maximum blocks for the oldest generation, based on twice
-   * the amount of live data now, adjusted to fit the maximum heap
-   * size if necessary.  
-   *
-   * This is an approximation, since in the worst case we'll need
-   * twice the amount of live data plus whatever space the other
-   * generations need.
-   */
-  if (major_gc && RtsFlags.GcFlags.generations > 1) {
-      nat blocks = oldest_gen->steps[0].n_blocks +
-         oldest_gen->steps[0].n_large_blocks;
-
-      oldest_gen->max_blocks = 
-         stg_max(blocks * RtsFlags.GcFlags.oldGenFactor,
-                 RtsFlags.GcFlags.minOldGenSize);
-      if (RtsFlags.GcFlags.compact) {
-         if ( oldest_gen->max_blocks >
-              RtsFlags.GcFlags.maxHeapSize *
-              (100 - RtsFlags.GcFlags.pcFreeHeap) / 100 ) {
-             oldest_gen->max_blocks = 
-                 RtsFlags.GcFlags.maxHeapSize *
-                 (100 - RtsFlags.GcFlags.pcFreeHeap) / 100;
-             if (oldest_gen->max_blocks < blocks) {
-                 belch("max_blocks: %ld, blocks: %ld, maxHeapSize: %ld",
-                       oldest_gen->max_blocks,  blocks, RtsFlags.GcFlags.maxHeapSize);
-                 heapOverflow();
-             }
-         }
-      } else {
-         if (oldest_gen->max_blocks > RtsFlags.GcFlags.maxHeapSize / 2) {
-             oldest_gen->max_blocks = RtsFlags.GcFlags.maxHeapSize / 2;
-             if (((int)oldest_gen->max_blocks - (int)blocks) < 
-                 (RtsFlags.GcFlags.pcFreeHeap *
-                  RtsFlags.GcFlags.maxHeapSize / 200)) {
-                 heapOverflow();
-             }
-         }
-      }
-  }
-
   /* run through all the generations/steps and tidy up 
    */
   copied = new_blocks * BLOCK_SIZE_W;
@@ -734,24 +694,8 @@ GarbageCollect ( void (*get_roots)(evac_fn), rtsBool force_major_gc )
        stp->large_objects  = stp->scavenged_large_objects;
        stp->n_large_blocks = stp->n_scavenged_large_blocks;
 
-       /* Set the maximum blocks for this generation, interpolating
-        * between the maximum size of the oldest and youngest
-        * generations.
-        *
-        * max_blocks =    oldgen_max_blocks * G
-        *                 ----------------------
-        *                      oldest_gen
-        */
-       if (g != 0) {
-#if 0
-         generations[g].max_blocks = (oldest_gen->max_blocks * g)
-              / (RtsFlags.GcFlags.generations-1);
-#endif
-         generations[g].max_blocks = oldest_gen->max_blocks;
-       }
-
-      // for older generations... 
       } else {
+       // for older generations... 
        
        /* For older generations, we need to append the
         * scavenged_large_object list (i.e. large objects that have been
@@ -770,7 +714,72 @@ GarbageCollect ( void (*get_roots)(evac_fn), rtsBool force_major_gc )
     }
   }
 
-  // Guess the amount of live data for stats. 
+  /* Reset the sizes of the older generations when we do a major
+   * collection.
+   *
+   * CURRENT STRATEGY: make all generations except zero the same size.
+   * We have to stay within the maximum heap size, and leave a certain
+   * percentage of the maximum heap size available to allocate into.
+   */
+  if (major_gc && RtsFlags.GcFlags.generations > 1) {
+      nat live, size, min_alloc;
+      nat max  = RtsFlags.GcFlags.maxHeapSize;
+      nat gens = RtsFlags.GcFlags.generations;
+
+      // live in the oldest generations
+      live = oldest_gen->steps[0].n_blocks +
+            oldest_gen->steps[0].n_large_blocks;
+
+      // default max size for all generations except zero
+      size = stg_max(live * RtsFlags.GcFlags.oldGenFactor,
+                    RtsFlags.GcFlags.minOldGenSize);
+
+      // minimum size for generation zero
+      min_alloc = (RtsFlags.GcFlags.pcFreeHeap * max) / 200;
+
+      // if we're going to go over the maximum heap size, reduce the
+      // size of the generations accordingly.  The calculation is
+      // different if compaction is turned on, because we don't need
+      // to double the space required to collect the old generation.
+      if (max != 0) {
+         if (RtsFlags.GcFlags.compact) {
+             if ( (size + (size - 1) * (gens - 2) * 2) + min_alloc > max ) {
+                 size = (max - min_alloc) / ((gens - 1) * 2 - 1);
+             }
+         } else {
+             if ( (size * (gens - 1) * 2) + min_alloc > max ) {
+                 size = (max - min_alloc) / ((gens - 1) * 2);
+             }
+         }
+
+         if (size < live) {
+             heapOverflow();
+         }
+      }
+
+#if 0
+      fprintf(stderr,"live: %d, min_alloc: %d, size : %d, max = %d\n", live,
+             min_alloc, size, max);
+#endif
+
+      for (g = 0; g < gens; g++) {
+         generations[g].max_blocks = size;
+      }
+
+      // Auto-enable compaction when the residency reaches a
+      // certain percentage of the maximum heap size (default: 30%).
+      if (RtsFlags.GcFlags.compact &&
+         oldest_gen->steps[0].n_blocks > 
+           (RtsFlags.GcFlags.compactThreshold * max) / 100) {
+         oldest_gen->steps[0].is_compacted = 1;
+//       fprintf(stderr,"compaction: on\n", live);
+      } else {
+         oldest_gen->steps[0].is_compacted = 0;
+//       fprintf(stderr,"compaction: off\n", live);
+      }
+  }
+
+  // Guess the amount of live data for stats.
   live = calcLive();
 
   /* Free the small objects allocated via allocate(), since this will
@@ -818,9 +827,9 @@ GarbageCollect ( void (*get_roots)(evac_fn), rtsBool force_major_gc )
     /* For a two-space collector, we need to resize the nursery. */
     
     /* set up a new nursery.  Allocate a nursery size based on a
-     * function of the amount of live data (currently a factor of 2,
-     * should be configurable (ToDo)).  Use the blocks from the old
-     * nursery if possible, freeing up any left over blocks.
+     * function of the amount of live data (by default a factor of 2)
+     * Use the blocks from the old nursery if possible, freeing up any
+     * left over blocks.
      *
      * If we get near the maximum heap size, then adjust our nursery
      * size accordingly.  If the nursery is the same size as the live
@@ -829,7 +838,7 @@ GarbageCollect ( void (*get_roots)(evac_fn), rtsBool force_major_gc )
      * 
      * A normal 2-space collector would need 4L bytes to give the same
      * performance we get from 3L bytes, reducing to the same
-     * performance at 2L bytes.  
+     * performance at 2L bytes.
      */
     blocks = g0s0->n_to_blocks;
 
@@ -906,8 +915,7 @@ GarbageCollect ( void (*get_roots)(evac_fn), rtsBool force_major_gc )
     zero_static_object_list(scavenged_static_objects);
   }
 
-  /* Reset the nursery
-   */
+  // Reset the nursery
   resetNurseries();
 
   // start any pending finalizers 
@@ -1002,7 +1010,8 @@ traverse_weak_ptr_list(void)
 
     /* Now, check whether the key is reachable.
      */
-    if ((new = isAlive(w->key))) {
+    new = isAlive(w->key);
+    if (new != NULL) {
       w->key = new;
       // evacuate the value and finalizer 
       w->value = evacuate(w->value);
@@ -1063,7 +1072,6 @@ traverse_weak_ptr_list(void)
          // not alive (yet): leave this thread on the old_all_threads list.
          prev = &(t->global_link);
          next = t->global_link;
-         continue;
       } 
       else {
          // alive: move this thread onto the all_threads list.
@@ -1071,7 +1079,6 @@ traverse_weak_ptr_list(void)
          t->global_link = all_threads;
          all_threads  = t;
          *prev = next;
-         break;
       }
     }
   }
index 3c78d10..7f137a3 100644 (file)
@@ -1,5 +1,5 @@
 /* -----------------------------------------------------------------------------
- * $Id: RtsFlags.c,v 1.42 2001/07/23 23:37:35 andy Exp $
+ * $Id: RtsFlags.c,v 1.43 2001/08/07 09:20:52 simonmar Exp $
  *
  * (c) The AQUA Project, Glasgow University, 1994-1997
  * (c) The GHC Team, 1998-1999
@@ -223,7 +223,7 @@ void initRtsFlagsDefaults(void)
 
     RtsFlags.GcFlags.minAllocAreaSize   = (256 * 1024)        / BLOCK_SIZE;
     RtsFlags.GcFlags.minOldGenSize      = (1024 * 1024)       / BLOCK_SIZE;
-    RtsFlags.GcFlags.maxHeapSize       = (64  * 1024 * 1024) / BLOCK_SIZE;
+    RtsFlags.GcFlags.maxHeapSize       = 0;    /* off by default */
     RtsFlags.GcFlags.heapSizeSuggestion        = 0;    /* none */
     RtsFlags.GcFlags.pcFreeHeap                = 3;    /* 3% */
     RtsFlags.GcFlags.oldGenFactor       = 2;
@@ -235,9 +235,10 @@ void initRtsFlagsDefaults(void)
 #else
     RtsFlags.GcFlags.generations        = 2;
     RtsFlags.GcFlags.steps              = 2;
-    RtsFlags.GcFlags.compact            = rtsFalse;
     RtsFlags.GcFlags.squeezeUpdFrames  = rtsTrue;
 #endif
+    RtsFlags.GcFlags.compact            = rtsTrue;
+    RtsFlags.GcFlags.compactThreshold   = 30.0;
 #ifdef RTS_GTK_FRONTPANEL
     RtsFlags.GcFlags.frontpanel         = rtsFalse;
 #endif
@@ -385,10 +386,12 @@ usage_text[] = {
 "  -A<size> Sets the minimum allocation area size (default 256k) Egs: -A1m -A10k",
 "  -M<size> Sets the maximum heap size (default 64M)  Egs: -M256k -M1G",
 "  -H<size> Sets the minimum heap size (default 0M)   Egs: -H24m  -H1G",
-"  -m<n>%   Minimum % of heap which must be available (default 3%)",
+"  -m<n>    Minimum % of heap which must be available (default 3%)",
 "  -G<n>    Number of generations (default: 2)",
 "  -T<n>    Number of steps in younger generations (default: 2)",
-"  -c       Enable compaction for the oldest generation",
+"  -c<n>    Auto-enable compaction of the oldest generation when live data is",
+"           at least <n>% of the maximum heap size set with -M (default: 30%)",
+"  -c       Disable compaction",
 "",
 "  -t<file> One-line GC statistics  (default file: <program>.stat)",
 "  -s<file> Summary  GC statistics  (with -Sstderr going to stderr)",
@@ -467,6 +470,8 @@ usage_text[] = {
 "  -b...     All GranSim options start with -b; see GranSim User's Guide for details",
 #endif
 "",
+"RTS options may also be specified using the GHCRTS environment variable.",
+"",
 "Other RTS options may be available for programs compiled a different way.",
 "The GHC User's Guide has full details.",
 "",
@@ -527,6 +532,33 @@ setupRtsFlags(int *argc, char *argv[], int *rts_argc, char *rts_argv[])
     argv[*argc] = (char *) 0;
     rts_argv[*rts_argc] = (char *) 0;
 
+    // process arguments from the GHCRTS environment variable.
+    {
+       char *ghc_rts = getenv("GHCRTS");
+       char *c1, *c2, *s;
+
+       if (ghc_rts != NULL) {
+           c1 = ghc_rts;
+           do {
+               while (isspace(*c1)) { c1++; };
+               c2 = c1;
+               while (!isspace(*c2) && *c2 != '\0') { c2++; };
+
+               if (c1 == c2) { break; }
+
+               if (*rts_argc < MAX_RTS_ARGS-1) {
+                   s = malloc(c2-c1+1);
+                   strncpy(s, c1, c2-c1);
+                   s[c2-c1] = '\0';
+                   rts_argv[(*rts_argc)++] = s;
+               } else {
+                   barf("too many RTS arguments (max %d)", MAX_RTS_ARGS-1);
+               }
+
+               c1 = c2;
+           } while (*c1 != '\0');
+       }
+    }
     /* Process RTS (rts_argv) part: mainly to determine statsfile */
 
     for (arg = 0; arg < *rts_argc; arg++) {
@@ -620,8 +652,14 @@ error = rtsTrue;
                break;
 
              case 'c':
-               RtsFlags.GcFlags.compact = rtsTrue;
-               break;
+                 if (rts_argv[arg][2] != '\0') {
+                     RtsFlags.GcFlags.compact = rtsTrue;
+                     RtsFlags.GcFlags.compactThreshold =
+                         atof(rts_argv[arg]+2);
+                 } else {
+                     RtsFlags.GcFlags.compact = rtsFalse;
+                 }
+                 break;
 
              case 'F':
                RtsFlags.GcFlags.oldGenFactor = atof(rts_argv[arg]+2);
index 26f9a9c..ab493d2 100644 (file)
@@ -1,5 +1,5 @@
 /* -----------------------------------------------------------------------------
- * $Id: RtsFlags.h,v 1.34 2001/07/23 17:23:19 simonmar Exp $
+ * $Id: RtsFlags.h,v 1.35 2001/08/07 09:20:52 simonmar Exp $
  *
  * (c) The GHC Team, 1998-1999
  *
@@ -33,9 +33,11 @@ struct GC_FLAGS {
 
     nat     generations;
     nat     steps;
-    rtsBool compact;      
     rtsBool squeezeUpdFrames;
 
+    rtsBool compact;
+    double  compactThreshold;
+
     rtsBool ringBell;
     rtsBool frontpanel;
 };
index 1e53758..b8de7d3 100644 (file)
@@ -1,5 +1,5 @@
 /* -----------------------------------------------------------------------------
- * $Id: Storage.c,v 1.42 2001/07/24 16:36:43 simonmar Exp $
+ * $Id: Storage.c,v 1.43 2001/08/07 09:20:52 simonmar Exp $
  *
  * (c) The GHC Team, 1998-1999
  *
@@ -160,10 +160,7 @@ initStorage (void)
     generations[g].steps[s].to = &generations[g+1].steps[0];
   }
   
-  /* The oldest generation has one step and it is compacted. */
-  if (RtsFlags.GcFlags.compact) {
-      oldest_gen->steps[0].is_compacted = 1;
-  }
+  /* The oldest generation has one step. */
   oldest_gen->steps[0].to = &oldest_gen->steps[0];
 
   /* generation 0 is special: that's the nursery */
@@ -625,25 +622,25 @@ calcLive(void)
 extern lnat 
 calcNeeded(void)
 {
-  lnat needed = 0;
-  nat g, s;
-  step *stp;
-
-  for (g = 0; g < RtsFlags.GcFlags.generations; g++) {
-    for (s = 0; s < generations[g].n_steps; s++) {
-      if (g == 0 && s == 0) { continue; }
-      stp = &generations[g].steps[s];
-      if (generations[g].steps[0].n_blocks +
-         generations[g].steps[0].n_large_blocks 
-         > generations[g].max_blocks
-         && stp->is_compacted == 0) {
-       needed += 2 * stp->n_blocks;
-      } else {
-       needed += stp->n_blocks;
-      }
+    lnat needed = 0;
+    nat g, s;
+    step *stp;
+    
+    for (g = 0; g < RtsFlags.GcFlags.generations; g++) {
+       for (s = 0; s < generations[g].n_steps; s++) {
+           if (g == 0 && s == 0) { continue; }
+           stp = &generations[g].steps[s];
+           if (generations[g].steps[0].n_blocks +
+               generations[g].steps[0].n_large_blocks 
+               > generations[g].max_blocks
+               && stp->is_compacted == 0) {
+               needed += 2 * stp->n_blocks;
+           } else {
+               needed += stp->n_blocks;
+           }
+       }
     }
-  }
-  return needed;
+    return needed;
 }
 
 /* -----------------------------------------------------------------------------