[pypy-commit] stmgc c5: more in-progress

arigo noreply at buildbot.pypy.org
Fri Dec 20 10:57:15 CET 2013


Author: Armin Rigo <arigo at tunes.org>
Branch: c5
Changeset: r569:96ac76cc0a32
Date: 2013-12-20 10:57 +0100
http://bitbucket.org/pypy/stmgc/changeset/96ac76cc0a32/

Log:	more in-progress

diff --git a/c5/demo2.c b/c5/demo2.c
new file mode 100644
--- /dev/null
+++ b/c5/demo2.c
@@ -0,0 +1,14 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+
+char *stm_large_malloc(size_t request_size);
+
+
+int main()
+{
+    printf("%p\n", stm_large_malloc(10000));
+    printf("%p\n", stm_large_malloc(10000));
+    printf("%p\n", stm_large_malloc(10000));
+    return 0;
+}
diff --git a/c5/largemalloc.c b/c5/largemalloc.c
--- a/c5/largemalloc.c
+++ b/c5/largemalloc.c
@@ -3,6 +3,11 @@
    blocks, which in our case means at least 288 bytes.
 */
 
+#include <stdlib.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <assert.h>
+
 
 #define MMAP_LIMIT    (1280*1024)
 
@@ -15,32 +20,34 @@
                             83)
 #define N_BINS              84
 
+typedef struct dlist_s {
+    struct dlist_s *next;   /* a doubly-linked list */
+    struct dlist_s *prev;
+} dlist_t;
+
 typedef struct malloc_chunk {
-    size_t prev_size;     /* - if the previous chunk is free: its size
+    size_t prev_size;     /* - if the previous chunk is free: size of its data
                              - otherwise, if this chunk is free: 1
                              - otherwise, 0. */
-    size_t size;          /* size of this chunk */
+    size_t size;          /* size of the data in this chunk */
 
-    union {
-        char data[1];               /* if not free: start of the user data */
-        struct malloc_chunk *next;  /* if free: a doubly-linked list */
-    };
-    struct malloc_chunk *prev;
+    dlist_t d;            /* if free: a doubly-linked list */
+                          /* if not free: the user data starts here */
 
     /* The chunk has a total size of 'size'.  It is immediately followed
        in memory by another chunk.  This list ends with the last "chunk"
        being actually only one word long, 'size_t prev_size'.  Both this
        last chunk and the theoretical chunk before the first one are
        considered "not free". */
-} *mchunk_t;
+} mchunk_t;
 
 #define THIS_CHUNK_FREE      1
 #define BOTH_CHUNKS_USED     0
-#define CHUNK_HEADER_SIZE    offsetof(struct malloc_chunk, data)
-#define MINSIZE              sizeof(struct malloc_chunk)
+#define CHUNK_HEADER_SIZE    offsetof(struct malloc_chunk, d)
 
-#define chunk_at_offset(p, ofs)  ((mchunk_t *)(((char *)(p)) + ofs))
-#define next_chunk(p)            chunk_at_offset(p, (p)->size)
+#define chunk_at_offset(p, ofs)  ((mchunk_t *)(((char *)(p)) + (ofs)))
+#define data2chunk(p)            chunk_at_offset(p, -CHUNK_HEADER_SIZE)
+#define next_chunk(p)         chunk_at_offset(p, CHUNK_HEADER_SIZE + (p)->size)
 
 
 /* The free chunks are stored in "bins".  Each bin is a doubly-linked
@@ -53,55 +60,100 @@
    neighbors to ensure this.
 
    In each bin's doubly-linked list, chunks are sorted by their size in
-   decreasing order .  Among chunks of equal size, they are ordered with
-   the most recently freed first, and we take them from the back.  This
-   results in FIFO order, which is better to give each block a while to
-   rest in the list and be consolidated into potentially larger blocks.
+   decreasing order (if you start from 'd.next').
 */
 
