/* primes_master.cc This program works together with primes_slave to count the number of primes between 1 and some specified upper limit. The upper limit can be specified as a command-line argument. This is the master program, which should be started by the user. */ #include #include #include #include const int DEFAULT_MAX = 1000000; int main(int argc, char **argv) { /* Get the upper limit from the first command-line argument, or use the default if no legal limit is given. */ int MAX = 0; if (argc > 1) MAX = atoi(argv[1]); if (MAX < 10) MAX = DEFAULT_MAX; cout << "Counting primes between 1 and " << MAX << "...\n"; /* Initialize PVM by getting the task identifier for this task. */ int tid = pvm_mytid(); if (tid < 0) { cout << "Error: Can't create pvm task. PVM not running??\n"; exit(1); } /* Use pvm_config to find the number of host computers that are part of the virtual machine and to get the hostinfo for each machine. I only need the hostct, not the other info provided by pvm_config */ int hostct, archct; struct pvmhostinfo *hostinfo; if (pvm_config(&hostct, &archct, &hostinfo) < 0) { cout << "Error: Can't get PVM configuration info.\n"; pvm_exit(); exit(1); } cout << "Found " << hostct << + " hosts; starting tasks...\n"; /* Start as many tasks as there are host computers. Each task runs the "primes_slave" program. Note that even the computer on which primes_master is running will run a copy of primes_slave. */ int task_tids[hostct]; // Task identifiers for the tasks that are // created. I need these to communicate // with the tasks. int taskct = pvm_spawn("primes_slave", 0, 0, 0, hostct, task_tids); if (taskct <= 0) { cout << "Error: Can't spawn tasks.\n"; pvm_exit(); exit(1); } /* The computation proceeds in two phases. The first phase makes a list of all the prime numbers in the range from 1 to sqrt(MAX). This probably won't take very long and could probably just be done by the master program. However, as an example, I divide the problem into taskct chunks and send one chunk to each task. A chunk consists of a range of values. The job of the slave task is to find all the primes in the assigned range and to return the list of primes to the master program. */ int s = (int)(sqrt(MAX) + 0.1); // Upper limit of range int limits[2]; // This will hold the endpoints of the range // that will be assigned to a particular task. // A task will be responsible for testing // numbers in the range limits[0] <= N <= limits[1]. int jobsize = s / taskct; // How many items to send to each task. limits[1] = 1; for (int i = 0; i < taskct; i++) { pvm_initsend(PvmDataRaw); limits[0] = limits[1] + 1; limits[1] = limits[0] + jobsize; if (i == taskct - 1 || limits[1] > s) { // This is to make sure that we cover all the integers // in the range and that we don't assign any integers // outside the range. If s is small, some tasks will // get a range of size 0. limits[1] = s; } pvm_pkint(limits,2,1); pvm_send(task_tids[i],1); // Send data, with message id 1 cout << "Sent limits " << limits[0] << " to " << limits[1] << " to task " << task_tids[i] << endl; } // The tasks have been sent. Now wait for and collect the results. int primes[s]; // An array to hold the primes. int primect = 0; // Number of primes stored in the array. int ct; // Number of primes that a particular slave has found. for (int i = 0; i < taskct; i++) { // The message from a slave is the number of primes that // it has found, followed by the primes themselves. Note // that messages are received from the slave tasks in order, // so that the primes will also be in order in the array. pvm_recv(task_tids[i],2); // Receive message from task i with msg id 2. pvm_upkint(&ct,1,1); // If there are any primes, get them as well. if (ct > 0) pvm_upkint(&primes[primect],ct,1); // Unpack directly into array. cout << "Got " << ct << " primes from task " << task_tids[i] << endl; primect += ct; } if (MAX <= 10000) { // Show user the primes, if there aren't a lot. cout << primect << " primes between 1 and " << s << ":\n"; for (int i = 0; i < primect; i++) cout << " " << primes[i] << endl; cout << endl; } /* Now that all the primes between 1 and s are known, send a copy of the list to each slave task. Since the same information is going to all the tasks, this can be done with pvm_mcast. */ cout << "Sending primes to tasks...\n"; pvm_initsend(PvmDataRaw); pvm_pkint(&primect,1,1); pvm_pkint(primes,primect,1); pvm_mcast(task_tids,taskct,3); /* In the second major phase of the computation, each slave task is sent a range of numbers between 1 and MAX. The slave must count the number of primes in the assigned range and send back the answer. */ jobsize = (MAX / taskct); limits[1] = 1; for (int i = 0; i < taskct; i++) { pvm_initsend(PvmDataRaw); limits[0] = limits[1] + 1; limits[1] = limits[0] + jobsize; if (i == taskct - 1 || limits[1] > MAX) limits[1] = MAX; pvm_pkint(limits,2,1); pvm_send(task_tids[i],4); cout << "Sent limits " << limits[0] << " to " << limits[1] << " to task " << task_tids[i] << endl; } // The assignments have been set. Collect the responses. cout << "Collecting totals from tasks...\n"; int total = 0; // Total number of primes found. for (int i = 0; i < taskct; i++) { pvm_recv(-1,5); // Accept a message from any task -- there is // no need to force the slaves to report in order. pvm_upkint(&ct,1,1); total += ct; } /* Report the final result and exit. */ cout << "\nNumber of primes is " << total << endl; pvm_exit(); return 0; }