nb_linkvar and nb_set_shared_var

git-svn-id: https://yap.svn.sf.net/svnroot/yap/trunk@1932 b08c6af1-5177-4d33-ba66-4b1c6b8b522a
This commit is contained in:
vsc 2007-09-21 14:18:12 +00:00
parent 7f366435f4
commit 0860b141de
3 changed files with 280 additions and 79 deletions

View File

@ -346,11 +346,11 @@ clean_dirty_tr(tr_fr_ptr TR0) {
#if COROUTINING
static int
CopyAttVar(CELL *orig, CELL ***to_visit_ptr, CELL *res, Term *att_arenap)
CopyAttVar(CELL *orig, struct cp_frame **to_visit_ptr, CELL *res, Term *att_arenap)
{
register attvar_record *attv = (attvar_record *)orig;
register attvar_record *newv;
CELL **to_visit = *to_visit_ptr;
struct cp_frame *to_visit = *to_visit_ptr;
CELL *vt;
/* add a new attributed variable */
@ -361,17 +361,19 @@ CopyAttVar(CELL *orig, CELL ***to_visit_ptr, CELL *res, Term *att_arenap)
RESET_VARIABLE(&(newv->Value));
RESET_VARIABLE(&(newv->Done));
vt = &(attv->Atts);
to_visit[0] = vt-1;
to_visit[1] = vt;
to_visit->start_cp = vt-1;
to_visit->end_cp = vt;
if (IsVarTerm(attv->Atts)) {
newv->Atts = (CELL)H;
to_visit[2] = H;
to_visit->to = H;
H++;
} else {
to_visit[2] = &(newv->Atts);
to_visit->to = &(newv->Atts);
}
to_visit[3] = (CELL *)vt[-1];
*to_visit_ptr = to_visit+4;
to_visit->oldv = vt[-1];
/* you're coming from a variable */
to_visit->ground = FALSE;
*to_visit_ptr = to_visit+1;
*res = (CELL)&(newv->Done);
*att_arenap = (CELL)(newv);
return TRUE;
@ -379,15 +381,17 @@ CopyAttVar(CELL *orig, CELL ***to_visit_ptr, CELL *res, Term *att_arenap)
#endif
static int
copy_complex_term(register CELL *pt0, register CELL *pt0_end, Term arena, CELL *ptf, CELL *HLow, Term *att_arenap)
copy_complex_term(register CELL *pt0, register CELL *pt0_end, int share, CELL *ptf, CELL *HLow, Term *att_arenap)
{
CELL **to_visit0, **to_visit = (CELL **)Yap_PreAllocCodeSpace();
struct cp_frame *to_visit0, *to_visit = (struct cp_frame *)Yap_PreAllocCodeSpace();
CELL *HB0 = HB;
tr_fr_ptr TR0 = TR;
#ifdef COROUTINING
CELL *dvars = NULL;
#endif
int ground = TRUE;
HB = HLow;
to_visit0 = to_visit;
loop:
@ -402,7 +406,7 @@ copy_complex_term(register CELL *pt0, register CELL *pt0_end, Term arena, CELL *
{
if (IsPairTerm(d0)) {
CELL *ap2 = RepPair(d0);
if ((arena == GlobalArena && ap2 < H) ||
if ((share && ap2 < HB) ||
(ap2 >= HB && ap2 < H)) {
/* If this is newer than the current term, just reuse */
*ptf++ = d0;
@ -411,27 +415,30 @@ copy_complex_term(register CELL *pt0, register CELL *pt0_end, Term arena, CELL *
*ptf = AbsPair(H);
ptf++;
#ifdef RATIONAL_TREES
if (to_visit + 4 >= (CELL **)AuxSp) {
if (to_visit+1 >= (struct cp_frame *)AuxSp) {
goto heap_overflow;
}
to_visit[0] = pt0;
to_visit[1] = pt0_end;
to_visit[2] = ptf;
to_visit[3] = (CELL *)*pt0;
to_visit->start_cp = pt0;
to_visit->end_cp = pt0_end;
to_visit->to = ptf;
to_visit->oldv = *pt0;
to_visit->ground = ground;
/* fool the system into thinking we had a variable there */
*pt0 = AbsPair(H);
to_visit += 4;
to_visit ++;
#else
if (pt0 < pt0_end) {
if (to_visit + 3 >= (CELL **)AuxSp) {
if (to_visit + 1 >= (CELL **)AuxSp) {
goto heap_overflow;
}
to_visit[0] = pt0;
to_visit[1] = pt0_end;
to_visit[2] = ptf;
to_visit += 3;
to_visit->start_cp = pt0;
to_visit->end_cp = pt0_end;
to_visit->to = ptf;
to_visit->ground = ground;
to_visit ++;
}
#endif
ground = TRUE;
pt0 = ap2 - 1;
pt0_end = ap2 + 1;
ptf = H;
@ -444,7 +451,7 @@ copy_complex_term(register CELL *pt0, register CELL *pt0_end, Term arena, CELL *
register CELL *ap2;
/* store the terms to visit */
ap2 = RepAppl(d0);
if ((arena == GlobalArena && ap2 < H) ||
if ((share && ap2 < HB) ||
(ap2 >= HB && ap2 < H)) {
/* If this is newer than the current term, just reuse */
*ptf++ = d0;
@ -505,27 +512,30 @@ copy_complex_term(register CELL *pt0, register CELL *pt0_end, Term arena, CELL *
ptf++;
/* store the terms to visit */
#ifdef RATIONAL_TREES
if (to_visit + 4 >= (CELL **)AuxSp) {
if (to_visit+1 >= (struct cp_frame *)AuxSp) {
goto heap_overflow;
}
to_visit[0] = pt0;
to_visit[1] = pt0_end;
to_visit[2] = ptf;
to_visit[3] = (CELL *)*pt0;
to_visit->start_cp = pt0;
to_visit->end_cp = pt0_end;
to_visit->to = ptf;
to_visit->oldv = *pt0;
to_visit->ground = ground;
/* fool the system into thinking we had a variable there */
*pt0 = AbsAppl(H);
to_visit += 4;
to_visit ++;
#else
if (pt0 < pt0_end) {
if (to_visit + 3 >= (CELL **)AuxSp) {
if (to_visit ++ >= (CELL **)AuxSp) {
goto heap_overflow;
}
to_visit[0] = pt0;
to_visit[1] = pt0_end;
to_visit[2] = ptf;
to_visit += 3;
to_visit->start_cp = pt0;
to_visit->end_cp = pt0_end;
to_visit->to = ptf;
to_visit->ground = ground;
to_visit ++;
}
#endif
ground = (f != FunctorMutable);
d0 = ArityOfFunctor(f);
pt0 = ap2;
pt0_end = ap2 + d0;
@ -544,14 +554,17 @@ copy_complex_term(register CELL *pt0, register CELL *pt0_end, Term arena, CELL *
}
derefa_body(d0, ptd0, copy_term_unk, copy_term_nvar);
if (ptd0 >= HLow && ptd0 < H) {
ground = FALSE;
/* don't need to copy variables if we want to share the global term */
if ((share && ptd0 < HB && ptd0 > H0) ||
(ptd0 >= HLow && ptd0 < H)) {
/* we have already found this cell */
*ptf++ = (CELL) ptd0;
} else {
#if COROUTINING
if (IsAttachedTerm((CELL)ptd0)) {
/* if unbound, call the standard copy term routine */
CELL **bp[1];
struct cp_frame *bp[1];
if (dvars == NULL) {
dvars = (CELL *)DelayArenaPt(*att_arenap);
@ -593,18 +606,14 @@ copy_complex_term(register CELL *pt0, register CELL *pt0_end, Term arena, CELL *
}
/* Do we still have compound terms to visit */
if (to_visit > to_visit0) {
to_visit --;
pt0 = to_visit->start_cp;
pt0_end = to_visit->end_cp;
ptf = to_visit->to;
#ifdef RATIONAL_TREES
to_visit -= 4;
pt0 = to_visit[0];
pt0_end = to_visit[1];
ptf = to_visit[2];
*pt0 = (CELL)to_visit[3];
#else
to_visit -= 3;
pt0 = to_visit[0];
pt0_end = to_visit[1];
ptf = to_visit[2];
*pt0 = to_visit->oldv;
#endif
ground = (ground && to_visit->ground);
goto loop;
}
@ -621,11 +630,11 @@ copy_complex_term(register CELL *pt0, register CELL *pt0_end, Term arena, CELL *
HB = HB0;
#ifdef RATIONAL_TREES
while (to_visit > to_visit0) {
to_visit -= 4;
pt0 = to_visit[0];
pt0_end = to_visit[1];
ptf = to_visit[2];
*pt0 = (CELL)to_visit[3];
to_visit --;
pt0 = to_visit->start_cp;
pt0_end = to_visit->end_cp;
ptf = to_visit->to;
*pt0 = to_visit->oldv;
}
#endif
reset_trail(TR0);
@ -639,11 +648,11 @@ copy_complex_term(register CELL *pt0, register CELL *pt0_end, Term arena, CELL *
HB = HB0;
#ifdef RATIONAL_TREES
while (to_visit > to_visit0) {
to_visit -= 4;
pt0 = to_visit[0];
pt0_end = to_visit[1];
ptf = to_visit[2];
*pt0 = (CELL)to_visit[3];
to_visit--;
pt0 = to_visit->start_cp;
pt0_end = to_visit->end_cp;
ptf = to_visit->to;
*pt0 = to_visit->oldv;
}
#endif
reset_trail(TR0);
@ -659,11 +668,11 @@ copy_complex_term(register CELL *pt0, register CELL *pt0_end, Term arena, CELL *
HB = HB0;
#ifdef RATIONAL_TREES
while (to_visit > to_visit0) {
to_visit -= 4;
pt0 = to_visit[0];
pt0_end = to_visit[1];
ptf = to_visit[2];
*pt0 = (CELL)to_visit[3];
to_visit--;
pt0 = to_visit->start_cp;
pt0_end = to_visit->end_cp;
ptf = to_visit->to;
*pt0 = to_visit->oldv;
}
#endif
reset_trail(TR0);
@ -678,11 +687,11 @@ copy_complex_term(register CELL *pt0, register CELL *pt0_end, Term arena, CELL *
HB = HB0;
#ifdef RATIONAL_TREES
while (to_visit > to_visit0) {
to_visit -= 4;
pt0 = to_visit[0];
pt0_end = to_visit[1];
ptf = to_visit[2];
*pt0 = (CELL)to_visit[3];
to_visit--;
pt0 = to_visit->start_cp;
pt0_end = to_visit->end_cp;
ptf = to_visit->to;
*pt0 = to_visit->oldv;
}
#endif
reset_trail(TR0);
@ -690,7 +699,7 @@ copy_complex_term(register CELL *pt0, register CELL *pt0_end, Term arena, CELL *
}
static Term
CopyTermToArena(Term t, Term arena, UInt arity, Term *newarena, Term *att_arenap, UInt min_grow)
CopyTermToArena(Term t, Term arena, int share, UInt arity, Term *newarena, Term *att_arenap, UInt min_grow)
{
UInt old_size = ArenaSz(arena);
CELL *oldH = H;
@ -700,6 +709,7 @@ CopyTermToArena(Term t, Term arena, UInt arity, Term *newarena, Term *att_arenap
#if COROUTINING
Term old_delay_arena;
#endif
Term tn;
restart:
#if COROUTINING
@ -716,13 +726,17 @@ CopyTermToArena(Term t, Term arena, UInt arity, Term *newarena, Term *att_arenap
*H = t;
Hi = H+1;
H += 2;
if ((res = copy_complex_term(Hi-2, Hi-1, arena, Hi, Hi, att_arenap)) < 0)
if ((res = copy_complex_term(Hi-2, Hi-1, share, Hi, Hi, att_arenap)) < 0)
goto error_handler;
CloseArena(oldH, oldHB, oldASP, newarena, old_size);
return Hi[0];
}
#endif
Term tn = MkVarTerm();
if (share && VarOfTerm(t) > ArenaPt(arena)) {
CloseArena(oldH, oldHB, oldASP, newarena, old_size);
return t;
}
tn = MkVarTerm();
if (H > ASP - 128) {
res = -1;
goto error_handler;
@ -736,13 +750,16 @@ CopyTermToArena(Term t, Term arena, UInt arity, Term *newarena, Term *att_arenap
CELL *ap;
CELL *Hi;
if (share && ArenaPt(arena) > RepPair(t)) {
return t;
}
H = HB = ArenaPt(arena);
ASP = ArenaLimit(arena);
ap = RepPair(t);
Hi = H;
tf = AbsPair(H);
H += 2;
if ((res = copy_complex_term(ap-1, ap+1, arena, Hi, Hi, att_arenap)) < 0) {
if ((res = copy_complex_term(ap-1, ap+1, share, Hi, Hi, att_arenap)) < 0) {
goto error_handler;
}
CloseArena(oldH, oldHB, oldASP, newarena, old_size);
@ -753,6 +770,9 @@ CopyTermToArena(Term t, Term arena, UInt arity, Term *newarena, Term *att_arenap
CELL *HB0;
CELL *ap;
if (share && ArenaPt(arena) > RepAppl(t)) {
return t;
}
H = HB = ArenaPt(arena);
ASP = ArenaLimit(arena);
f = FunctorOfTerm(t);
@ -809,7 +829,7 @@ CopyTermToArena(Term t, Term arena, UInt arity, Term *newarena, Term *att_arenap
res = -1;
goto error_handler;
}
if ((res = copy_complex_term(ap, ap+ArityOfFunctor(f), arena, HB0+1, HB0, att_arenap)) < 0) {
if ((res = copy_complex_term(ap, ap+ArityOfFunctor(f), share, HB0+1, HB0, att_arenap)) < 0) {
goto error_handler;
}
}
@ -1156,7 +1176,7 @@ garena_overflow_size(CELL *arena)
static Int
p_nb_copyterm(void)
{
Term to = CopyTermToArena(ARG1, GlobalArena, 2, &GlobalArena, &GlobalDelayArena, garena_overflow_size(ArenaPt(GlobalArena)));
Term to = CopyTermToArena(ARG1, GlobalArena, TRUE, 2, &GlobalArena, &GlobalDelayArena, garena_overflow_size(ArenaPt(GlobalArena)));
if (to == 0L)
return FALSE;
return Yap_unify(ARG2,to);
@ -1205,7 +1225,29 @@ p_nb_setval(void)
return (FALSE);
}
ge = GetGlobalEntry(AtomOfTerm(t));
to = CopyTermToArena(ARG2, GlobalArena, 2, &GlobalArena, &GlobalDelayArena, garena_overflow_size(ArenaPt(GlobalArena)));
to = CopyTermToArena(ARG2, GlobalArena, FALSE, 2, &GlobalArena, &GlobalDelayArena, garena_overflow_size(ArenaPt(GlobalArena)));
if (to == 0L)
return FALSE;
WRITE_LOCK(ge->GRWLock);
ge->global=to;
WRITE_UNLOCK(ge->GRWLock);
return TRUE;
}
static Int
p_nb_set_shared_val(void)
{
Term t = Deref(ARG1), to;
GlobalEntry *ge;
if (IsVarTerm(t)) {
Yap_Error(INSTANTIATION_ERROR,t,"nb_setval");
return (TermNil);
} else if (!IsAtomTerm(t)) {
Yap_Error(TYPE_ERROR_ATOM,t,"nb_setval");
return (FALSE);
}
ge = GetGlobalEntry(AtomOfTerm(t));
to = CopyTermToArena(ARG2, GlobalArena, TRUE, 2, &GlobalArena, &GlobalDelayArena, garena_overflow_size(ArenaPt(GlobalArena)));
if (to == 0L)
return FALSE;
WRITE_LOCK(ge->GRWLock);
@ -1473,7 +1515,7 @@ p_nb_queue_enqueue(void)
} else {
min_size = 0L;
}
to = CopyTermToArena(ARG2, arena, 2, qd+QUEUE_ARENA, qd+QUEUE_DELAY_ARENA, min_size);
to = CopyTermToArena(ARG2, arena, FALSE, 2, qd+QUEUE_ARENA, qd+QUEUE_DELAY_ARENA, min_size);
if (to == 0L)
return FALSE;
qd = GetQueue(ARG1,"enqueue");
@ -1803,9 +1845,9 @@ p_nb_heap_add_to_heap(void)
arena = qd[HEAP_ARENA];
if (arena == 0L)
return FALSE;
key = CopyTermToArena(ARG2, arena, 3, qd+HEAP_ARENA, qd+HEAP_DELAY_ARENA, 0);
key = CopyTermToArena(ARG2, arena, FALSE, 3, qd+HEAP_ARENA, qd+HEAP_DELAY_ARENA, 0);
arena = qd[HEAP_ARENA];
to = CopyTermToArena(ARG3, arena, 3, qd+HEAP_ARENA, qd+HEAP_DELAY_ARENA, 0);
to = CopyTermToArena(ARG3, arena, FALSE, 3, qd+HEAP_ARENA, qd+HEAP_DELAY_ARENA, 0);
if (key == 0 || to == 0L)
return FALSE;
qd = GetHeap(ARG1,"add_to_heap");
@ -2204,9 +2246,9 @@ p_nb_beam_add_to_beam(void)
arena = qd[HEAP_ARENA];
if (arena == 0L)
return FALSE;
key = CopyTermToArena(ARG2, qd[HEAP_ARENA], 3, qd+HEAP_ARENA, qd+HEAP_DELAY_ARENA, 0);
key = CopyTermToArena(ARG2, qd[HEAP_ARENA], FALSE, 3, qd+HEAP_ARENA, qd+HEAP_DELAY_ARENA, 0);
arena = qd[HEAP_ARENA];
to = CopyTermToArena(ARG3, arena, 3, qd+HEAP_ARENA, qd+HEAP_DELAY_ARENA, 0);
to = CopyTermToArena(ARG3, arena, FALSE, 3, qd+HEAP_ARENA, qd+HEAP_DELAY_ARENA, 0);
if (key == 0 || to == 0L)
return FALSE;
qd = GetHeap(ARG1,"add_to_beam");
@ -2441,6 +2483,7 @@ void Yap_InitGlobals(void)
Yap_InitCPred("nb_copy_term", 2, p_nb_copyterm, 0L);
Yap_InitCPred("nb_make_term", 2, p_nb_maketerm, 0L);
Yap_InitCPred("nb_setval", 2, p_nb_setval, 0L);
Yap_InitCPred("nb_set_shared_val", 2, p_nb_set_shared_val, 0L);
Yap_InitCPred("nb_linkval", 2, p_nb_linkval, 0L);
Yap_InitCPred("nb_getval", 2, p_nb_getval, SafePredFlag);
Yap_InitCPred("nb_delete", 1, p_nb_delete, 0L);

View File

@ -16,6 +16,7 @@
<h2>Yap-5.1.3:</h2>
<ul>
<li> NEW: improve nb_ routines with linkvar and set_shared_var.</li>
<li> NEW: make copy_term share ground-terms and add non-sharing
version, duplicate_term/2.</li>
<li> FIXED: improve efficiency of global variables.</li>

View File

@ -124,6 +124,7 @@ Built In Predicates
* Preds:: Predicate Information
* OS:: Access to Operating System Functionality
* Term Modification:: Updating Prolog Terms
* Global Variables:: Manipulating Global Variables
* Profiling:: Profiling Prolog Execution
* Call Counting:: Limiting the Maximum Number of Reductions
* Arrays:: Supporting Global and Local Arrays
@ -2179,6 +2180,7 @@ Built-ins, Debugging, Syntax, Top
* Preds:: Predicate Information
* OS:: Access to Operating System Functionality
* Term Modification:: Updating Prolog Terms
* Global Variables:: Manipulating Global Variables
* Profiling:: Profiling Prolog Execution
* Call Counting:: Limiting the Maximum Number of Reductions
* Arrays:: Supporting Global and Local Arrays
@ -5921,7 +5923,162 @@ Unify the current value of mutable term @var{M} with term @var{D}.
Set the current value of mutable term @var{M} to term @var{D}.
@end table
@node Profiling, Call Counting, Term Modification, Top
@node Global Variables, Profiling, Profiling, Term Modification, Top
@section Global Variables
@cindex global variables
Global variables are associations between names (atoms) and
terms. They differ in various ways from storing information using
@node{assert/1} or @node{recorda/3}.
@itemize @bullet
@item The value lives on the Prolog (global) stack. This implies that
lookup time is independent from the size of the term. This is
particularly interesting for large data structures such as parsed XML
documents or the CHR global constraint store.
@item They support both global assignment using @code{nb_setval/2} and
backtrackable assignment using @code{b_setval/2}.
@item Only one value (which can be an arbitrary complex Prolog term)
can be associated to a variable at a time.
@item Their value cannot be shared among threads. Each thread has its own
namespace and values for global variables.
@end itemize
Currently global variables are scoped globally. We may consider module
scoping in future versions. Both @code{b_setval/2} and
@code{nb_setval/2} implicitly create a variable if the referenced name
does not already refer to a variable.
Global variables may be initialised from directives to make them
available during the program lifetime, but some considerations are
necessary for saved-states and threads. Saved-states to not store
global variables, which implies they have to be declared with
@code{initialization/1} to recreate them after loading the saved
state. Each thread has its own set of global variables, starting with
an empty set. Using @code{thread_initialization/1} to define a global
variable it will be defined, restored after reloading a saved state
and created in all threads that are created after the
registration. Finally, global variables can be initialised using the
exception hook called @code{exception/3}. The latter technique is used
by CHR.
@table @code
@item b_setval(+@var{Name}, +@var{Value})
@findex b_setval/2
@snindex b_setval/2
@cnindex b_setval/2
Associate the term @var{Value} with the atom @var{Name} or replaces
the currently associated value with @var{Value}. If @var{Name} does
not refer to an existing global variable a variable with initial value
[] is created (the empty list). On backtracking the assignment is
reversed.
@item b_getval(+@var{Name}, -@var{Value})
@findex b_getval/2
@snindex b_getval/2
@cnindex b_getval/2
Get the value associated with the global variable @var{Name} and unify
it with @var{Value}. Note that this unification may further
instantiate the value of the global variable. If this is undesirable
the normal precautions (double negation or @var{copy_term/2}) must be
taken. The @var{b_getval/2} predicate generates errors if @var{Name} is not
an atom or the requested variable does not exist.
@item nb_setval(+@var{Name}, +@var{Value})
@findex nb_setval/2
@snindex nb_setval/2
@cnindex nb_setval/2
Associates a copy of @var{Value} created with @var{duplicate_term/2} with
the atom @var{Name}. Note that this can be used to set an initial
value other than @code{[]} prior to backtrackable assignment.
@item nb_getval(+@var{Name}, -@var{Value})
@findex nb_getval/2
@snindex nb_getval/2
@cnindex nb_getval/2
The @code{nb_getval/2} predicate is a synonym for @code{b_getval/2},
introduced for compatibility and symmetry. As most scenarios will use
a particular global variable either using non-backtracable or
backtrackable assignment, using @code{nb_getval/2} can be used to
document that the variable is used non-backtracable.
@item nb_linkval(+@var{Name}, +@var{Value})
@findex nb_linkval/2
@snindex nb_linkval/2
@cnindex nb_linkval/2
Associates the term @var{Value} with the atom @var{Name} without
copying it. This is a fast special-purpose variation of @code{nb_setval/2}
intended for expert users only because the semantics on backtracking
to a point before creating the link are poorly defined for compound
terms. The principal term is always left untouched, but backtracking
behaviour on arguments is undone if the original assignment was
trailed and left alone otherwise, which implies that the history that
created the term affects the behaviour on backtracking. Please
consider the following example:
@example
demo_nb_linkval :-
T = nice(N),
( N = world,
nb_linkval(myvar, T),
fail
; nb_getval(myvar, V),
writeln(V)
).
@end example
@item nb_set_shared_val(+@var{Name}, +@var{Value})
@findex nb_set_shared_val/2
@snindex nb_set_shared_val/2
@cnindex nb_set_shared_val/2
Associates the term @var{Value} with the atom @var{Name}, but sharing
non-backtrackable terms. This may be useful if you want to rewrite a
global variable so that the new copy will survive backtracking, but
you want to share structure with the previous term.
The next example shows the differences between the three built-ins:
@example
?- nb_setval(a,a(_)),nb_getval(a,A),nb_setval(b,t(C,A)),nb_getval(b,B).
A = a(_A),
B = t(_B,a(_C)) ?
?- nb_setval(a,a(_)),nb_getval(a,A),nb_set_shared_val(b,t(C,A)),nb_getval(b,B).
?- nb_setval(a,a(_)),nb_getval(a,A),nb_linkval(b,t(C,A)),nb_getval(b,B).
A = a(_A),
B = t(C,a(_A)) ?
@end example
@item nb_current(?@var{Name}, ?@var{Value})
@findex nb_current/2
@snindex nb_current/2
@cnindex nb_current/2
Enumerate all defined variables with their value. The order of
enumeration is undefined.
@item nb_delete(+@var{Name})
@findex nb_delete/2
@snindex nb_delete/2
@cnindex nb_delete/2
Delete the named global variable.
@end table
Global variables have been introduced by various Prolog
implementations recently. We follow the implementation of them in
SWI-Prolog, itself based on hProlog by Bart Demoen.
GNU-Prolog provides a rich set of global variables, including
arrays. Arrays can be implemented easily in YAP and SWI-Prolog using
@code{functor/3} and @code{setarg/3} due to the unrestricted arity of
compound terms.
@node Profiling, Call Counting, Global Variables, Top
@section Profiling Prolog Programs
@cindex profiling