Freeing nb_setval

I was curious. If I make a variable with nb_setval(x,2) can I free it later? When does it get gc?

If free is like free as not bound (ref) and if var/1 is true when free and nonvar/1 is true when bound then,

?- nb_setval(x,2).
true.

?- nb_current(x,Value),nonvar(Value).
Value = 2.

?- nb_current(x,Value),var(Value).
false.

?- nb_setval(x,_).
true.

?- nb_current(x,Value),nonvar(Value).
false.

?- nb_current(x,Value),var(Value).
true.

Note: nonvar/1 and ground/1 are the same, E.g.

?- nb_setval(x,2).
true.

?- nb_current(x,Value),ground(Value).
Value = 2.

?- nb_current(x,Value),nonvar(Value).
Value = 2.

?- nb_setval(x,_).
true.

?- nb_current(x,Value),ground(Value).
false.

?- nb_current(x,Value),nonvar(Value).
false.

When does it get gc?

I can’t answer that but if you are wondering how to remove it then use nb_delete/1, E.g.

?- nb_setval(x,2).
true.

?- nb_current(x,Value).
Value = 2.

?- nb_getval(x,Value).
Value = 2.

?- nb_delete(x).
true.

?- nb_current(x,Value).
false.

?- nb_getval(x,Value).
ERROR: variable `x' does not exist
ERROR: In:
ERROR:   [10] nb_getval(x,_10578)
ERROR:    [9] toplevel_call('<garbage_collected>') at c:/program files/swipl/boot/toplevel.pl:1117

HTH


Side note.

I have to give @Boris some credit for this answer because the other day he rightly questioned my stating that Prolog does not have types and noted Verify Type of a Term. One of the reasons I never liked saying Prolog has types is because I don’t consider a variable a type, I.e. why is var/1 on that page. The more I tried to respond the more it became apparent that a variable might be a type, still haven’t decided if a variable is a type (think about type system and/or duck typing) but it does help in understanding problems like the one presented in this question. :slightly_smiling_face:

2 Likes

You more or less did. The variable itself exists as long as you do not use nb_delete/1. The current value can of course not be garbage collected. Old values can. The global variable structure itself is a (per thread) hash table mapping variable names to Prolog terms. Primitive values (atoms, small integers) are stored directly in the table. Otherwise the hash entry points at a Prolog term on the (global) stack. Prolog (stack) GC is based on the idea of a root pointer set that is followed to find all accessible data. The rest is garbage. This means it can collect with structures of any topology, including cycles as they become unreachable.

Note that global variables that have their value on the stacks do not scale very well for GC. For some detailed internal reason GC has to create a copy of the mapping on the stack, run GC and restore the table entries. So, do not make too many of them …

1 Like

Instead of global variables i pass along a global structure – its now a term - functor with arguments – which is essentially an array – where each argument holds a global value or (assoc) structure.

I have get and set predicates for the structure, so that its relational.

     global_struct(	global(Arg1, Arg2, Arg3), 
					   	Arg1, Arg2, Arg3).

	global_get_arg_1(GD, Arg) :- arg(1, GD, Arg).
	global_set_arg_1(	global(_Arg1, Arg2, Arg3), New_Arg, 
							global(New_Arg, Arg2, Arg3)).

init(G) :-
   global_struct(G, 1, two, "three").

change_arg1(Arg, G0, G) :-
   global_set_arg_1(G0, Arg, G).

get_arg1(G, Arg) :-
 global_get_arg1(G, Arg).

Like this its available across threads – and now i hear that its probably also more efficient when it comes to garbage collection

Dan

Oh no. :dizzy_face:

I have been eating these like candy for a current project because operators AFAIK can not pass state along so have to hold the state elsewhere and Global variables was what I knew best for this. Since the template for the code was based on something that used the operators I just kept using the operators. The state is used to enable/disable features like

  • displaying the intermediate representations of the AST during processing
  • a trace but at a higher level

However the template code is very old, somewhere around SWI-Prolog 3.x and used The recorded database.

Is there another data structure I should use to hold the state that is independent of the operators and accessible globally (not using threads now, so if it is local to a thread at present would be fine)?

I have little clue what “independent of the operators” means. You can pack many name/value pairs in an assoc, rbtree or dict and make that globally accessible. For an array you use a compound term. This all works pretty nice if you can us b_setval/2 (the backtrackable one). Note that backtrackable global variables have the same representation and thus are subject to the same GC issue. Setting them is a lot cheaper though because nb_setval/2 first needs to do duplicate_term/2.

That said, in practice it may all be quite ok. Use statistics/2 or the profiler to find out how much time is spent in GC. It that isn’t too much there is apparently no problem. If it is a lot it is not easy to know whether this is due to the global var issue or not :frowning: The only way out is to avoid them and see the impact (or not) or use C level profiling tools and figure out how the GC time is split over the various steps GC needs to take.

2 Likes

After reading your comment, I simplified codes for closing zdd control term. The longer previous codes clears all buckets vector elements expecting more chance for GC, but as far as my experiment I got to doubt effect of the extra codes. Now I think the hash table must be inaccessible because of loss of the root pointer. In fact, quick test for the simplified codes for closing seems to work good. Thanks.

Simplified codes.

close_state(S):- clear_state(S).
%
clear_state(S):-
	nb_setarg(1, S, []),
	nb_setarg(2, S, []),
	nb_setarg(3, S, []),
	nb_setarg(4, S, []).

The previous codes:

close_state(S):-
	arg(1, S, Core),
	nb_setarg(1, S, []),
	nb_setarg(2, S, []),
	nb_setarg(3, S, []),
	arg(4, S, S0),
	nb_setarg(4, S, []),
	(	S0 == [] -> true
	;	close_state(S0)
	),
	arg(1, Core, N),
	arg(2, Core, H0),
	arg(2, H0, HVec),
	arg(3, Core, Vec),
	clear_vector(HVec),
	clear_vector(N, Vec).

clear_vector(V):- functor(V, _, N),	clear_vector(N, V).
%
clear_vector(0, _):-!.
clear_vector(I, V):- nb_setarg(I, V, 0), !,
	J is I - 1,
	clear_vector(J, V).