Dana Vrajitoru
B424 Parallel and Distributed Programming

Pipelined Computations


The program consists in a series of tasks that have to be computed repeatedly in order.

Taskn(Taskn-1(...(Task2( Task1(data)))...));

Sequential algorithm
while (data) {
  Task_1();
  Task_2();
  ...
  Task_n();
}

Parallel algorithm. We are going to assign each task to a different process: no_proc = n.

Examples

Features

Pipeline Performance

More Examples

Pipeline_Process(id) { // process id
  while (data) {
    if (id == 0)
      input data;
    else
      Get(data, id-1);
    Execute_Task(data);
    if (id == no_proc - 1)
      output data;
    else 
      Send(data, id+1);
  }
}

Example: the sieve of Eratosthenes

Note: If x is the next prime number, then the first multiple of x to be eliminated is x2 because all the multiples of x less than x2 are obtained by multiplying x with a number smaller than it, so they must have been eliminated in the previous steps.

Eratosthenes(n*n)
{
  for (i=0; i<n*n; i++)
    mark[i] = true;
  x = 2;
  while (x <= n) {
    m = x*x;
    while (m < n*n) {
      mark[m] = false;
      m = m+x;
    }
    do
      ++x;
    while (!mark[x]);
  }
}

Parallel algorithm
The first process starts with 2 as its prime number, eliminates all of the even numbers, and sends all of the odd numbers to the next one.

First_process() // np = no_proc; 
{
  x = 2; 
  cout << x << endl;
  for (m=3; m < np*np; m++)
    if (m % x != 0)
      Send(m, 1);
  Send(terminator, 1);
}

The intermediate processes receive a set of numbers from the previous one. The first number they receive is a prime, so they output it. Then they check each of the subsequent numbers to see if they are multiples of the first one (x), and if they are not, then they send them to the next process in the pipeline.

Intermediate_process(id) {
  Get(x, id-1);
    cout << x << endl;
  do {
    Get(m, id-1);
    if (m==terminator || m%x != 0)
      Send(m, id + 1);
  } while (m != terminator);
}

The last process receives a first number that will be a prime (x). It checks all the other numbers it receives after that to see if they are multiples of x, and if they are not, then it outputs them. If the range of number is the square of the number of processes, then all the numbers output by the last process should be prime.

Last_process() 
{
  Get(x, id-1);
    cout << x << endl;
  do {
    Get(m, id-1);
    if (m!=terminator && m%x != 0)
      cout << m << endl;
  } while (m != terminator);
}

MPI Implementation
Sequential algorithm
eratosthenes.h
eratosthenes.cc
main.cc
Makefile