-static struct { mchunk_t *head, mchunk_t *tail; } largebins[N_BINS];
+static struct { dlist_t d; dlist_t *unsorted; } largebins[N_BINS] = {
+
+#define INIT(num)   { { &largebins[num].d, &largebins[num].d }, NULL }
+    INIT(0),  INIT(1),  INIT(2),  INIT(3),  INIT(4),
+    INIT(5),  INIT(6),  INIT(7),  INIT(8),  INIT(9),
+    INIT(10), INIT(11), INIT(12), INIT(13), INIT(14),
+    INIT(15), INIT(16), INIT(17), INIT(18), INIT(19),
+    INIT(20), INIT(21), INIT(22), INIT(23), INIT(24),
+    INIT(25), INIT(26), INIT(27), INIT(28), INIT(29),
+    INIT(30), INIT(31), INIT(32), INIT(33), INIT(34),
+    INIT(35), INIT(36), INIT(37), INIT(38), INIT(39),
+    INIT(40), INIT(41), INIT(42), INIT(43), INIT(44),
+    INIT(45), INIT(46), INIT(47), INIT(48), INIT(49),
+    INIT(50), INIT(51), INIT(52), INIT(53), INIT(54),
+    INIT(55), INIT(56), INIT(57), INIT(58), INIT(59),
+    INIT(60), INIT(61), INIT(62), INIT(63), INIT(64),
+    INIT(65), INIT(66), INIT(67), INIT(68), INIT(69),
+    INIT(70), INIT(71), INIT(72), INIT(73), INIT(74),
+    INIT(75), INIT(76), INIT(77), INIT(78), INIT(79),
+    INIT(80), INIT(81), INIT(82), INIT(83) };
+#undef INIT
 
 
 static char *allocate_more(size_t request_size);
 
-static void insert_sort(mchunk_t *new)
+static void insert_unsorted(mchunk_t *new)
 {
     size_t index = largebin_index(new->size);
-    mchunk_t *head = largebins[index]->head;
+    new->d.next = largebins[index].unsorted;
+    largebins[index].unsorted = &new->d;
+}
 
