7.10 Parallel Ring AlgorithmsParallel processing refers to the partitioning of a problem so that pieces of the problem can be solved in parallel, thereby reducing the overall execution time. One measure of the effectiveness of the partitioning is the speedup, S(n), which is defined as follows. ![]() 7.10.1 Image filteringA filter is a transformation applied to an image. Filtering may remove noise, enhance detail or blur image features, depending on the type of transformation. This discussion considers a greyscale digital image represented by an n x n array of bytes. Common spatial filters replace each pixel value in such an image by a function of the original pixel and its neighbors. The filter algorithm uses a mask to specify the neighborhood that contributes to the calculation. Figure 7.11 shows a 3 x 3 mask of nearest neighbors. This particular mask represents a linear filter because the function is a weighted sum of the pixels in the mask. In contrast, a nonlinear filter cannot be written as a linear combination of pixels under the mask. Taking the median of the neighboring pixels is an example of a nonlinear filter. Figure 7.11. Mask for applying a smoothing filter to an image.![]() The values in the mask are the weights applied to each pixel in the sum when the mask is centered on the pixel being transformed. In Figure 7.11, all weights are 1/9. If ai,j is the pixel at position (i, j) of the original image and bi,j is the pixel at the corresponding position in the filtered image, the mask in Figure 7.11 represents the pixel transformation ![]() Figure 7.12. Mask for applying a difference filter to an image.![]() Filtering algorithms on the ringThe ring of processes is a natural architecture for parallelizing the types of filters described by masks such as those of Figures 7.11 and 7.12. Suppose a ring of n processes is to filter an n x n image. Each process can be responsible for computing the filter for one row or one column of the image. Since ISO C stores arrays in row-major format (i.e., the elements of a two-dimensional array are stored linearly in memory by first storing all elements of row zero followed by all elements of row one, and so on), it is more convenient to have each process handle one row.To perform the filtering operation in process p, do the following.
The preceding description is purposely vague about where the original image comes from and where it goes. This I/O is the heart of the problem. The simplest approach is to have each process read the part of the input image it needs from a file and write the resulting row to another file. In this approach, the processes are completely independent of each other. Assume that the original image is stored as a binary file of bytes in row-major order. Use lseek to position the file offset at the appropriate place in the file, and use read to input the three needed rows. After computing the new image, use lseek and write to write the row in the appropriate place in the image. Be sure to open the input and output image files after the fork so that each process on the ring has its own file offsets. A bidirectional ringAn alternative approach uses nearest-neighbor communication. Process p on the ring reads in only row p. It then writes row p to its neighbors on either side and reads rows p-1 and p+1 from its neighbors. This exchange of information requires the ring to be bidirectional, that is, a process node can read or write from the links in each direction. (Alternatively, replace each link in the ring by two unidirectional links, one in each direction.) It is probably overkill to implement the linear filter with nearest-neighbor communication, but several related problems require it.For example, the explicit method of solving the heat equation on an n x n grid uses a nearest-neighbor update of the form ![]() Block computationAnother important issue in parallel processing is the granularity of the problem and how it maps to the number of processes. The ring is typically under 100 processes, while the images of interest may be 1024 x 1024 pixels. In this case, each process computes the filter for a block of rows.Suppose the ring has m processes and the image has n x n pixels, where n = qm+r. The first r processes are responsible for q+1 rows, and the remaining processes are responsible for q rows. Each process computes from q and r the range of rows that it is responsible for. Pass m and n as command-line arguments to the original process in the ring. 7.10.2 Matrix multiplicationAnother problem that lends itself to parallel execution on a ring is matrix multiplication. To multiply two n x n matrices, A and B, form a third matrix C that has an entry in position (i, j) given by the following. ![]()
Initialize the elements of a[] and b[] from their respective files. Initialize c[], using
In process p, this approach accounts for the contribution of row p of B to row p of the output C. In other words, c[p,k] = a[p,p]*b[p,k]. Process p does the following.
The read function fills the b[] array with the values of the row of B held initially by the process immediately before it on the ring. One execution of the for loop adds the contribution of row p-1 of B to row p of the result corresponding to c[p,k]= c[p,k] +a[p,p-1]* b[p-1,k]. Execute this code n-1 times to multiply the entire array. Write the resulting c[] as row p of the output file. Note: The proposed strategy may cause a deadlock if n is so large that the write exceeds the size of PIPE_BUF. A more robust strategy might use select to process the reading and writing simultaneously. |