#include "brttutil.h" Pmtcomp * pmtcomp_create () long pmtcomp_destroy (Pmtcomp *mtc) long pmtcomp_addthread (Pmtcomp *mtc, long (*comp_sub) (long ioffset, long number, void *user_data), long ioffset, long number, void *user_data) long pmtcomp_setthread (Pmtcomp *mtc, long index, long (*comp_sub) (long ioffset, long number, void *user_data), long ioffset, long number, void *user_data) long pmtcomp_deletethread (Pmtcomp *mtc, long index) long pmtcomp_nthreads (Pmtcomp *mtc) long pmtcomp_processthreads (Pmtcomp *mtc)
These define an MT-safe interface for threading computational routines. The idea is that if you have a computational, or other, algorithm within a looping structure that is taking significant CPU resources and each iteration of the algorithm is independent of other iterations, you can break up the total number of looping iterations into a set of looping iterations over smaller ranges and run each in its own separate thread. In appropriate cases this should speed up the total computations by the number of available CPU cores. Note that the computational algorithms must be coded in such a way so that it is safe to execute the same code text over different threads simultaneously.
pmtcomp_create will create an empty Pmtcomp object ready to populate with the computational threads and return the Pmtcomp object pointer or NULL if there is an error.
pmtcomp_destroy will halt and join all of the existing threads associated with the mtc Pmtcomp object and free all resources associated with the object including the mtc structure itself.
pmtcomp_addthread will add a computational thread to a set of threads, specified by mtc as returned by pmtcomp_create, and launch the thread. The comp_sub argument is a pointer to the computation subroutine that is repeatedly called in this thread. It should return a number >= 0 if there are no errors and a negative number of there are errors. The ioffset argument is an integer that is passed to the computational subroutine when it is called and is meant to be a hint to the computational subroutine that tells it where in a looping iteration list to start processing. The number argument is an integer that is passed to the computational subroutine when it is called and is meant to be a hint to the computational subroutine that tells it how many iterations to process for each call to the subroutine. The user_data argument is a pointer to a user data structure. This routine returns -1 if there are errors or 0 if there are no errors.
pmtcomp_setthread will reset the parameters for a computational thread, specified by mtc as returned by pmtcomp_create and the thread index index. The other arguments are the same as for pmtcomp_addthread and are reset only if the arguments are not NULL, or, for the case of ioffset, greater than -1. This routine returns -1 if there are errors or 0 if there are no errors.
pmtcomp_deletethread will halt, join and delete a computational thread, specified by mtc as returned by pmtcomp_create and the thread index index.
pmtcomp_nthreads will return the number of computational threads associated with a Pmtcomp object specified by mtc as returned by pmtcomp_create.
pmtcomp_processthreads will make one processing pass through all of the threads in the mtc Pmtcomp object. Each of the comp_sub computational subroutines is called once for each of the threads running simultaneously, pmtcomp_processthreads waits for each of the computational subroutines to finish and then pmtcomp_processthreads returns with the number of threads that returned no errors. The individual return values for each thread are stored in the ret member of the PmtcompThread structure referenced by the mtc->threads entries (see brttutil.h).
/* A test for the pmtcomp routines. */ #include <stdio.h> #include <stdlib.h> #include <math.h> #include "brttutil.h" #include "coords.h" #include "stock.h" long mycomp (long ioffset, long number, void *user_data); /* This is the computational subroutine with dummy computations. */ long mycomp (long ioffset, long number, void *user_data) { long i, j, ret=0; double x; double *data = (double *)user_data; for (i=ioffset; i<ioffset+number; i++) { x = (i+1)*0.000123456; for (j=0; j<150; j++) { x = sin(x); x = cos(x)*sin(x)*x - sqrt(fabs(x)); } data[i] = x; } return (ret); } int main (int argc, char **argv) { Pmtcomp *mtc; unsigned long size; static double *data1; static double *data2; double time0, time1; long i, number_total; /* Allocate and initialize user data array. */ number_total = 1024*1024; size = number_total*sizeof(double); data1 = (double *) malloc(size); memset (data1, 0, size); data2 = (double *) malloc(size); memset (data2, 0, size); /* Compute and time iterations on a single thread. */ time0 = now(); mycomp(0, number_total, data1); time1 = now(); printf ("Took %.6f seconds for one thread\n", time1-time0); /* Create Pmtcomp object and populate with two threads. */ mtc = pmtcomp_create(); pmtcomp_addthread (mtc, mycomp, 0, number_total/2, data2); pmtcomp_addthread (mtc, mycomp, number_total/2, number_total/2, data2); /* Compute and time iterations on two threads. */ time0 = now(); pmtcomp_processthreads(mtc); time1 = now(); printf ("Took %.6f seconds for two threads\n", time1-time0); /* Test to make sure answers are the same. */ for (i=0; i<number_total; i++) { if (data1[i] != data2[i]) { printf ("Answers not the same at index %ld (%f != %f)\n", i, data1[i], data2[i]); exit (1); } } printf ("Answers the same\n"); /* Add two more threads and reset hint parameters in original two threads. */ pmtcomp_setthread (mtc, 0, NULL, 0, number_total/4, NULL); pmtcomp_setthread (mtc, 1, NULL, number_total/4, number_total/4, NULL); pmtcomp_addthread (mtc, mycomp, number_total/2, number_total/4, data2); pmtcomp_addthread (mtc, mycomp, 3*number_total/4, number_total/4, data2); /* Compute and time iterations on four threads. */ time0 = now(); pmtcomp_processthreads(mtc); time1 = now(); printf ("Took %.6f seconds for four threads\n", time1-time0); /* Test to make sure answers are the same. */ for (i=0; i<number_total; i++) { if (data1[i] != data2[i]) { printf ("Answers not the same at index %ld\n", i); exit (1); } } printf ("Answers the same\n"); /* Delete two threads, add six more and reset hint parameters for 8 total threads. */ pmtcomp_deletethread (mtc, 0); pmtcomp_deletethread (mtc, 0); pmtcomp_setthread (mtc, 0, NULL, 0, number_total/8, NULL); pmtcomp_setthread (mtc, 1, NULL, number_total/8, number_total/8, NULL); pmtcomp_addthread (mtc, mycomp, 2*number_total/8, number_total/8, data2); pmtcomp_addthread (mtc, mycomp, 3*number_total/8, number_total/8, data2); pmtcomp_addthread (mtc, mycomp, 4*number_total/8, number_total/8, data2); pmtcomp_addthread (mtc, mycomp, 5*number_total/8, number_total/8, data2); pmtcomp_addthread (mtc, mycomp, 6*number_total/8, number_total/8, data2); pmtcomp_addthread (mtc, mycomp, 7*number_total/8, number_total/8, data2); /* Compute and time iterations on eight threads. */ time0 = now(); pmtcomp_processthreads(mtc); time1 = now(); printf ("Took %.6f seconds for eight threads\n", time1-time0); /* Test to make sure answers are the same. */ for (i=0; i<number_total; i++) { if (data1[i] != data2[i]) { printf ("Answers not the same at index %ld\n", i); exit (1); } } printf ("Answers the same\n"); /* Shutdown all threads. */ pmtcomp_destroy (mtc); exit (0); } windom% pmtcomp_test Took 11.113200 seconds for one thread Took 5.679524 seconds for two threads Answers the same Took 3.278345 seconds for four threads Answers the same Took 1.615314 seconds for eight threads Answers the same windom%