On 05/13/2011 08:46 AM, Paolo Bonzini wrote:
On 05/12/2011 05:24 PM, Holger Hans Peter Freyther wrote:
How many read-only objects do we have in a bootstrapped image? In a
typical
iliad application? Does it make sense to optimize for it? or will RO
handling
just become more easy and this is a cleanup?
I think the point is to optimize _non_ read-only objects at the cost of
making tenuring read-only objects a bit more expensive (you will have to
unprotect the page, allocate, copy, protect the page). We also win
read-only access to fixed instance variables, which GNU Smalltalk
doesn't have (only indexed instance variables are protected).
BTW, for the same reason I think that moving objects to read-only space
directly when you make them read-only (like you did for fixedspace) is a
good idea. Anyway, read-only objects are mostly literals so they are
long-living. Gwen, can you reorganize the patch along these lines?
The patch making readonly space, well, readonly should be a separate
one. For unprotection/protection around allocation you can add hooks to
alloc.c heaps, like we have after_allocating: that would be
before_freeing, after_freeing, before_allocating.
Paolo
I've made the new patch, the ro_old space is like fixed. I've fixed
two bugs one in the symbol allocation and another when the ro_old heap
should grow.
Gwen
diff --git a/libgst/gstpriv.h b/libgst/gstpriv.h
index 68c34b9..2c6c49f 100644
--- a/libgst/gstpriv.h
+++ b/libgst/gstpriv.h
@@ -373,8 +373,7 @@ enum {
/* Set whether an object, OOP, is readonly or readwrite. */
#define MAKE_OOP_READONLY(oop, ro) \
- (((oop)->flags &= ~F_READONLY), \
- ((oop)->flags |= (ro) ? F_READONLY : 0))
+ _gst_make_oop_readonly (oop, ro)
#ifdef ENABLE_SECURITY
diff --git a/libgst/oop.c b/libgst/oop.c
index ad264d9..0dff6fa 100644
--- a/libgst/oop.c
+++ b/libgst/oop.c
@@ -145,9 +145,18 @@ static OOP *queue_put (surv_space *q, OOP *src, int n);
/* Move an object from survivor space to oldspace. */
static void tenure_one_object ();
+/* Initialize an allocation heap with a hooks set. */
+static heap_data *init_space (void *function, size_t size);
+
/* Initialize an allocation heap with the oldspace hooks set. */
static heap_data *init_old_space (size_t size);
+/* Initialize an allocation heap with the read-only oldspace hooks set. */
+static heap_data *init_ro_old_space (size_t size);
+
+/* Initialize an allocation heap with the fixedspace hooks set. */
+static heap_data *init_fixed_space (size_t size);
+
/* Initialize a surv_space structure. */
static void init_survivor_space (struct surv_space *space, size_t size);
@@ -208,6 +217,15 @@ static int oldspace_sigsegv_handler (void* fault_address, int serious);
#endif
/* Hook that triggers garbage collection. */
+static heap_data *grow_nomemory (heap_data *h, size_t sz);
+
+/* Hook that triggers garbage collection. */
+static heap_data *ro_oldspace_nomemory (heap_data *h, size_t sz);
+
+/* Hook that triggers garbage collection. */
+static heap_data *fixedspace_nomemory (heap_data *h, size_t sz);
+
+/* Hook that triggers garbage collection. */
static heap_data *oldspace_nomemory (heap_data *h, size_t sz);
/* Answer the number of fields to be scanned in the object starting
@@ -247,16 +265,34 @@ init_survivor_space (struct surv_space *space, size_t size)
}
heap_data *
-init_old_space (size_t size)
+init_space (void *function, size_t size)
{
heap_data *h = _gst_mem_new_heap (0, size);
h->after_prim_allocating = oldspace_after_allocating;
h->before_prim_freeing = oldspace_before_freeing;
- h->nomemory = oldspace_nomemory;
+ h->nomemory = function;
return h;
}
+heap_data *
+init_fixed_space (size_t size)
+{
+ return init_space (fixedspace_nomemory, size);
+}
+
+heap_data *
+init_ro_old_space (size_t size)
+{
+ return init_space (ro_oldspace_nomemory, size);
+}
+
+heap_data *
+init_old_space (size_t size)
+{
+ return init_space (oldspace_nomemory, size);
+}
+
void
_gst_init_mem_default ()
{
@@ -331,8 +367,9 @@ _gst_init_mem (size_t eden, size_t survivor, size_t old,
{
if (old)
{
+ _gst_mem.ro_old = init_ro_old_space (old);
_gst_mem.old = init_old_space (old);
- _gst_mem.fixed = init_old_space (old);
+ _gst_mem.fixed = init_fixed_space (old);
}
_gst_mem.active_half = &_gst_mem.surv[0];
@@ -695,6 +732,55 @@ _gst_swap_objects (OOP oop1,
_gst_make_oop_weak (oop1);
}
+mst_Boolean
+_gst_make_oop_readonly (OOP oop, mst_Boolean ro)
+{
+ gst_object newObj;
+ int size;
+
+ /* they are the same so I can return */
+ if (((oop->flags & F_READONLY) > 1) == ro)
+ return ro;
+
+ size = SIZE_TO_BYTES (TO_INT(oop->object->objSize));
+ if (ro)
+ {
+ /* become read only */
+ newObj = (gst_object) _gst_mem_alloc (_gst_mem.ro_old, size);
+ if UNCOMMON (!newObj)
+ {
+ _gst_global_gc (0);
+ newObj = (gst_object) _gst_mem_alloc (_gst_mem.ro_old, size);
+ if COMMON (!newObj)
+ abort ();
+ }
+
+ memcpy (newObj, oop->object, size);
+
+ if (oop->flags & F_FIXED)
+ _gst_mem_free (_gst_mem.fixed, oop->object);
+ else if (oop->flags & F_OLD)
+ _gst_mem_free (_gst_mem.old, oop->object);
+
+ oop->flags |= F_READONLY | F_FIXED;
+ }
+ else
+ {
+ /* become writeable */
+ newObj = (gst_object) _gst_mem_alloc (_gst_mem.old, size);
+ if (!newObj)
+ abort ();
+
+ memcpy (newObj, oop->object, size);
+ _gst_mem_free (_gst_mem.ro_old, oop->object);
+
+ oop->flags &= ~(F_READONLY | F_FIXED);
+ oop->flags |= F_OLD;
+ }
+
+ oop->object = newObj;
+ return ro;
+}
void
_gst_make_oop_fixed (OOP oop)
@@ -734,12 +820,14 @@ _gst_tenure_oop (OOP oop)
if (!(oop->flags & F_FIXED))
{
int size = SIZE_TO_BYTES (TO_INT(oop->object->objSize));
+
newObj = (gst_object) _gst_mem_alloc (_gst_mem.old, size);
+ _gst_mem.numOldOOPs++;
+
if (!newObj)
abort ();
memcpy (newObj, oop->object, size);
- _gst_mem.numOldOOPs++;
oop->object = newObj;
}
@@ -780,6 +868,39 @@ _gst_alloc_obj (size_t size,
}
gst_object
+_gst_alloc_ro_obj (size_t size,
+ OOP *p_oop)
+{
+ gst_object p_instance;
+
+ size = ROUNDED_BYTES (size);
+
+ /* If the object is big enough, we put it directly in oldspace. */
+ p_instance = (gst_object) _gst_mem_alloc (_gst_mem.ro_old, size);
+ if COMMON (p_instance)
+ goto ok;
+
+ _gst_global_gc (size);
+ p_instance = (gst_object) _gst_mem_alloc (_gst_mem.ro_old, size);
+ if COMMON (p_instance)
+ goto ok;
+
+ _gst_compact (0);
+ p_instance = (gst_object) _gst_mem_alloc (_gst_mem.ro_old, size);
+ if UNCOMMON (!p_instance)
+ {
+ /* !!! do something more reasonable in the future */
+ _gst_errorf ("Cannot recover, exiting...");
+ exit (1);
+ }
+
+ok:
+ *p_oop = alloc_oop (p_instance, F_OLD | F_FIXED | F_READONLY);
+ p_instance->objSize = FROM_INT (BYTES_TO_SIZE (size));
+ return p_instance;
+}
+
+gst_object
_gst_alloc_old_obj (size_t size,
OOP *p_oop)
{
@@ -889,25 +1010,43 @@ oldspace_before_freeing (heap_data *h, heap_block *blk, size_t sz)
}
heap_data *
-oldspace_nomemory (heap_data *h, size_t sz)
+grow_nomemory (heap_data *h, size_t sz)
{
if (!_gst_gc_running)
_gst_global_gc (sz);
else
{
/* Already garbage collecting, emergency growth just to satisfy
- tenuring necessities. */
- int grow_amount_to_satisfy_rate = _gst_mem.old->heap_limit
+ tenuring necessities. */
+ int grow_amount_to_satisfy_rate = h->heap_limit
* (100.0 + _gst_mem.space_grow_rate) / 100;
- int grow_amount_to_satisfy_threshold =
- (sz + _gst_mem.old->heap_total)
- * 100.0 /_gst_mem.grow_threshold_percent;
+ int grow_amount_to_satisfy_threshold =
+ (sz + h->heap_total)
+ * 100.0 /_gst_mem.grow_threshold_percent;
- _gst_mem.old->heap_limit = MAX (grow_amount_to_satisfy_rate,
- grow_amount_to_satisfy_threshold);
+ h->heap_limit = MAX (grow_amount_to_satisfy_rate,
+ grow_amount_to_satisfy_threshold);
}
- return _gst_mem.old;
+ return h;
+}
+
+heap_data *
+ro_oldspace_nomemory (heap_data *h, size_t sz)
+{
+ return grow_nomemory (_gst_mem.ro_old, sz);
+}
+
+heap_data *
+fixedspace_nomemory (heap_data *h, size_t sz)
+{
+ return grow_nomemory (_gst_mem.fixed, sz);
+}
+
+heap_data *
+oldspace_nomemory (heap_data *h, size_t sz)
+{
+ return grow_nomemory (_gst_mem.old, sz);
}
#ifndef NO_SIGSEGV_HANDLING
@@ -979,6 +1118,7 @@ void
grow_memory_no_compact (size_t new_heap_limit)
{
_gst_mem.old->heap_limit = new_heap_limit;
+ _gst_mem.ro_old->heap_limit = new_heap_limit;
_gst_mem.fixed->heap_limit = new_heap_limit;
_gst_mem.numGrowths++;
update_stats (&stats.timeOfLastGrowth,
@@ -996,6 +1136,7 @@ _gst_compact (size_t new_heap_limit)
if (new_heap_limit)
{
_gst_mem.fixed->heap_limit = new_heap_limit;
+ _gst_mem.ro_old->heap_limit = new_heap_limit;
_gst_mem.numGrowths++;
update_stats (&stats.timeOfLastGrowth,
&_gst_mem.timeBetweenGrowths, NULL);
@@ -1037,7 +1178,7 @@ _gst_compact (size_t new_heap_limit)
oop < &_gst_mem.ot[_gst_mem.ot_size]; oop++)
{
PREFETCH_LOOP (oop, PREF_READ | PREF_NTA);
- if ((oop->flags & (F_OLD | F_FIXED | F_LOADED)) == F_OLD)
+ if ((oop->flags & (F_OLD | F_FIXED | F_LOADED | F_READONLY)) == F_OLD)
{
gst_object new;
size_t size = SIZE_TO_BYTES (TO_INT (oop->object->objSize));
@@ -1049,6 +1190,7 @@ _gst_compact (size_t new_heap_limit)
}
xfree (_gst_mem.old);
+
_gst_mem.old = new_heap;
new_heap->nomemory = oldspace_nomemory;
@@ -1146,7 +1288,9 @@ _gst_global_gc (int next_allocation)
if UNCOMMON ((next_allocation + _gst_mem.old->heap_total)
* 100.0 / old_limit > _gst_mem.grow_threshold_percent
|| (next_allocation + _gst_mem.fixed->heap_total)
- * 100.0 / _gst_mem.fixed->heap_limit > _gst_mem.grow_threshold_percent)
+ * 100.0 / _gst_mem.fixed->heap_limit > _gst_mem.grow_threshold_percent
+ || (next_allocation + _gst_mem.ro_old->heap_total)
+ * 100.0 / _gst_mem.ro_old->heap_limit > _gst_mem.grow_threshold_percent)
{
int grow_amount_to_satisfy_rate = old_limit
* (100.0 + _gst_mem.space_grow_rate) / 100;
@@ -1190,7 +1334,9 @@ _gst_scavenge (void)
|| _gst_mem.old->heap_total * 100.0 / _gst_mem.old->heap_limit >
_gst_mem.grow_threshold_percent
|| _gst_mem.fixed->heap_total * 100.0 / _gst_mem.fixed->heap_limit >
- _gst_mem.grow_threshold_percent)
+ _gst_mem.grow_threshold_percent
+ || _gst_mem.ro_old->heap_total * 100.0 / _gst_mem.ro_old->heap_limit >
+ _gst_mem.grow_threshold_percent)
{
_gst_global_gc (0);
_gst_incremental_gc_step ();
@@ -1452,20 +1598,20 @@ _gst_sweep_oop (OOP oop)
_gst_make_oop_non_weak (oop);
/* Free unreachable oldspace objects. */
- if UNCOMMON (oop->flags & F_FIXED)
+ if (oop->flags & F_OLD)
{
_gst_mem.numOldOOPs--;
stats.reclaimedOldSpaceBytesSinceLastGlobalGC +=
- SIZE_TO_BYTES (TO_INT (OOP_TO_OBJ (oop)->objSize));
- if ((oop->flags & F_LOADED) == 0)
- _gst_mem_free (_gst_mem.fixed, oop->object);
+ SIZE_TO_BYTES (TO_INT (OOP_TO_OBJ (oop)->objSize));
}
- else if UNCOMMON (oop->flags & F_OLD)
+
+ if ((oop->flags & F_LOADED) == 0)
{
- _gst_mem.numOldOOPs--;
- stats.reclaimedOldSpaceBytesSinceLastGlobalGC +=
- SIZE_TO_BYTES (TO_INT (OOP_TO_OBJ (oop)->objSize));
- if ((oop->flags & F_LOADED) == 0)
+ if UNCOMMON (IS_OOP_READONLY (oop))
+ _gst_mem_free (_gst_mem.ro_old, oop->object);
+ else if UNCOMMON (oop->flags & F_FIXED)
+ _gst_mem_free (_gst_mem.fixed, oop->object);
+ else if UNCOMMON (oop->flags & F_OLD)
_gst_mem_free (_gst_mem.old, oop->object);
}
@@ -1617,7 +1763,9 @@ tenure_one_object ()
_gst_tenure_oop (oop);
queue_get (&_gst_mem.tenuring_queue, 1);
- queue_get (_gst_mem.active_half, TO_INT (oop->object->objSize));
+
+ if (!(oop->flags & F_FIXED))
+ queue_get (_gst_mem.active_half, TO_INT (oop->object->objSize));
}
void
@@ -2061,8 +2209,10 @@ _gst_copy_an_oop (OOP oop)
#endif
queue_put (&_gst_mem.tenuring_queue, &oop, 1);
- obj = oop->object = (gst_object)
- queue_put (_gst_mem.active_half, pData, TO_INT (obj->objSize));
+
+ if (!(oop->flags & F_FIXED))
+ obj = oop->object = (gst_object)
+ queue_put (_gst_mem.active_half, pData, TO_INT (obj->objSize));
oop->flags &= ~(F_SPACES | F_POOLED);
oop->flags |= _gst_mem.active_flag;
diff --git a/libgst/oop.h b/libgst/oop.h
index 6816b3f..fd9ea9f 100644
--- a/libgst/oop.h
+++ b/libgst/oop.h
@@ -189,7 +189,7 @@ typedef struct cheney_scan_state {
struct memory_space
{
- heap_data *old, *fixed;
+ heap_data *ro_old, *old, *fixed;
struct new_space eden;
struct surv_space surv[2], tenuring_queue;
@@ -414,6 +414,11 @@ extern gst_object _gst_alloc_old_obj (size_t size,
OOP *p_oop)
ATTRIBUTE_HIDDEN;
+/* The same, but for a read-only oldspace object */
+extern gst_object _gst_alloc_ro_obj (size_t size,
+ OOP *p_oop)
+ ATTRIBUTE_HIDDEN;
+
/* Allocate and return space for an object of SIZE words, without
creating an OOP. This is a special operation that is only needed
at bootstrap time, so it does not care about garbage collection. */
@@ -437,6 +442,10 @@ extern mst_Boolean _gst_realloc_oop_table (size_t newSize)
extern void _gst_tenure_oop (OOP oop)
ATTRIBUTE_HIDDEN;
+/* Set whether an object, OOP, is readonly or readwrite. */
+extern mst_Boolean _gst_make_oop_readonly (OOP oop, mst_Boolean ro)
+ ATTRIBUTE_HIDDEN;
+
/* Move OOP to fixedspace. */
extern void _gst_make_oop_fixed (OOP oop)
ATTRIBUTE_HIDDEN;
diff --git a/libgst/save.c b/libgst/save.c
index 77cc8b9..70f41ed 100644
--- a/libgst/save.c
+++ b/libgst/save.c
@@ -646,7 +646,9 @@ load_normal_oops (int imageFd)
else
{
- if (flags & F_FIXED)
+ if (flags & F_READONLY)
+ object = (gst_object) _gst_mem_alloc (_gst_mem.ro_old, size);
+ else if (flags & F_FIXED)
{
_gst_mem.numFixedOOPs++;
object = (gst_object) _gst_mem_alloc (_gst_mem.fixed, size);
diff --git a/libgst/sym.c b/libgst/sym.c
index a3cfd65..e0d7c8a 100644
--- a/libgst/sym.c
+++ b/libgst/sym.c
@@ -1460,11 +1460,10 @@ alloc_symbol_oop (const char *str, int len)
numBytes = sizeof(gst_object_header) + len;
alignedBytes = ROUNDED_BYTES (numBytes);
- symbol = (gst_symbol) _gst_alloc_obj (alignedBytes, &symbolOOP);
+ symbol = (gst_symbol) _gst_alloc_ro_obj (alignedBytes, &symbolOOP);
INIT_UNALIGNED_OBJECT (symbolOOP, alignedBytes - numBytes);
memcpy (symbol->symString, str, len);
- symbolOOP->flags |= F_READONLY;
return symbolOOP;
}
diff --git a/snprintfv/snprintfv/filament.h b/snprintfv/snprintfv/filament.h
index 4a91eb6..8a7ce6c 100644
--- a/snprintfv/snprintfv/filament.h
+++ b/snprintfv/snprintfv/filament.h
@@ -1,4 +1,4 @@
-#line 1 "../../../snprintfv/snprintfv/filament.in"
+#line 1 "./filament.in"
/* -*- Mode: C -*- */
/* filament.h --- a bit like a string but different =)O|
@@ -118,7 +118,7 @@ extern char * fildelete (Filament *fil);
extern void _fil_extend (Filament *fil, size_t len, boolean copy);
-#line 61 "../../../snprintfv/snprintfv/filament.in"
+#line 61 "./filament.in"
/* Save the overhead of a function call in the great majority of cases. */
#define fil_maybe_extend(fil, len, copy) \
diff --git a/snprintfv/snprintfv/printf.h b/snprintfv/snprintfv/printf.h
index 49a2e9f..1437dd5 100644
--- a/snprintfv/snprintfv/printf.h
+++ b/snprintfv/snprintfv/printf.h
@@ -1,4 +1,4 @@
-#line 1 "../../../snprintfv/snprintfv/printf.in"
+#line 1 "./printf.in"
/* -*- Mode: C -*- */
/* printf.in --- printf clone for argv arrays
@@ -266,7 +266,7 @@ enum
} \
} SNV_STMT_END
-#line 269 "../../../snprintfv/snprintfv/printf.in"
+#line 269 "./printf.in"
/**
* printf_generic_info:
* @pinfo: the current state information for the format
@@ -302,7 +302,7 @@ extern int printf_generic_info (struct printf_info *const pinfo, size_t n, int *
extern int printf_generic (STREAM *stream, struct printf_info *const pinfo, union printf_arg const *args);
-#line 270 "../../../snprintfv/snprintfv/printf.in"
+#line 270 "./printf.in"
/**
* register_printf_function:
* @spec: the character which will trigger @func, cast to an unsigned int.
@@ -789,7 +789,7 @@ extern int snv_vasprintf (char **result, const char *format, va_list ap);
extern int snv_asprintfv (char **result, const char *format, snv_constpointer const args[]);
-#line 271 "../../../snprintfv/snprintfv/printf.in"
+#line 271 "./printf.in"
/* If you don't want to use snprintfv functions for *all* of your string
formatting API, then define COMPILING_SNPRINTFV_C and use the snv_
diff --git a/snprintfv/snprintfv/stream.h b/snprintfv/snprintfv/stream.h
index 496bd33..0bebce1 100644
--- a/snprintfv/snprintfv/stream.h
+++ b/snprintfv/snprintfv/stream.h
@@ -1,4 +1,4 @@
-#line 1 "../../../snprintfv/snprintfv/stream.in"
+#line 1 "./stream.in"
/* -*- Mode: C -*- */
/* stream.h --- customizable stream routines
@@ -180,7 +180,7 @@ extern int stream_puts (char *s, STREAM *stream);
extern int stream_get (STREAM *stream);
-#line 88 "../../../snprintfv/snprintfv/stream.in"
+#line 88 "./stream.in"
#ifdef __cplusplus
#if 0
/* This brace is so that emacs can still indent properly: */
_______________________________________________
help-smalltalk mailing list
[email protected]
https://lists.gnu.org/mailman/listinfo/help-smalltalk