[m-rev.] For review: Improvments to Stack Segments code.
Zoltan Somogyi
zs at csse.unimelb.edu.au
Wed Feb 23 07:58:27 AEDT 2011
On 22-Feb-2011, Paul Bone <pbone at csse.unimelb.edu.au> wrote:
> Non-segmented stack (32MB)
> asm_fast.gc average of 5 with ignore=1 18.16 (1.00)
>
> With 512KB (normal) segments:
> asm_fast.gc.stseg and NO caching average of 5 with ignore=1 19.20 (1.06)
> asm_fast.gc.stseg WITH caching average of 5 with ignore=1 19.16 (1.06)
>
> With 4KB segments:
> asm_fast.gc.stseg and NO caching average of 5 with ignore=1 20.66 (1.14)
> asm_fast.gc.stseg WITH caching average of 5 with ignore=1 19.66 (1.08)
> Not all zones are added to the used list for the benefit of the memory
> error handlers.
I don't understand what this means.
> Updates to used_memory_zones now use a pthread mutex so that only one
> thread may be updating the list at once. This lock is shared with the
> free_memory_zones structure.
>
> Updates to used_memory_zones now use memory barriers to guarantee that
> concurrent reads always read a consistent, but possibly incomplete,
> data-structure. This is necessary because it is read from a signal handler
> which cannot call pthread_mutex().
Do you mean that READS from used_memory_zones use barriers?
> runtime/mercury_stacks.c:
> Use MR_GC_NEW rather than MR_GC_malloc_uncollectable for the list of
> previous stack segments associated with a context. The context is heap
> allocated so the list need not be uncollectable.
But if you know that it is never garbage until explicitly released, then
you can spare unnecessary scanning.
> runtime/mercury_wrapper.h:
> runtime/mercury_wrapper.c:
> Disable redzones when using stack segments. The settings here apply to
> the first segment on every stack.
What settings do you mean?
> runtime/mercury_context.h:
> runtime/mercury_context.c:
> Small contexts are only used in stack segment grades.
But what is the change?
> runtime/mercury_memory.c:
> Adjust the definition of MR_unit. It is now guaranteed to be a multiple of
> the page size which is required by it's use in mercury_memory_zones.c
>
> Conform to changes in mercury_wrapper.h.
s/it's/its/
> doc/user_guide.texi:
> Corrected the default detstack size.
You should add comments linking from the definition to the documentation
and vice versa.
> +** This list contains used zones that need a signal handler, for example those
> +** that have redzones. Other used zones may exist that are not on this list
> +** because:
> +**
> +** 1) They don't have a redzone.
> +**
> +** 2) Putting them on this list in a threadsafe grade requires extra
> +** synchronisation.
> +*/
Document here where those zones go when they are no longer needed.
> - /*
> - ** XXX MR_construct_zone() does not yet reuse previously allocated memory
> - ** zones properly and simply leaks memory when it tries to do so. As a
> - ** workaround, we never put memory zones on the free list and deallocate
> - ** them immediately here.
> - */
We should redo the paper's benchmarks once this is committed and installed.
> + zone = MR_get_free_zone(size + redsize);
> + if (zone) {
This should be
if (zone != NULL)
On a few systems, NULL is not 0.
> + if (redsize || (handler != MR_null_handler)) {
redsize > 0
s/redsize/redzone_size/g
> + ** Any zone with a redzone or a non-default handler must be
> + ** added the the used zones list.
added TO the used zones list.
> + ** The redzone must be page aligned and it's size must be a multiple of
s/it's/its/
> - zone->MR_zone_name = name;
> - zone->MR_zone_id = id;
> - zone->MR_zone_desired_size = size;
> - zone->MR_zone_redzone_size = redsize;
> + zone->MR_zone_name = NULL;
Why ignore the name?
> #ifdef MR_CHECK_OVERFLOW_VIA_MPROTECT
> - zone->MR_zone_handler = handler;
> + zone->MR_zone_handler = NULL;
> #endif /* MR_CHECK_OVERFLOW_VIA_MPROTECT */
Why this change?
> + /*
> + ** XXX: Why use this value for new_total_size?
> + */
> #ifdef MR_PROTECTPAGE
> new_total_size = new_size + 2 * MR_unit;
> #else
If you think it does not make sense, change it.
> +/****************************************************************************
> +**
> +** Cacheing of memory zones.
> +*/
> +#ifdef MR_DO_NOT_CACHE_FREE_MEMORY_ZONES
Mention that this is only for benchmarking.
> +** MR_should_gc_memory_zones() - True if either number and number of pages are
> +** above the high water mark.
I don't understand what this is trying to say. What numbers?
> +** MR_should_stop_gc_memory_zones() - True if both the number nad number of
> +** pages are below the low water mark.
Or this.
> +static void MR_gc_zones(void);
> +static MR_bool MR_should_gc_memory_zones(void);
> +static MR_bool MR_should_stop_gc_memory_zones(void);
> +
> +/*
> +** Currently we use high and low water marks, collection begins if either the
> +** number of zones or total number of pages is above their respective high
> +** water marks and stops when both are below their low water marks.
> +**
> +** TODO: Test for optimal values of these settings, however there's probably
> +** not much to be gained here that can't be more easily gained somewhere else
> +** first.
> +*/
Now I understand. Put the two comments together.
> +/* 16 zones per thread */
> +#define MR_FREE_MEMORY_ZONES_NUM_HIGH (16*MR_num_threads)
> +/* 4 zones per thread */
> +#define MR_FREE_MEMORY_ZONES_NUM_LOW (4*MR_num_threads)
> +/* 16MB per thread */
> +#define MR_FREE_MEMORY_ZONES_PAGES_HIGH (((16*1024*1024)/MR_page_size)*MR_num_threads)
> +/* 4MB per thread */
> +#define MR_FREE_MEMORY_ZONES_PAGES_LOW (((4*1024*1024)/MR_page_size)*MR_num_threads)
You may want to control these via MERCURY_OPTIONS, and doing that would be
simpler if the multiplication by MR_num_threads was done by the code using
these constants, not parts of the constants themselves.
> +static MR_MemoryZonesFree * MR_THREADSAFE_VOLATILE
> +free_memory_zones = NULL;
This is one definition, not two, so indent the second line.
> +/*
> +** A pointer into the list of free zones that always points to sub-list (when
> +** valid) with the least-recently-used zones.
I don't understand what this is trying to say.
Certain actions can invalidate
> +** this pointer, if this happens it is set to NULL. Therefore this can be
> +** thought of as 'cached information'.
Cached from what?
> +/*
> +** The next token to be given to a memory zone as it's added to the free list.
> +** The tokens are used to detect the least recently used zones when freeing
> +** them.
That tells me what they are used for, but not what they MEAN. Also,
s/it's/it is/
> +static MR_THREADSAFE_VOLATILE MR_Unsigned lru_memory_zone_token = 0;
> +
> +static MR_MemoryZone *
> +MR_get_free_zone(size_t size)
> +{
> + MR_MemoryZone *zone;
> + MR_MemoryZonesFree *zones_list;
> + MR_MemoryZonesFree *zones_list_prev;
> +
> + /*
> + ** Unlink the first zone on the free-list, link it onto the used-list
> + ** and return it.
> + */
> + MR_LOCK(&memory_zones_lock, "MR_get_free_zone");
> +
> + zones_list = free_memory_zones;
> + zones_list_prev = NULL;
> + while (zones_list) {
> + if (zones_list->MR_zonesfree_size <= size) {
> + /*
> + * A zone on this list will fit our needs.
> + */
Comment style.
> + if (zones_list) {
!= NULL
> + zone = zones_list->MR_zonesfree_minor_head;
> + if (zone->MR_zone_next) {
!= NULL
and more places below.
> + ** This zone list had the least recently used zone on it,
> + ** invalidate the lru_free_memory_zones pointer.
Why is that OK to do? Why can't you set it to point to the next least recently
used zone?
> + if (cur_list == NULL) {
> + MR_MemoryZonesFree *new_list, *prev_list;
Separate declarations are less error-prone.
> + /*
> + ** Because no lists currently exist, this new list must be the list
> + ** with the lru zone on it.
According to a comment a couple of dozen or so lines ago, the assumption
here seems to be incorrect; some lists MAY currently exist.
> +
> + zone->MR_zone_next = cur_list->MR_zonesfree_minor_head;
> + cur_list->MR_zonesfree_minor_head = zone;
> + if (!cur_list->MR_zonesfree_minor_tail) {
> + /*
> + ** This is the first zone on this list, so we set the tail to it to
> + ** track it as the least-recently-used zone on the list
> + */
> + cur_list->MR_zonesfree_minor_tail = zone;
Don't you need the tail pointer set up correctly anyway? And end the sentence
with a period.
> +static MR_bool
> +MR_should_stop_gc_memory_zones(void)
> +{
> + return (free_memory_zones_num < MR_FREE_MEMORY_ZONES_NUM_LOW) ||
> + (free_memory_zones_pages < MR_FREE_MEMORY_ZONES_PAGES_LOW);
> +}
Didn't your comment above say this || should be &&?
> +static void
> +MR_gc_zones(void)
> +{
> + do {
> + MR_LOCK(&memory_zones_lock, "MR_gc_zones");
> + MR_MemoryZonesFree *cur_list;
> + MR_Unsigned oldest_lru_token, cur_lru_token;
> +
> + if (!lru_free_memory_zones) {
lru_free_memory_zones == NULL, here and below.
> + while(cur_list)
> + {
Brace style.
> + ** There's no memory to collect, perhaps there was a race before we
> + ** locked mercury_zones_lock
End with period.
> +** lru_token This field is filled with a token each time a zone is freed,
> +** it's used to track the least-recently-used zones in the free
> +** list so that they can be handed back to the garbage
> +** collector/OS.
You need to say what the token MEANS.
> +/*
> +** Free memory zones are arranged in a list of lists, The outer list (below)
> +** associates a size of the zones in each inner list. It is to be kept in
> +** sorted order from smallest to largest. So that a traversal of this list
> +** returns the zones that are the 'best fit' as the 'first fit'. The inner
> +** lists (using the MR_zone_next field of the zones contain zones of all the
> +** same size.
> +*/
Add closing ).
> +struct MR_MemoryZonesFree_Struct {
> + size_t MR_zonesfree_size;
> + MR_MemoryZonesFree *MR_zonesfree_major_next;
> + MR_MemoryZonesFree *MR_zonesfree_major_prev;
> + MR_MemoryZone *MR_zonesfree_minor_head;
> + MR_MemoryZone *MR_zonesfree_minor_tail;
Align the fields' indentation.
> + MR_release_zone(MR_CONTEXT(MR_ctxt_detstack_zone));
>
> list = MR_CONTEXT(MR_ctxt_prev_detstack_zones);
> MR_CONTEXT(MR_ctxt_detstack_zone) = list->MR_zones_head;
> MR_CONTEXT(MR_ctxt_prev_detstack_zones) = list->MR_zones_tail;
> MR_CONTEXT(MR_ctxt_sp) = orig_sp;
> + /* XXX: GC will get this */
> MR_GC_free(list);
Yeah, so? Why the XXX?
Otherwise, the diff seems fine.
Zoltan.
--------------------------------------------------------------------------
mercury-reviews mailing list
Post messages to: mercury-reviews at csse.unimelb.edu.au
Administrative Queries: owner-mercury-reviews at csse.unimelb.edu.au
Subscriptions: mercury-reviews-request at csse.unimelb.edu.au
--------------------------------------------------------------------------
More information about the reviews
mailing list