void
qt_loopaccum_balance(const size_t start, const size_t stop, const size_t
size, void *out, const qt_loopr_f func, void *argptr, const qt_accum_f
acc);
void
qt_loopaccum_balance_future(const size_t start, const size_t stop, const
size_t size, void *out, const qt_loopr_f func, void *argptr, const qt_accum_f
acc);
These function can be thought of as akin to (but not replicating exactly) the following code:
unsigned int i;
for (i = start; i < stop; i ++) {
func(NULL, argptr, ret);
acc(tmp, ret);
}
One qthread (or future) is spawned for each shepherd. The set of values of i (iterations) is divided evenly among the shepherds, and each qthread (or future) is assigned a set of iterations to perform.
The func argument must be a function pointer with a qt_loopr_f prototype. Note that this is NOT the same as qt_loop_f functions, as it has the extra ret argument. Its basic code structure is expected to look like this:
void func(qthread_t *me, const size_t startat, const size_t stopat, void *arg, void *ret)
{
for (unsigned int i = startat; i < stopat; i++) {
/* do work */
}
}
The arguments startat and stopat are determined by the library, and tell the function what range of i values (iterations) it is responsible for. qt_loop_balance and qt_loop_balance_future will not return until all of the qthreads (or futures) it spawned have exited.
The acc argument must be a function pointer with a qt_accum_f prototype. This prototype looks like this:
void acc (void *a, void *b);
The accumulating value is stored in the memory pointed to by the a argument, and a new value is passed in via the b argument. Keep in mind that when there is only a single shepherd, this function is never called, as the entire set of iterations will be given to a single instance of func to perform. Because of this, and because there is no guarantee as to how the iterations will be divided, func is expected to perform essentially the same accumulation operation that acc does. There is also no guarantee as to what order things will be accumulated in, so the operation needs to be commutative if all runs of the program are expected to return the same result.
The result of the accumulations (acc), if any, of the output (ret) of func will be stored in the memory pointed to by out. This memory is assumed to be at least size bytes, and size bytes will be used for storage of all ret arguments to func calls.
double qt_double_sum (double *array, size_t length)
{
double ret;}
qt_loopaccum_balance_future(0, length, sizeof(double), &ret, qtds_worker, array, qtds_acc);
return ret;
There are two function arguments to the qt_loopaccum_balance_future call, qtds_worker (as the func argument) and qtds_acc (as the acc or "accumulator" argument). qtds_acc is extremely simple:
static void qtds_acc (void *a,
void *b)
{
*(double*)a += *(double*)b;}
The qtds_worker function does the real work of adding up the numbers:
static void qtds_worker (qthread_t *me,
const size_t startat, const size_t stopat, void *array, void *ret)
{
size_t i;}
double sum = (double *)array[startat];
for (i = startat + 1; i < stopat; i++) {sum += (double *)array[i];}
*(double *)ret = sum;