mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-13 04:44:39 +00:00
Estimated hours taken: 7
Branches: main
A common way to use parallel conjunction can cause a lot of Mercury contexts
to be allocated, e.g.
map([], []).
map([H0|T0], [H|T]) :-
( p(H0, H) % contains no parallel conjunctions
& map(T0, T)
).
When the left parallel conjunct completes, the engine that was executing it
must suspend the context in which it was run, waiting for the right conjunct
to finish. The engine is then idle and will attempt to find further work to
execute in a _new_ context. To avoid excessive memory consumption due to
contexts we currently limit the number of contexts we allocate. However,
that severely limits the parallelism we can exploit in this example (and
similar patterns of work distribution). There are a lot of contexts
allocated but most of them are simply suspended.
Assuming that most parallel conjuncts contain small sub-computations, we can
allow many contexts to be allocated without excessive memory consumption by
just giving them smaller stacks. This patch creates a simple variant of a
MR_Context structure which has smaller stacks than the initial MR_Context
structure and executes parallel conjuncts in the smaller contexts if
larger contexts are unavailable.
runtime/mercury_memory.c:
runtime/mercury_wrapper.c:
runtime/mercury_wrapper.h:
doc/user_guide.texi:
Add globals to hold the desired sizes of small det and nondet stacks.
Add `--small-detstack-size' and `--small-nondetstack-size'
options for the MERCURY_OPTIONS environment variable to set the
desired sizes.
runtime/mercury_context.h:
Add a MR_ctxt_size field to MR_Context to indicate whether it has
regular or small sized stacks.
runtime/mercury_context.c:
Add an argument to MR_create_context() specifying whether we want a
regular or small context.
Ask for small stacks when creating new contexts to begin execution
from a spark (i.e. parallel conjuncts).
Create a new free-list to hold unused small contexts.
extras/concurrency/spawn.m:
runtime/mercury_mm_own_stacks.c:
runtime/mercury_thread.c:
Match the interface change to MR_create_context(). We give the
initial context and contexts created due for explicit Mercury threads
regular-sized stacks.
380 lines
9.1 KiB
C
380 lines
9.1 KiB
C
/*
|
|
** Copyright (C) 1994-2000,2002-2004, 2006 The University of Melbourne.
|
|
** This file may only be copied under the terms of the GNU Library General
|
|
** Public License - see the file COPYING.LIB in the Mercury distribution.
|
|
*/
|
|
|
|
/*
|
|
** This module defines the register array and data regions of the
|
|
** execution algorithm.
|
|
** They are defined together here to allow us to control how they map
|
|
** onto direct mapped caches.
|
|
** We allocate a large arena, preferably aligned on a boundary that
|
|
** is a multiple of both the page size and the primary cache size.
|
|
**
|
|
** We then allocate the heap and the stacks in such a way that
|
|
**
|
|
** the register array
|
|
** the bottom of the heap
|
|
** the bottom of the detstack
|
|
** the bottom of the nondstack
|
|
**
|
|
** all start at different offsets from multiples of the primary cache size.
|
|
** This should reduce cache conflicts (especially for small programs).
|
|
**
|
|
** If the operating system of the machine supports the mprotect syscall,
|
|
** we also protect a chunk at the end of each area against access,
|
|
** thus detecting area overflow.
|
|
**
|
|
** The code for handling the allocation and management of different
|
|
** memory zones is in mercury_memory_zones.{c,h}.
|
|
** The code for handling overflows and memory access errors in general
|
|
** is in mercury_memory_handlers.{c,h}.
|
|
*/
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
#include "mercury_imp.h"
|
|
|
|
/*
|
|
** This include must come before anything else that might include <signal.h>.
|
|
** See the commments in mercury_signal.h.
|
|
*/
|
|
#include "mercury_signal.h"
|
|
|
|
#ifdef MR_HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#ifdef MR_HAVE_SYS_SIGINFO_H
|
|
#include <sys/siginfo.h>
|
|
#endif
|
|
|
|
#ifdef MR_HAVE_MPROTECT
|
|
#include <sys/mman.h>
|
|
#endif
|
|
|
|
#ifdef MR_HAVE_UCONTEXT_H
|
|
#include <ucontext.h>
|
|
#endif
|
|
|
|
#ifdef MR_HAVE_SYS_UCONTEXT_H
|
|
#include <sys/ucontext.h>
|
|
#endif
|
|
|
|
#include "mercury_imp.h"
|
|
#include "mercury_trace_base.h"
|
|
#include "mercury_memory_handlers.h"
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
#if defined(MR_HAVE_SYSCONF) && defined(_SC_PAGESIZE)
|
|
#define getpagesize() sysconf(_SC_PAGESIZE)
|
|
#elif !defined(MR_HAVE_GETPAGESIZE)
|
|
#if defined(MR_WIN32_GETSYSTEMINFO)
|
|
#include <windows.h>
|
|
|
|
static size_t
|
|
getpagesize(void)
|
|
{
|
|
SYSTEM_INFO SysInfo;
|
|
GetSystemInfo(&SysInfo);
|
|
return (size_t) SysInfo.dwPageSize;
|
|
}
|
|
#else
|
|
#define getpagesize() 8192
|
|
#endif
|
|
#endif
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
#ifdef MR_HAVE_SIGINFO
|
|
static MR_bool try_munprotect(void *address, void *context);
|
|
static char *explain_context(void *context);
|
|
#endif /* MR_HAVE_SIGINFO */
|
|
|
|
/*
|
|
** Define the memory zones used by the Mercury runtime.
|
|
** (The trail zone is declared in mercury_trail.c.)
|
|
** XXX All the zones should be in mercury_engine.h
|
|
*/
|
|
|
|
#ifdef MR_USE_MINIMAL_MODEL_STACK_COPY
|
|
MR_MemoryZone *MR_genstack_zone;
|
|
MR_MemoryZone *MR_cutstack_zone;
|
|
MR_MemoryZone *MR_pnegstack_zone;
|
|
#endif
|
|
|
|
size_t MR_unit;
|
|
size_t MR_page_size;
|
|
|
|
void
|
|
MR_init_memory(void)
|
|
{
|
|
static MR_bool already_initialized = MR_FALSE;
|
|
|
|
if (already_initialized != MR_FALSE)
|
|
return;
|
|
|
|
already_initialized = MR_TRUE;
|
|
|
|
/*
|
|
** Convert all the sizes are from kilobytes to bytes and
|
|
** make sure they are multiples of the page and cache sizes.
|
|
*/
|
|
|
|
MR_page_size = getpagesize();
|
|
MR_unit = MR_max(MR_page_size, MR_pcache_size);
|
|
|
|
#ifdef MR_CONSERVATIVE_GC
|
|
MR_heap_size = 0;
|
|
MR_heap_zone_size = 0;
|
|
MR_solutions_heap_size = 0;
|
|
MR_solutions_heap_zone_size = 0;
|
|
MR_global_heap_size = 0;
|
|
MR_global_heap_zone_size = 0;
|
|
MR_debug_heap_size = 0;
|
|
MR_debug_heap_zone_size = 0;
|
|
MR_heap_margin_size = 0;
|
|
#else
|
|
MR_heap_size = MR_round_up(MR_heap_size * 1024,
|
|
MR_unit);
|
|
MR_heap_zone_size = MR_round_up(MR_heap_zone_size * 1024,
|
|
MR_unit);
|
|
MR_solutions_heap_size = MR_round_up(MR_solutions_heap_size * 1024,
|
|
MR_unit);
|
|
MR_solutions_heap_zone_size = MR_round_up(
|
|
MR_solutions_heap_zone_size * 1024,
|
|
MR_unit);
|
|
MR_global_heap_size = MR_round_up(MR_global_heap_size * 1024,
|
|
MR_unit);
|
|
MR_global_heap_zone_size = MR_round_up(MR_global_heap_zone_size * 1024,
|
|
MR_unit);
|
|
MR_debug_heap_size = MR_round_up(MR_debug_heap_size * 1024,
|
|
MR_unit);
|
|
MR_debug_heap_zone_size = MR_round_up(MR_debug_heap_zone_size * 1024,
|
|
MR_unit);
|
|
/* Note that there's no need for the heap margin to be rounded up */
|
|
MR_heap_margin_size = MR_heap_margin_size * 1024;
|
|
#endif
|
|
MR_detstack_size = MR_round_up(MR_detstack_size * 1024,
|
|
MR_unit);
|
|
MR_small_detstack_size = MR_round_up(MR_small_detstack_size * 1024,
|
|
MR_unit);
|
|
MR_detstack_zone_size = MR_round_up(MR_detstack_zone_size * 1024,
|
|
MR_unit);
|
|
MR_nondstack_size = MR_round_up(MR_nondstack_size * 1024,
|
|
MR_unit);
|
|
MR_small_nondstack_size = MR_round_up(MR_small_nondstack_size * 1024,
|
|
MR_unit);
|
|
MR_nondstack_zone_size = MR_round_up(MR_nondstack_zone_size * 1024,
|
|
MR_unit);
|
|
#ifdef MR_USE_MINIMAL_MODEL_STACK_COPY
|
|
MR_genstack_size = MR_round_up(MR_genstack_size * 1024,
|
|
MR_unit);
|
|
MR_genstack_zone_size = MR_round_up(
|
|
MR_genstack_zone_size * 1024,
|
|
MR_unit);
|
|
MR_cutstack_size = MR_round_up(MR_cutstack_size * 1024,
|
|
MR_unit);
|
|
MR_cutstack_zone_size = MR_round_up(MR_cutstack_zone_size * 1024,
|
|
MR_unit);
|
|
MR_pnegstack_size = MR_round_up(MR_pnegstack_size * 1024,
|
|
MR_unit);
|
|
MR_pnegstack_zone_size = MR_round_up(MR_pnegstack_zone_size * 1024,
|
|
MR_unit);
|
|
#else
|
|
MR_genstack_size = 0;
|
|
MR_genstack_zone_size = 0;
|
|
MR_cutstack_size = 0;
|
|
MR_cutstack_zone_size = 0;
|
|
MR_pnegstack_size = 0;
|
|
MR_pnegstack_zone_size = 0;
|
|
#endif
|
|
|
|
#ifdef MR_USE_MINIMAL_MODEL_OWN_STACKS
|
|
MR_gen_detstack_size = MR_round_up(MR_gen_detstack_size * 1024,
|
|
MR_unit);
|
|
MR_gen_nonstack_size = MR_round_up(MR_gen_nonstack_size * 1024,
|
|
MR_unit);
|
|
MR_gen_detstack_zone_size = MR_round_up(
|
|
MR_gen_detstack_zone_size * 1024,
|
|
MR_unit);
|
|
MR_gen_nonstack_zone_size = MR_round_up(
|
|
MR_gen_nonstack_zone_size * 1024,
|
|
MR_unit);
|
|
#else
|
|
MR_gen_detstack_size = 0;
|
|
MR_gen_nonstack_size = 0;
|
|
MR_gen_detstack_zone_size = 0;
|
|
MR_gen_nonstack_zone_size = 0;
|
|
#endif
|
|
|
|
#ifdef MR_USE_TRAIL
|
|
MR_trail_size = MR_round_up(MR_trail_size * 1024,
|
|
MR_unit);
|
|
MR_trail_zone_size = MR_round_up(MR_trail_zone_size * 1024,
|
|
MR_unit);
|
|
#else
|
|
MR_trail_size = 0;
|
|
MR_trail_zone_size = 0;
|
|
#endif
|
|
|
|
/*
|
|
** If the zone sizes were set to something too big, then
|
|
** set them to a single unit.
|
|
*/
|
|
|
|
#ifndef MR_CONSERVATIVE_GC
|
|
if (MR_heap_zone_size >= MR_heap_size) {
|
|
MR_heap_zone_size = MR_unit;
|
|
}
|
|
if (MR_solutions_heap_zone_size >= MR_solutions_heap_size) {
|
|
MR_solutions_heap_zone_size = MR_unit;
|
|
}
|
|
if (MR_global_heap_zone_size >= MR_global_heap_size) {
|
|
MR_global_heap_zone_size = MR_unit;
|
|
}
|
|
if (MR_heap_margin_size >= MR_heap_size) {
|
|
MR_heap_margin_size = MR_unit;
|
|
}
|
|
#endif
|
|
|
|
if (MR_detstack_zone_size >= MR_detstack_size) {
|
|
MR_detstack_zone_size = MR_unit;
|
|
}
|
|
|
|
if (MR_nondstack_zone_size >= MR_nondstack_size) {
|
|
MR_nondstack_zone_size = MR_unit;
|
|
}
|
|
|
|
#ifdef MR_USE_TRAIL
|
|
if (MR_trail_zone_size >= MR_trail_size) {
|
|
MR_trail_zone_size = MR_unit;
|
|
}
|
|
#endif
|
|
|
|
MR_init_zones();
|
|
|
|
} /* end MR_init_memory() */
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/*
|
|
** These routines allocate memory that will NOT be scanned by the
|
|
** conservative garbage collector. You MUST NOT uses these to
|
|
** store pointers into GC'ed memory.
|
|
**
|
|
*/
|
|
|
|
void *
|
|
MR_malloc(size_t n)
|
|
{
|
|
void *ptr;
|
|
|
|
ptr = malloc(n);
|
|
if (ptr == NULL && n != 0) {
|
|
MR_fatal_error("ran out of memory");
|
|
}
|
|
|
|
return ptr;
|
|
}
|
|
|
|
void *
|
|
MR_realloc(void *old_ptr, size_t num_bytes)
|
|
{
|
|
void *ptr;
|
|
|
|
ptr = realloc(old_ptr, num_bytes);
|
|
if (ptr == NULL && num_bytes != 0) {
|
|
MR_fatal_error("ran out of memory");
|
|
}
|
|
|
|
return ptr;
|
|
}
|
|
|
|
char *
|
|
MR_copy_string(const char *s)
|
|
{
|
|
int len;
|
|
char *copy;
|
|
|
|
if (s == NULL) {
|
|
return NULL;
|
|
} else {
|
|
len = strlen(s);
|
|
copy = MR_malloc(len + 1);
|
|
strcpy(copy, s);
|
|
return copy;
|
|
}
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/*
|
|
** These routines allocate memory that will be scanned by the
|
|
** conservative garbage collector.
|
|
**
|
|
** XXX This is inefficient. If MR_BOEHM_GC is enabled,
|
|
** we should set `GC_oom_fn' (see boehm_gc/gc.h) rather than
|
|
** testing the return value from GC_MALLOC() or GC_MALLOC_UNCOLLECTABLE().
|
|
*/
|
|
|
|
void *
|
|
MR_GC_malloc(size_t num_bytes)
|
|
{
|
|
void *ptr;
|
|
|
|
#ifdef MR_CONSERVATIVE_GC
|
|
ptr = GC_MALLOC(num_bytes);
|
|
#else
|
|
ptr = malloc(num_bytes);
|
|
#endif
|
|
|
|
if (ptr == NULL && num_bytes != 0) {
|
|
MR_fatal_error("could not allocate memory");
|
|
}
|
|
|
|
return ptr;
|
|
}
|
|
|
|
void *
|
|
MR_GC_malloc_uncollectable(size_t num_bytes)
|
|
{
|
|
void *ptr;
|
|
|
|
#ifdef MR_CONSERVATIVE_GC
|
|
ptr = GC_MALLOC_UNCOLLECTABLE(num_bytes);
|
|
#else
|
|
ptr = malloc(num_bytes);
|
|
#endif
|
|
|
|
if (ptr == NULL && num_bytes != 0) {
|
|
MR_fatal_error("could not allocate memory");
|
|
}
|
|
|
|
return ptr;
|
|
}
|
|
|
|
void *
|
|
MR_GC_realloc(void *old_ptr, size_t num_bytes)
|
|
{
|
|
void *ptr;
|
|
|
|
#ifdef MR_CONSERVATIVE_GC
|
|
ptr = GC_REALLOC(old_ptr, num_bytes);
|
|
#else
|
|
ptr = realloc(old_ptr, num_bytes);
|
|
#endif
|
|
if (ptr == NULL && num_bytes != 0) {
|
|
MR_fatal_error("ran out of memory");
|
|
}
|
|
|
|
return ptr;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|