1.0.20.6: smaller allocation regions & reduced pinning
authorNikodemus Siivola <nikodemus@random-state.net>
Mon, 15 Sep 2008 14:09:56 +0000 (14:09 +0000)
committerNikodemus Siivola <nikodemus@random-state.net>
Mon, 15 Sep 2008 14:09:56 +0000 (14:09 +0000)
 * Align objects at least one page in size on page boundaries.
   Previously only "large" objects (those at least 4 pages in size)
   were guaranteed page alignment.

 * Don't allow small objects to cross page boundaries.

 * The effect is to reduce the size of continuous allocation regions
   that start and stop on page boundaries. Since GENCGC conservativism
   operates on such regions, it's effects are reduced as well: for
   SBCL self build this reduces the number of pinned pages by ~45%.

 * Also report the amount of bytes found (in case of allocation
   failure) more accurately.

NEWS
src/runtime/gencgc.c
version.lisp-expr

diff --git a/NEWS b/NEWS
index 1cc248c..449af42 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,7 @@
 ;;;; -*- coding: utf-8; -*-
+  * reduced conservativism on GENCGC platforms: on average 45% less
+    pages pinned (measured from SBCL self build).
+
 changes in sbcl-1.0.20 relative to 1.0.19:
   * minor incompatible change: OPTIMIZE qualities
     SB-C::STACK-ALLOCATE-DYNAMIC-EXTENT, SB-C::STACK-ALLOCATE-VECTOR,
index 1be0721..4f48966 100644 (file)
@@ -1113,76 +1113,82 @@ gc_heap_exhausted_error_or_lose (long available, long requested)
 page_index_t
 gc_find_freeish_pages(page_index_t *restart_page_ptr, long nbytes, int unboxed)
 {
-    page_index_t first_page;
-    page_index_t last_page;
-    long region_size;
-    page_index_t restart_page=*restart_page_ptr;
-    long bytes_found;
-    long num_pages;
-    int large_p=(nbytes>=large_object_size);
+    page_index_t first_page, last_page;
+    page_index_t restart_page = *restart_page_ptr;
+    long bytes_found = 0;
+    long most_bytes_found = 0;
     /* FIXME: assert(free_pages_lock is held); */
 
-    /* Search for a contiguous free space of at least nbytes. If it's
-     * a large object then align it on a page boundary by searching
-     * for a free page. */
-
+    /* Toggled by gc_and_save for heap compaction, normally -1. */
     if (gencgc_alloc_start_page != -1) {
         restart_page = gencgc_alloc_start_page;
     }
 
-    do {
-        first_page = restart_page;
-        if (large_p)
-            while ((first_page < page_table_pages)
-                   && (page_table[first_page].allocated != FREE_PAGE_FLAG))
-                first_page++;
-        else
-            while (first_page < page_table_pages) {
-                if(page_table[first_page].allocated == FREE_PAGE_FLAG)
-                    break;
-                if((page_table[first_page].allocated ==
-                    (unboxed ? UNBOXED_PAGE_FLAG : BOXED_PAGE_FLAG)) &&
-                   (page_table[first_page].large_object == 0) &&
-                   (page_table[first_page].gen == gc_alloc_generation) &&
-                   (page_table[first_page].bytes_used < (PAGE_BYTES-32)) &&
-                   (page_table[first_page].write_protected == 0) &&
-                   (page_table[first_page].dont_move == 0)) {
-                    break;
-                }
+    if (nbytes>=PAGE_BYTES) {
+        /* Search for a contiguous free space of at least nbytes,
+         * aligned on a page boundary. The page-alignment is strictly
+         * speaking needed only for objects at least large_object_size
+         * bytes in size. */
+        do {
+            first_page = restart_page;
+            while ((first_page < page_table_pages) &&
+                   (page_table[first_page].allocated != FREE_PAGE_FLAG))
                 first_page++;
-            }
-
-        if (first_page >= page_table_pages)
-            gc_heap_exhausted_error_or_lose(0, nbytes);
 
-        gc_assert(page_table[first_page].write_protected == 0);
+            last_page = first_page;
+            bytes_found = PAGE_BYTES;
+            while ((bytes_found < nbytes) &&
+                   (last_page < (page_table_pages-1)) &&
+                   (page_table[last_page+1].allocated == FREE_PAGE_FLAG)) {
+                last_page++;
+                bytes_found += PAGE_BYTES;
+                gc_assert(page_table[last_page].write_protected == 0);
+            }
+            if (bytes_found > most_bytes_found)
+                most_bytes_found = bytes_found;
+            restart_page = last_page + 1;
+        } while ((restart_page < page_table_pages) && (bytes_found < nbytes));
 
-        last_page = first_page;
-        bytes_found = PAGE_BYTES - page_table[first_page].bytes_used;
-        num_pages = 1;
-        while (((bytes_found < nbytes)
-                || (!large_p && (num_pages < 2)))
-               && (last_page < (page_table_pages-1))
-               && (page_table[last_page+1].allocated == FREE_PAGE_FLAG)) {
-            last_page++;
-            num_pages++;
-            bytes_found += PAGE_BYTES;
-            gc_assert(page_table[last_page].write_protected == 0);
+    } else {
+        /* Search for a page with at least nbytes of space. We prefer
+         * not to split small objects on multiple pages, to reduce the
+         * number of contiguous allocation regions spaning multiple
+         * pages: this helps avoid excessive conservativism. */
+        first_page = restart_page;
+        while (first_page < page_table_pages) {
+            if (page_table[first_page].allocated == FREE_PAGE_FLAG)
+                {
+                    bytes_found = PAGE_BYTES;
+                    break;
+                }
+            else if ((page_table[first_page].allocated ==
+                      (unboxed ? UNBOXED_PAGE_FLAG : BOXED_PAGE_FLAG)) &&
+                     (page_table[first_page].large_object == 0) &&
+                     (page_table[first_page].gen == gc_alloc_generation) &&
+                     (page_table[first_page].write_protected == 0) &&
+                     (page_table[first_page].dont_move == 0))
+                {
+                    bytes_found = PAGE_BYTES - page_table[first_page].bytes_used;
+                    if (bytes_found > most_bytes_found)
+                        most_bytes_found = bytes_found;
+                    if (bytes_found >= nbytes)
+                        break;
+                }
+            first_page++;
         }
-
-        region_size = (PAGE_BYTES - page_table[first_page].bytes_used)
-            + PAGE_BYTES*(last_page-first_page);
-
-        gc_assert(bytes_found == region_size);
-        restart_page = last_page + 1;
-    } while ((restart_page < page_table_pages) && (bytes_found < nbytes));
+        last_page = first_page;
+        restart_page = first_page + 1;
+    }
 
     /* Check for a failure */
-    if ((restart_page >= page_table_pages) && (bytes_found < nbytes))
-        gc_heap_exhausted_error_or_lose(bytes_found, nbytes);
+    if (bytes_found < nbytes) {
+        gc_assert(restart_page >= page_table_pages);
+        gc_heap_exhausted_error_or_lose(most_bytes_found, nbytes);
+    }
 
-    *restart_page_ptr=first_page;
+    gc_assert(page_table[first_page].write_protected == 0);
 
+    *restart_page_ptr = first_page;
     return last_page;
 }
 
index 422c46f..de3b135 100644 (file)
@@ -17,4 +17,4 @@
 ;;; checkins which aren't released. (And occasionally for internal
 ;;; versions, especially for internal versions off the main CVS
 ;;; branch, it gets hairier, e.g. "0.pre7.14.flaky4.13".)
-"1.0.20.5"
+"1.0.20.6"