Prev | Start | Next |
int main(int argc, char* argv[]) { pthread_t thread[Num_Proc]; int retval0, retval1, bounds[2], info; int primesfound=0; int Range0[3] = {0, 0, 100}; int Range1[3] = {1, 2, 100}; int ptid, tid, buf, len=0, tag=0; char my_name[100]; ptid = pvm_parent(); buf = pvm_recv(ptid, -1); if(buf <= 0) {pvm_perror("PVM_Recv failure\n");} info = pvm_bufinfo(buf, &len, &tag, &tid); if(info == PvmNoSuchBuf) {pvm_perror("PVM_BufInfo failure:PvmNoSuchBuf\n");} if(info == PvmBadParam) {pvm_perror("PVM_BufInfo failure:PvmBadParam\n");} pvm_upkint(bounds,2,1); do { Range0[0] = 0; Range0[1] = bounds[0]; Range0[2] = bounds[1]; Range1[0] = 1; Range1[1] = bounds[0] + 2; Range1[2] = bounds[1]; pthread_create(&thread[0], NULL, (void *)&findPrimes,(void *)&Range0); pthread_create(&thread[1], NULL, (void *)&findPrimes,(void *)&Range1); pthread_join(thread[0], (void*)&retval0); pthread_join(thread[1], (void*)&retval1); primesfound = *(int*)retval0 + *(int*)retval1; if(pvm_initsend(PvmDataDefault)<0) pvm_perror("initsend Error!!\n"); gethostname(my_name, 64); pvm_pkstr(my_name); pvm_pkint(&primesfound, 1, 1); pvm_pkint(bounds,2,1); pvm_send(ptid, 1); buf = pvm_recv(ptid, -1); if(buf <= 0) {pvm_perror("PVM_Recv failure\n");} info = pvm_bufinfo(buf, &len, &tag, &tid); if(info == PvmNoSuchBuf) {pvm_perror("PVM_BufInfo failure:PvmNoSuchBuf\n");} if(info == PvmBadParam) {pvm_perror("PVM_BufInfo failure:PvmBadParam\n");} pvm_upkint(bounds,2,1); } while (bounds[1] > 0); pvm_exit(); exit(0); }The interesting difference here from the SMP version is that there are no command line arguments supplied (although this method is one possible manner to provide such data, we don't wish to end the process at the the end of each iteration). We initially read the taskID for the master node, for use in communication later on [with pvm_parent() call]. The blocking wait is achieved with pvm_recv() initially and again at the end of the loop. "Blocking" means that the computation is stalled on this call until the request is satisfied. The computation is handled identically to the SMP version once the range is specified and until a signal is sent to indicate that the master has no more work requests. Data is only sent to the master process (on a different machine) for the duration of the computation.
Instructions will be given at the end of the tutorial on how to aquire a
tar ball of all of the code, but for completeness the above code is found
in
prime_thread.c. Also note that when the
code is compiled, the resulting binary must be located in a place that
your installation of PVM can 'find' for use with master.c.
${PVM_ROOT}/bin/LINUXworks for me, on our cluster, your mileage may vary.
int main(int argc, char* argv[]) { int cc=0, tid, i, buf, len=0, tag=0; int info, numsent = 0, numrecd = 0; char name[100]; int range = 1000; /* default minimal range for each node */ int LIMIT = 30000; /* default upper bound for computation */ int primes = 0, pfound = 0; int bounds[64][2], chunk[2], child[32]; if (argc > 1) { LIMIT = atoi(argv[1]); } /* mytid() _must_ be the first PVM function called!! */ printf("Master task[%x]\n", pvm_mytid()); cc = pvm_spawn("pvm_prime", (char**)0 , PvmTaskArch, "LINUX", 16, child); if(cc <= 0) {printf("PVM_SPAWN failure\n");pvm_exit();return -1;} range = ((LIMIT/cc) < range)? (LIMIT/cc) : range; printf("Starting %d PVM tasks: stride = %d\n", cc, range); for (i=0; i < cc; i++) { /* Send the data packets using range */ if(pvm_initsend(PvmDataDefault)<0) printf("initsend Error!!\n"); chunk[0] = i * range + 1; chunk[1] = (i+1)* range; pvm_pkint(chunk,2,1); pvm_send(child[i], 0); numsent++; } i=0; do { buf = pvm_recv(child[i%cc], -1); if(buf <= 0) {printf("PVM_Recv failure\n");continue;} info = pvm_bufinfo(buf, &len, &tag, &tid); if(info < 0) { if(info == PvmNoSuchBuf) {printf("PVM_BufInfo failure:PvmNoSuchBuf\n");continue;} if(info == PvmBadParam) {printf("PVM_BufInfo failure:PvmBadParam\n");continue;} } primes = 0; pvm_upkstr(name); pvm_upkint(&primes,1,1); pvm_upkint(bounds[i%cc], 2, 1); numrecd++; pfound += primes; chunk[0] += range; chunk[1] += range; if (chunk[1] <= LIMIT) { pvm_initsend(PvmDataDefault); pvm_pkint(chunk,2,1); pvm_send(child[i%cc], 0); numsent++; } i++; } while ( numrecd < numsent ); chunk[0] = chunk[1] = -1; for(i=0; iThe work heap management aspect of the master is the only issue at hand. After the necessary opening call to pvm_mytid(), pvm_spawn() seems to return whatever it feels like. So this returned value is used in a loop to assign work chunks of the grand computation to each node currently available. Data is packed and sent to nodes that are asking for further work, in the order they ask for it. This is actually a very important functionality to provide to avoid starvation whereby nodes are ready for more work but are denied for some reason. In the initial implementation of this code the master erroneously assigned work in a "for" loop based on childID. This was a mistake as it left nodes already finished their computation [blocking] queued for further work until all nodes predecessing them in taskID order were finished with their computations. The inefficiency of this method should be clear [and should have been to the programmer ;)]. The current solution [correctly] assigns work to the first node to report completion, thereby minimizing the amount of time any node spends awaiting further range data. Instructions will be given at the end of the tutorial on how to aquire a tar ball of all of the code, but for completeness the above code is found in master.c
Prev Start Next