-    if (head == NULL) {
-        assert(largebins[index]->tail == NULL);
-        new->prev = NULL;
-        new->next = NULL;
-        largebins[index]->tail = new;
-        largebins[index]->head = new;
-        return;
+static int compare_chunks(const void *vchunk1, const void *vchunk2)
+{
+    /* sort by decreasing size */
+    const mchunk_t *chunk1 = (const mchunk_t *)vchunk1;
+    const mchunk_t *chunk2 = (const mchunk_t *)vchunk2;
+    if (chunk1->size < chunk2->size)
+        return 1;
+    if (chunk1->size == chunk2->size)
+        return 0;
+    else
+        return -1;
+}
+
+static void really_sort_bin(size_t index)
+{
+    dlist_t *unsorted = largebins[index].unsorted;
+    largebins[index].unsorted = NULL;
+
+    dlist_t *scan = unsorted->next;
+    size_t count = 1;
+    while (scan != NULL) {
+        scan = scan->next;
+        ++count;
     }
-    assert(largebins[index]->tail != NULL);
 
-    size_t new_size = new->size;
-    if (new_size >= head->size) {
-        new->prev = NULL;
-        new->next = head;
-        assert(head->prev == NULL);
-        head->prev = new;
-        largebins[index]->head = new;
-        return;
+    mchunk_t *chunks[count];
+    size_t i;
+    for (i = 0; i < count; i++) {
+        chunks[i] = data2chunk(unsorted);
+        unsorted = unsorted->next;
     }
-    mchunk_t *search;
-    for (search = head; search != NULL; search = search->next) {
-        if (new_size >= search->size) {
-            new->prev = search->prev;
-            new->prev->next = new;
-            new->next = search;
-            search->prev = new;
-            return;
+    qsort(chunks, count, sizeof(mchunk_t *), compare_chunks);
+
+    dlist_t *head = largebins[index].d.next;
+    dlist_t *end = &largebins[index].d;
+    size_t search_size = chunks[0]->size;
+    i = 0;
+
+    while (1) {
+        if (head == end || search_size >= data2chunk(head)->size) {
+            /* insert 'chunks[i]' here, before the current head */
+            head->prev->next = &chunks[i]->d;
+            chunks[i]->d.prev = head->prev;
+            head->prev = &chunks[i]->d;
+            chunks[i]->d.next = head;
+            if (++i == count)
+                break;    /* all done */
+            search_size = chunks[i]->size;
+        }
+        else {
+            head = head->next;
         }
     }
-    new->prev = largebins[index]->tail;
-    new->prev->next = new;
-    new->next = NULL;
-    largebins[index]->tail = new;
+}
+
+static void sort_bin(size_t index)
+{
+    if (largebins[index].unsorted != NULL)
+        really_sort_bin(index);
 }
 
 char *stm_large_malloc(size_t request_size)
@@ -109,61 +161,59 @@
     /* 'request_size' should already be a multiple of the word size here */
     assert((request_size & (sizeof(char *)-1)) == 0);
 
-    size_t chunk_size = request_size + CHUNK_HEADER_SIZE;
-    if (chunk_size < request_size) {
-        /* 'request_size' is so large that the addition wrapped around */
-        fprintf(stderr, "allocation request too large\n");
-        abort();
-    }
-
-    size_t index = largebin_index(chunk_size);
+    size_t index = largebin_index(request_size);
+    sort_bin(index);
 
     /* scan through the chunks of current bin in reverse order
        to find the smallest that fits. */
-    mchunk_t *scan = largebins[index]->tail;
-    mchunk_t *head = largebins[index]->head;
+    dlist_t *scan = largebins[index].d.prev;
+    dlist_t *head = largebins[index].d.next;
+    mchunk_t *mscan;
     while (scan != head) {
-        assert(scan->prev_size == THIS_CHUNK_FREE);
-        assert(next_chunk(scan)->prev_size == scan->size);
+        mscan = data2chunk(scan);
+        assert(mscan->prev_size == THIS_CHUNK_FREE);
+        assert(next_chunk(mscan)->prev_size == mscan->size);
 
-        if (scan->size >= chunk_size) {
+        if (mscan->size >= request_size) {
             /* found! */
          found:
-            if (scan == largebins[index]->tail) {
-                largebins[index]->tail = scan->prev;
-            }
-            else {
-                scan->next->prev = scan->prev;
-            }
+            /* unlink mscan from the doubly-linked list */
+            mscan->d.next->prev = mscan->d.prev;
+            mscan->d.prev->next = mscan->d.next;
 
-            size_t remaining_size = scan->size - chunk_size;
-            if (remaining_size < MINSIZE) {
-                next_chunk(scan)->prev_size = BOTH_CHUNKS_USED;
+            size_t remaining_size = mscan->size - request_size;
+            if (remaining_size < sizeof(struct malloc_chunk)) {
+                next_chunk(mscan)->prev_size = BOTH_CHUNKS_USED;
             }
             else {
                 /* only part of the chunk is being used; reduce the size
-                   of 'scan' down to 'chunk_size', and create a new chunk
-                   of the 'remaining_size' afterwards */
-                mchunk_t *new = chunk_at_offset(scan, chunk_size);
+                   of 'mscan' down to 'request_size', and create a new
+                   chunk of the 'remaining_size' afterwards */
+                mchunk_t *new = chunk_at_offset(mscan, CHUNK_HEADER_SIZE +
+                                                       request_size);
                 new->prev_size = THIS_CHUNK_FREE;
-                new->size = remaining_size;
+                new->size = remaining_size - CHUNK_HEADER_SIZE;
                 next_chunk(new)->prev_size = remaining_size;
-                insert_sort(new);
-                scan->size = chunk_size;
+                insert_unsorted(new);
+                mscan->size = request_size;
             }
-            scan->prev_size = BOTH_CHUNKS_USED;
-            return scan->data;
+            mscan->prev_size = BOTH_CHUNKS_USED;
+            return (char *)&mscan->d;
         }
-        scan = scan->prev;
+        scan = mscan->d.prev;
     }
 
     /* search now through all higher bins.  We only need to take the
        smallest item of the first non-empty bin, as it will be large
        enough.  xxx use a bitmap to speed this up */
     while (++index < N_BINS) {
-        scan = largebins[index]->tail;
-        if (scan != NULL)
+        sort_bin(index);
+        scan = largebins[index].d.prev;
+        if (scan != &largebins[index].d) {
+            mscan = data2chunk(scan);
+            assert(mscan->size >= request_size);
             goto found;
+        }
     }
 
     /* not enough free memory.  We need to allocate more. */
@@ -182,9 +232,9 @@
     }
 
     big_chunk->prev_size = THIS_CHUNK_FREE;
-    big_chunk->size = big_size - sizeof(size_t);
+    big_chunk->size = big_size - CHUNK_HEADER_SIZE - sizeof(size_t);
     next_chunk(big_chunk)->prev_size = big_chunk->size;
-    insert_sort(big_chunk);
+    insert_unsorted(big_chunk);
 
     return stm_large_malloc(request_size);
 }


More information about the pypy-commit mailing list