Commit 1c5e802e authored by Radford Neal's avatar Radford Neal

new helpers module, using is_in_use and is_being_computed

parent 9b8d131b
......@@ -40,4 +40,4 @@ This software is available at github.com/radfordneal/helpers.
Radford Neal may be contacted at radfordneal@gmail.com.
This is the version of 2013-09-10.
This is the version of 2013-09-12.
......@@ -314,6 +314,9 @@ all scheduled tasks have completed. Calling this procedure may be
advisable before terminating the program, if some pending tasks may
have desired side effects (such as writing data to files).
FAST CHECKS FOR VARIABLES BEING IN USE OR BEING COMPUTED
Programs may sometimes wish to quickly check (in the master thread)
whether a variable is currently in use or is being computed, without
the overhead of a procedure call. This can be arranged by providing
......@@ -339,15 +342,33 @@ and "not in use".
When one of these macros is not defined in helpers-app.h, the
corresponding marker will not be set or cleared, which is sensible
only if this marker is not used.
only if this marker is not used by the application.
These macros will be invoked only in the master thread, when a task is
scheduled by helpers_do_task, or when helpers_do_task or one of the
other procedures notices that a task has completed. Consequently, a
variable may remain marked as in-use or being-computed when it no
longer is, if the master thread has not yet noticed the change. These
markers should be examined only from the master thread, so that no
synchronization will be required when accessing them.
longer is, if the master thread has not yet noticed the change.
These markers may be accessed by the application in whatever way is
convenient, but they should only be accessed from the master thread,
so that no synchronization will be required when accessing them. An
application may choose to make access to these markers available to
the helpers implementation, by defining one or both of the following
macros:
helpers_is_in_use
helpers_is_being_computed
These take one argument of type helpers_var_ptr. The helpers
implementation will use these macros, if they are defined, to avoid
some computations. For correct operation, these macros must return a
non-zero value if the variable given is in use or is being computed.
They may return a non-zero value even if the variable is not in use or
is not being computed, though this will slow down some operations.
All the macros described in this section will never be invoked by
the helpers implementation with a null pointer argument.
SETTING UP PIPELINED COMPUTATIONS
......@@ -1084,17 +1105,23 @@ The application program must also define the following procedure:
helpers_master void void
The following macros may be defined in helpers-app.h, but will default
to stubs if they are not defined there:
The following macros may be defined in helpers-app.h by the
application (but need not be defined for correct operation of the
helpers facility):
-----------------------------------------------------------
macro returns arguments
-----------------------------------------------------------
helpers_can_merge int see documentation above
helpers_merge void regarding these macros
helpers_mark_in_use void helpers_var_ptr
helpers_mark_not_in_use void helpers_var_ptr
helpers_mark_being_computed void helpers_var_ptr
helpers_mark_not_being_computed void helpers_var_ptr
helpers_is_in_use int helpers_var_ptr
helpers_is_being_computed int helpers_var_ptr
helpers_var_name char * helpers_var_ptr
helpers_task_name char * helpers_var_ptr
......@@ -1161,3 +1188,6 @@ options when building the helpers facility:
HELPERS_DISABLED
HELPERS_NO_MULTITHREADING
Note that the task merging facility is disabled if the application
does not define the helpers_can_merge macro.
......@@ -23,6 +23,10 @@
#include <Defn.h>
/* Defn.h defines the macros helpers_mark_in_use, helpers_mark_being_computed,
helpers_mark_not_in_use, helpers_mark_not_being_computed, helpers_is_in_use,
and helpers_is_being_computed. */
#undef helpers_wait_until_not_being_computed /* May've been def'd in Defn.h */
#undef helpers_wait_until_not_being_computed2 /* so helpers.h not always req */
......@@ -58,15 +62,6 @@ typedef SEXP helpers_var_ptr;
#define MAX_TASKS 31
/* MARKING MACROS. */
#define helpers_mark_in_use(v) ((v)->sxpinfo.in_use = 1)
#define helpers_mark_not_in_use(v) ((v)->sxpinfo.in_use = 0)
#define helpers_mark_being_computed(v) ((v)->sxpinfo.being_computed = 1)
#define helpers_mark_not_being_computed(v) ((v)->sxpinfo.being_computed = 0)
/* TASK AND VARIABLE NAMES FOR TRACE OUTPUT. Functions references are in
helpers-app.c. */
......@@ -96,15 +91,15 @@ extern char *Rf_var_name (helpers_var_ptr);
HELPERS_NOW_OR_LATER((((_flags_) & HELPERS_MERGE_IN_OUT) != 0 \
&& !helpers_not_merging || !helpers_not_multithreading) \
&& (_variant_ & VARIANT_PENDING_OK) && (_c_), \
IS_BEING_COMPUTED_BY_TASK(_in1_) || \
IS_BEING_COMPUTED_BY_TASK(_in2_), \
helpers_is_being_computed(_in1_) || \
helpers_is_being_computed(_in2_), \
_flags_, _proc_, _op_, _out_, _in1_, _in2_)
#define DO_NOW_OR_LATER1(_variant_,_c_,_flags_,_proc_,_op_,_out_,_in_) \
HELPERS_NOW_OR_LATER((((_flags_) & HELPERS_MERGE_IN_OUT) != 0 \
&& !helpers_not_merging || !helpers_not_multithreading) \
&& (_variant_ & VARIANT_PENDING_OK) && (_c_), \
IS_BEING_COMPUTED_BY_TASK(_in_), \
helpers_is_being_computed(_in_), \
_flags_, _proc_, _op_, _out_, _in_, NULL)
......
......@@ -93,6 +93,14 @@
#define helpers_printf printf
#endif
#ifndef helpers_is_being_computed
#define helpers_is_being_computed(v) 1
#endif
#ifndef helpers_is_in_use
#define helpers_is_in_use(v) 1
#endif
/* NULL VARIABLE POINTER, AND VARIABLE NAME MACRO HANDLING NULL. */
......@@ -1012,7 +1020,7 @@ static void notice_completed_proc (void)
other tasks to see if one is still using or computing the variable. */
# ifdef helpers_mark_not_being_computed
v = info->var[0];
{ v = info->var[0];
if (v!=null)
{ for (j = i+1; j<helpers_tasks; j++)
{ struct task_info *einfo = &task[used[j]].info;
......@@ -1023,15 +1031,17 @@ static void notice_completed_proc (void)
helpers_mark_not_being_computed(v);
}
done_c: ;
}
# endif
# ifdef helpers_mark_not_in_use
for (w = 1; w<=2; w++)
{ for (w = 1; w<=2; w++)
{ v = info->var[w];
if (v!=null && v!=info->var[0])
{ maybe_mark_not_in_use(v);
}
}
}
# endif
}
}
......@@ -1324,7 +1334,7 @@ void helpers_do_task
{
struct task_info *info;
int flags;
int pipe0;
mtix pipe0;
mtix *uh;
mtix t;
int i;
......@@ -1353,7 +1363,7 @@ void helpers_do_task
If one is found, "uh" is left pointing to its position in "used". */
pipe0 = 0;
if (out!=null && helpers_tasks>0)
if (out!=null && helpers_is_being_computed(out) && helpers_tasks>0)
{ uh = &used[helpers_tasks-1];
do
{ if (task[*uh].info.var[0]==out)
......@@ -1648,7 +1658,7 @@ out_of_merge:
if (any_needed)
{ wait_while_any_needed();
if (pipe0!=0) pipe0 = -1; /* task pipe0 might have finished, look again */
if (pipe0!=0) pipe0 = -1; /* task pipe0 might've finished, look again */
}
}
}
......@@ -1660,11 +1670,13 @@ out_of_merge:
or any other tasks in the master if no runnable master-only tasks. */
if (helpers_tasks==MAX_TASKS)
{ do
{
do
{ do_task_in_master(0);
notice_completed();
} while (helpers_tasks==MAX_TASKS);
if (pipe0!=0) pipe0 = -1; /* task pipe0 might have finished, look again */
if (pipe0!=0) pipe0 = -1; /* task pipe0 might've finished, look again */
}
/* Store info about the new task in a new task entry (t and info). But
......@@ -1688,20 +1700,6 @@ out_of_merge:
info->helper = -1; /* nobody is doing the task yet */
info->needed = 0; /* master isn't currently waiting for task to finish */
/* Set the in-use and being-computed flags as appropriate, if the
application defined the required macros. (Note that we don't do
this if the task is done directly in the master, since the flags
could never be consulted until unset anyway.) */
# ifdef helpers_mark_in_use
if (in1!=null && in1!=out) helpers_mark_in_use(in1);
if (in2!=null && in2!=out) helpers_mark_in_use(in2);
# endif
# ifdef helpers_mark_being_computed
if (out!=null) helpers_mark_being_computed(out);
# endif
/* Clear 'done' and 'amt_out' in the task info for the new task. Not
necessary in a task done directly in the master (since never seen). */
......@@ -1709,7 +1707,7 @@ out_of_merge:
info->amt_out = 0;
}
/* Record the previous task (if any) outputting the output variable of the
/* Look for the previous task (if any) outputting the output variable of the
new task. This was found earlier, and stored in pipe0, but if tasks
had to be done in the master, it might have changed, so look again. */
......@@ -1731,16 +1729,31 @@ out_of_merge:
/* For each input variable in the new task, find the task (if any) that is
outputting that variable. When more than one task has the same output
variable, the one scheduled most recently takes precedence. */
variable, the one scheduled most recently takes precedence. If an
input variable is the same as the output variable, we can use pipe0.
If helpers_is_being_computed is false, we don't need to search. */
info->pipe[1] = info->pipe[2] = 0;
if (helpers_tasks>0 && (in1!=null || in2!=null))
if (helpers_tasks>0)
{
uh = &used[helpers_tasks-1];
if (in1==null) goto search_in2;
if (in2==null) goto search_in1;
if (in1==out) info->pipe[1] = pipe0;
if (in2==out) info->pipe[2] = pipe0;
if (in1==null || in1==out || !helpers_is_being_computed(in1))
{ if (in2==null || in2==out || !helpers_is_being_computed(in2))
{ goto search_done;
}
else
{ goto search_in2;
}
}
if (in2==null || in2==out || !helpers_is_being_computed(in2))
{ goto search_in1;
}
do
{ if (task[*uh].info.var[0]==in1)
......@@ -1783,6 +1796,20 @@ out_of_merge:
goto direct;
}
/* Set the in-use and being-computed flags as appropriate, if the
application defined the required macros. (Note that we don't do
this if the task is done directly in the master, since the flags
could never be consulted until unset anyway.) */
# ifdef helpers_mark_in_use
if (in1!=null && in1!=out) helpers_mark_in_use(in1);
if (in2!=null && in2!=out) helpers_mark_in_use(in2);
# endif
# ifdef helpers_mark_being_computed
if (out!=null) helpers_mark_being_computed(out);
# endif
/* Mark the new task entry as being in use. */
helpers_tasks += 1;
......@@ -2010,7 +2037,7 @@ void helpers_wait_until_not_in_use (helpers_var_ptr v)
notice_completed();
if (helpers_tasks==0)
if (v==null || !helpers_is_in_use(v) || helpers_tasks==0)
{ if (trace) trace_wait_until_not_in_use(0,v);
return;
}
......@@ -2071,7 +2098,8 @@ void helpers_wait_until_not_being_computed2
notice_completed();
if (helpers_tasks==0)
if (helpers_tasks==0 || (v1==null || !helpers_is_being_computed(v1))
&& (v2==null || !helpers_is_being_computed(v2)))
{ if (trace) trace_wait_until_not_being_computed (0,v1,v2);
return;
}
......
......@@ -480,7 +480,7 @@ typedef struct {
Return of a variant result is usually indicated by the attribute field
being R_VariantResult, but a VARIANT_NULL variant result can be just
R_NilValue, and results with VARIANT_PENDING_OK may be ordinary vectors
marked as IS_BEING_COMPUTED_BY_TASK. */
marked as being computed. */
#define VARIANT_NULL 1 /* May just return R_NilValue (but do side effects) */
/* (Should usually be OR'd with VARIANT_PENDING_OK) */
......@@ -498,10 +498,16 @@ typedef struct {
#ifdef R_DEFERRED_EVAL
/* Access to markers maintained with assistance of the helpers facility. */
/* Markers maintained in conjunction with the helpers facility. */
#define IS_BEING_COMPUTED_BY_TASK(x) ((x)->sxpinfo.being_computed)
#define IS_IN_USE_BY_TASK(x) ((x)->sxpinfo.in_use)
#define helpers_is_being_computed(x) ((x)->sxpinfo.being_computed)
#define helpers_is_in_use(x) ((x)->sxpinfo.in_use)
#define helpers_mark_in_use(v) ((v)->sxpinfo.in_use = 1)
#define helpers_mark_not_in_use(v) ((v)->sxpinfo.in_use = 0)
#define helpers_mark_being_computed(v) ((v)->sxpinfo.being_computed = 1)
#define helpers_mark_not_being_computed(v) ((v)->sxpinfo.being_computed = 0)
/* Macros to wait until variables(s) computed. */
......@@ -510,19 +516,19 @@ typedef struct {
extern void helpers_wait_until_not_being_computed2 (SEXP, SEXP);
#define WAIT_UNTIL_COMPUTED(x) \
( ! IS_BEING_COMPUTED_BY_TASK(x) \
( ! helpers_is_being_computed(x) \
? (void) 0 \
: helpers_wait_until_not_being_computed(x) )
#define WAIT_UNTIL_COMPUTED_2(x1,x2) \
( ! IS_BEING_COMPUTED_BY_TASK(x1) && ! IS_BEING_COMPUTED_BY_TASK(x2) \
( ! helpers_is_being_computed(x1) && ! helpers_is_being_computed(x2) \
? (void) 0 \
: helpers_wait_until_not_being_computed2(x1,x2) ) \
#else
#define IS_BEING_COMPUTED_BY_TASK(x) 0 /* Stub routines used when support */
#define IS_IN_USE_BY_TASK(x) 0 /* for helpers is not enabled */
#define helpers_is_being_computed(x) 0 /* Stub routines used when support */
#define helpers_is_in_use(x) 0 /* for helpers is not enabled */
#define WAIT_UNTIL_COMPUTED(x) 0
#define WAIT_UNTIL_COMPUTED_2(x1,x2) 0
......
......@@ -306,20 +306,20 @@ extern void helpers_wait_until_not_in_use(SEXP);
#endif
#define NAMEDCNT(x) \
( (x)->sxpinfo.in_use && (x)->sxpinfo.nmcnt != MAX_NAMEDCNT \
( helpers_is_in_use(x) && (x)->sxpinfo.nmcnt != MAX_NAMEDCNT \
? (helpers_wait_until_not_in_use(x), (x)->sxpinfo.nmcnt) \
: (x)->sxpinfo.nmcnt )
#define NAMEDCNT_EQ_0(x) \
( (x)->sxpinfo.nmcnt != 0 ? 0 : !(x)->sxpinfo.in_use ? 1 \
( (x)->sxpinfo.nmcnt != 0 ? 0 : !helpers_is_in_use(x) ? 1 \
: (helpers_wait_until_not_in_use(x), 1) )
#define NAMEDCNT_GT_0(x) \
( (x)->sxpinfo.nmcnt != 0 ? 1 : !(x)->sxpinfo.in_use ? 0 \
( (x)->sxpinfo.nmcnt != 0 ? 1 : !helpers_is_in_use(x) ? 0 \
: (helpers_wait_until_not_in_use(x), 0) )
#define NAMEDCNT_GT_1(x) \
( (x)->sxpinfo.nmcnt > 1 ? 1 : !(x)->sxpinfo.in_use ? 0 \
( (x)->sxpinfo.nmcnt > 1 ? 1 : !helpers_is_in_use(x) ? 0 \
: (helpers_wait_until_not_in_use(x), 0) )
#define SET_NAMEDCNT(x,v) ((x)->sxpinfo.nmcnt = (v))
......@@ -357,7 +357,7 @@ extern void helpers_wait_until_not_in_use(SEXP);
#undef NAMEDCNT_GT_1
#define NAMEDCNT_GT_1(x) \
( ((x)->sxpinfo.nmcnt & (MAX_NAMEDCNT-1)) != 0 ? 1 \
: !(x)->sxpinfo.in_use ? 0 \
: !helpers_is_in_use(x) ? 0 \
: (helpers_wait_until_not_in_use(x), 0) )
#endif
......
......@@ -343,7 +343,7 @@ void attribute_hidden check_stack_balance(SEXP op, int save)
if (helpers_tasks > 0) { \
SEXP _a_ = (_args_); \
while (_a_ != R_NilValue) { \
if (IS_BEING_COMPUTED_BY_TASK(CAR(_a_))) { \
if (helpers_is_being_computed(CAR(_a_))) { \
wait_until_arguments_computed (_a_); \
break; \
} \
......@@ -362,7 +362,7 @@ void attribute_hidden wait_until_arguments_computed (SEXP args)
for (a = args; a != R_NilValue; a = CDR(a)) {
SEXP this_arg = CAR(a);
if (IS_BEING_COMPUTED_BY_TASK(this_arg)) {
if (helpers_is_being_computed(this_arg)) {
if (wait_for == NULL)
wait_for = this_arg;
else {
......
......@@ -1909,9 +1909,9 @@ static void RunGenCollect(R_size_t size_needed)
for (SEXP *var_list = helpers_var_list(); *var_list; var_list++) {
SEXP v = *var_list;
if (!NODE_IS_MARKED(v) && NODE_CLASS(v) == LARGE_NODE_CLASS) {
if (IS_BEING_COMPUTED_BY_TASK(v))
if (helpers_is_being_computed(v))
helpers_wait_until_not_being_computed(v);
if (IS_IN_USE_BY_TASK(v))
if (helpers_is_in_use(v))
helpers_wait_until_not_in_use(v);
}
}
......
......@@ -1834,7 +1834,7 @@ SEXP attribute_hidden do_writetable(SEXP call, SEXP op, SEXP args, SEXP rho)
if(len != nr * nc) /* quick integrity check */
error(_("corrupt matrix -- dims not not match length"));
if (IS_BEING_COMPUTED_BY_TASK(x)) {
if (helpers_is_being_computed(x)) {
helpers_start_computing_var(x);
avail = 0;
}
......
......@@ -1046,7 +1046,7 @@ static SEXP one_vector_subscript (SEXP x, SEXP s)
if (ix>0) {
R_len_t avail;
ix -= 1;
if (IS_BEING_COMPUTED_BY_TASK(x)) {
if (helpers_is_being_computed(x)) {
helpers_start_computing_var(x);
HELPERS_WAIT_IN_VAR (x, avail, ix, n);
}
......@@ -1133,7 +1133,7 @@ static SEXP two_matrix_subscripts (SEXP x, SEXP dim, SEXP s1, SEXP s2)
e = (ix1 - 1) + nrow * (ix2 - 1);
if (IS_BEING_COMPUTED_BY_TASK(x)) {
if (helpers_is_being_computed(x)) {
helpers_start_computing_var(x);
HELPERS_WAIT_IN_VAR (x, avail, e, LENGTH(x));
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment