Open In App
Related Articles

Implementation of Quick sort using MPI, OMP and Posix thread

Improve Article
Improve
Save Article
Save
Like Article
Like

QuickSort is a Divide and Conquer Algorithm. It picks an element as a pivot and partitions the array around the picked pivot. There are many ways of choosing the pivot elements. They are:

  • Always pick the first element as a pivot.
  • Always pick the last element as the pivot (implemented below)
  • Pick a random element as a pivot.
  • Pick median as a pivot.      

MPI: MPI stands for Message Passing Interface. Here the message is data. MPI allows data to be passed between processes in a distributed memory environment. In C, “mpi.h” is a header file that includes all data structures, routines, and constants of MPI. Using “mpi.h” parallelized the quick sort algorithm.  Below is the C program to implement quicksort using MPI:

C




// C program to implement the Quick Sort
// Algorithm using MPI
#include <mpi.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
using namespace std;
 
// Function to swap two numbers
void swap(int* arr, int i, int j)
{
    int t = arr[i];
    arr[i] = arr[j];
    arr[j] = t;
}
 
// Function that performs the Quick Sort
// for an array arr[] starting from the
// index start and ending at index end
void quicksort(int* arr, int start, int end)
{
    int pivot, index;
 
    // Base Case
    if (end <= 1)
        return;
 
    // Pick pivot and swap with first
    // element Pivot is middle element
    pivot = arr[start + end / 2];
    swap(arr, start, start + end / 2);
 
    // Partitioning Steps
    index = start;
 
    // Iterate over the range [start, end]
    for (int i = start + 1; i < start + end; i++) {
 
        // Swap if the element is less
        // than the pivot element
        if (arr[i] < pivot) {
            index++;
            swap(arr, i, index);
        }
    }
 
    // Swap the pivot into place
    swap(arr, start, index);
 
    // Recursive Call for sorting
    // of quick sort function
    quicksort(arr, start, index - start);
    quicksort(arr, index + 1, start + end - index - 1);
}
 
// Function that merges the two arrays
int* merge(int* arr1, int n1, int* arr2, int n2)
{
    int* result = (int*)malloc((n1 + n2) * sizeof(int));
    int i = 0;
    int j = 0;
    int k;
 
    for (k = 0; k < n1 + n2; k++) {
        if (i >= n1) {
            result[k] = arr2[j];
            j++;
        }
        else if (j >= n2) {
            result[k] = arr1[i];
            i++;
        }
 
        // Indices in bounds as i < n1
        // && j < n2
        else if (arr1[i] < arr2[j]) {
            result[k] = arr1[i];
            i++;
        }
 
        // v2[j] <= v1[i]
        else {
            result[k] = arr2[j];
            j++;
        }
    }
    return result;
}
 
// Driver Code
int main(int argc, char* argv[])
{
    int number_of_elements;
    int* data = NULL;
    int chunk_size, own_chunk_size;
    int* chunk;
    FILE* file = NULL;
    double time_taken;
    MPI_Status status;
 
    if (argc != 3) {
        printf("Desired number of arguments are not their "
               "in argv....\n");
        printf("2 files required first one input and "
               "second one output....\n");
        exit(-1);
    }
 
    int number_of_process, rank_of_process;
    int rc = MPI_Init(&argc, &argv);
 
    if (rc != MPI_SUCCESS) {
        printf("Error in creating MPI "
               "program.\n "
               "Terminating......\n");
        MPI_Abort(MPI_COMM_WORLD, rc);
    }
 
    MPI_Comm_size(MPI_COMM_WORLD, &number_of_process);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank_of_process);
 
    if (rank_of_process == 0) {
        // Opening the file
        file = fopen(argv[1], "r");
 
        // Printing Error message if any
        if (file == NULL) {
            printf("Error in opening file\n");
            exit(-1);
        }
 
        // Reading number of Elements in file ...
        // First Value in file is number of Elements
        printf(
            "Reading number of Elements From file ....\n");
        fscanf(file, "%d", &number_of_elements);
        printf("Number of Elements in the file is %d \n",
               number_of_elements);
 
        // Computing chunk size
        chunk_size
            = (number_of_elements % number_of_process == 0)
                  ? (number_of_elements / number_of_process)
                  : (number_of_elements / number_of_process
                     - 1);
 
        data = (int*)malloc(number_of_process * chunk_size
                            * sizeof(int));
 
        // Reading the rest elements in which
        // operation is being performed
        printf("Reading the array from the file.......\n");
        for (int i = 0; i < number_of_elements; i++) {
            fscanf(file, "%d", &data[i]);
        }
 
        // Padding data with zero
        for (int i = number_of_elements;
             i < number_of_process * chunk_size; i++) {
            data[i] = 0;
        }
 
        // Printing the array read from file
        printf("Elements in the array is : \n");
        for (int i = 0; i < number_of_elements; i++) {
            printf("%d ", data[i]);
        }
 
        printf("\n");
 
        fclose(file);
        file = NULL;
    }
 
    // Blocks all process until reach this point
    MPI_Barrier(MPI_COMM_WORLD);
 
    // Starts Timer
    time_taken -= MPI_Wtime();
 
    // BroadCast the Size to all the
    // process from root process
    MPI_Bcast(&number_of_elements, 1, MPI_INT, 0,
              MPI_COMM_WORLD);
 
    // Computing chunk size
    chunk_size
        = (number_of_elements % number_of_process == 0)
              ? (number_of_elements / number_of_process)
              : number_of_elements
                    / (number_of_process - 1);
 
    // Calculating total size of chunk
    // according to bits
    chunk = (int*)malloc(chunk_size * sizeof(int));
 
    // Scatter the chuck size data to all process
    MPI_Scatter(data, chunk_size, MPI_INT, chunk,
                chunk_size, MPI_INT, 0, MPI_COMM_WORLD);
    free(data);
    data = NULL;
 
    // Compute size of own chunk and
    // then sort them
    // using quick sort
 
    own_chunk_size = (number_of_elements
                      >= chunk_size * (rank_of_process + 1))
                         ? chunk_size
                         : (number_of_elements
                            - chunk_size * rank_of_process);
 
    // Sorting array with quick sort for every
    // chunk as called by process
    quicksort(chunk, 0, own_chunk_size);
 
    for (int step = 1; step < number_of_process;
         step = 2 * step) {
        if (rank_of_process % (2 * step) != 0) {
            MPI_Send(chunk, own_chunk_size, MPI_INT,
                     rank_of_process - step, 0,
                     MPI_COMM_WORLD);
            break;
        }
 
        if (rank_of_process + step < number_of_process) {
            int received_chunk_size
                = (number_of_elements
                   >= chunk_size
                          * (rank_of_process + 2 * step))
                      ? (chunk_size * step)
                      : (number_of_elements
                         - chunk_size
                               * (rank_of_process + step));
            int* chunk_received;
            chunk_received = (int*)malloc(
                received_chunk_size * sizeof(int));
            MPI_Recv(chunk_received, received_chunk_size,
                     MPI_INT, rank_of_process + step, 0,
                     MPI_COMM_WORLD, &status);
 
            data = merge(chunk, own_chunk_size,
                         chunk_received,
                         received_chunk_size);
 
            free(chunk);
            free(chunk_received);
            chunk = data;
            own_chunk_size
                = own_chunk_size + received_chunk_size;
        }
    }
 
    // Stop the timer
    time_taken += MPI_Wtime();
 
    // Opening the other file as taken form input
    // and writing it to the file and giving it
    // as the output
    if (rank_of_process == 0) {
        // Opening the file
        file = fopen(argv[2], "w");
 
        if (file == NULL) {
            printf("Error in opening file... \n");
            exit(-1);
        }
 
        // Printing total number of elements
        // in the file
        fprintf(
            file,
            "Total number of Elements in the array : %d\n",
            own_chunk_size);
 
        // Printing the value of array in the file
        for (int i = 0; i < own_chunk_size; i++) {
            fprintf(file, "%d ", chunk[i]);
        }
 
        // Closing the file
        fclose(file);
 
        printf("\n\n\n\nResult printed in output.txt file "
               "and shown below: \n");
 
        // For Printing in the terminal
        printf("Total number of Elements given as input : "
               "%d\n",
               number_of_elements);
        printf("Sorted array is: \n");
 
        for (int i = 0; i < number_of_elements; i++) {
            printf("%d ", chunk[i]);
        }
 
        printf(
            "\n\nQuicksort %d ints on %d procs: %f secs\n",
            number_of_elements, number_of_process,
            time_taken);
    }
 
    MPI_Finalize();
    return 0;
}


Output:

OMP: OMP is Open Multi-Processing. It’s an Application Program Interface (API) that may be used to explicitly direct multi-threaded, shared memory parallelism. In C/ C++, “omp.h” is a header file that includes all the related directives related to OMP. Using “omp.h” parallelized quick sort. Below is the C++ program to implement the above concept:

C++




// C++ program to implement the Quick Sort
// using OMI
#include <bits/stdc++.h>
#include <omp.h>
using namespace std;
 
// Function to swap two numbers a and b
void swap(int* a, int* b)
{
    int t = *a;
    *a = *b;
    *b = t;
}
 
// Function to perform the partitioning
// of array arr[]
int partition(int arr[], int start, int end)
{
    // Declaration
    int pivot = arr[end];
    int i = (start - 1);
 
    // Rearranging the array
    for (int j = start; j <= end - 1; j++) {
        if (arr[j] < pivot) {
            i++;
            swap(&arr[i], &arr[j]);
        }
    }
    swap(&arr[i + 1], &arr[end]);
 
    // Returning the respective index
    return (i + 1);
}
 
// Function to perform QuickSort Algorithm
// using openmp
void quicksort(int arr[], int start, int end)
{
    // Declaration
    int index;
 
    if (start < end) {
 
        // Getting the index of pivot
        // by partitioning
        index = partition(arr, start, end);
 
// Parallel sections
#pragma omp parallel sections
        {
#pragma omp section
            {
                // Evaluating the left half
                quicksort(arr, start, index - 1);
            }
#pragma omp section
            {
                // Evaluating the right half
                quicksort(arr, index + 1, end);
            }
        }
    }
}
 
// Driver Code
int main()
{
    // Declaration
    int N;
 
    // Taking input the number of
    // elements we wants
    cout << "Enter the number of elements"
         << " you want to Enter\n";
    cin >> N;
 
    // Declaration of array
    int arr[N];
 
    cout << "Enter the array: \n";
 
    // Taking input that array
    for (int i = 0; i < N; i++) {
        cin >> arr[i];
    }
 
    // Calling quicksort having parallel
    // code implementation
    quicksort(arr, 0, N - 1);
 
    // Printing the sorted array
    cout << "Array after Sorting is: \n";
 
    for (int i = 0; i < N; i++) {
        cout << arr[i] << " ";
    }
 
    return 0;
}


Output: 

POSIX Threads: The POSIX thread libraries are a C/C++ thread API based on standards. It enables the creation of a new concurrent process flow. It works well on multi-processor or multi-core systems, where the process flow may be scheduled to execute on another processor, increasing speed through parallel or distributed processing. Below is the C++ program to implement quicksort using POSIX threads: 

C++14




// C++ program to implement the Quick Sort
// using POSIX Thread
#include <bits/stdc++.h>
#include <pthread.h>
using namespace std;
 
// Structure
struct data_set {
    int start_index;
    int end_index;
    int* data;
};
 
// Function to perform swap operations
void swap(int* a, int* b)
{
    int t = *a;
    *a = *b;
    *b = t;
}
 
// Partition function for making
// partition in array
int partition(int arr[], int left_index,
              int right_index)
{
    // Declaration and initialization
    // choosing pivot element form which
    // we make partition
 
    // Here pivot is last element of
    // the array
    int pivot = arr[right_index];
    int i = left_index - 1;
 
    // Making array as per requirement
    // arranging element smaller than
    // pivot on left side and larger
    // then pivot on right side
    for (int j = left_index;
         j <= right_index - 1; j++) {
 
        if (arr[j] < pivot) {
            i++;
            swap(&arr[i], &arr[j]);
        }
    }
 
    swap(&arr[i + 1], &arr[right_index]);
 
    // Returning the partition index
    return i + 1;
}
 
// Quicksort Function for sorting
// array
void* quick_sort(void* data)
{
    // Retrieving back the data sent
    // from thread
    struct data_set* info = (struct data_set*)data;
 
    // Declaration of left index
    int left_index, right_index, index;
 
    // Initialization of left and
    // right index
    left_index = info->start_index;
    right_index = info->end_index;
 
    // Recursive call of quick_sort
    // function
    if (left_index < right_index) {
 
        // Declaration of pthread and
        // pthread attribute type object
        pthread_attr_t attr;
        pthread_t first_thread;
        pthread_t second_thread;
 
        // Making two pointers of type
        // data_set for making again
        // call form thread
        struct data_set* info1 = new data_set;
        struct data_set* info2 = new data_set;
 
        // Their initialization
        info1->data = info->data;
        info2->data = info->data;
 
        // Initialize of pthread attribute
        pthread_attr_init(&attr);
 
        // For setting the set detach
        // state of attribute
        pthread_attr_setdetachstate(
            &attr, PTHREAD_CREATE_JOINABLE);
 
        // Partition the array for any
        // recursive call
        index = partition(info->data,
                          left_index,
                          right_index);
 
        info1->start_index = left_index;
        info1->end_index = index - 1;
 
        // Create pthread type object and
        // printing the error if any
        if (pthread_create(&first_thread,
                           &attr, quick_sort,
                           info1)) {
            cout << "Error in creating thread "
                 << endl;
 
            // Exiting in case of not
            // creation of thread
            exit(-1);
        }
 
        info2->start_index = index + 1;
        info2->end_index = right_index;
 
        // Creating pthread type object
        // and print the error
        if (pthread_create(&second_thread,
                           &attr, quick_sort,
                           info2)) {
            cout << "Error in creating thread "
                 << endl;
 
            // Exiting in case of not
            // creation of thread
            exit(-1);
        }
 
        // Joining the threads
        pthread_join(first_thread, NULL);
        pthread_join(second_thread, NULL);
    }
 
    return NULL;
}
 
// Driver Code
int main()
{
    // Declaration of Number of threads
    int N;
 
    struct data_set* info = new data_set;
 
    // Taking number of elements as input
    cout << "Enter number of elements"
         << " in the array: \n";
    cin >> N;
 
    // Declaration of array
    int A[N];
 
    // Initialization of array
    cout << "Enter the array: " << endl;
    for (int i = 0; i < N; i++) {
        cin >> A[i];
    }
 
    // Initialize of structure of
    // data_set type
    info->data = A;
    info->start_index = 0;
    info->end_index = N - 1;
 
    // Declaration of pthread object
    pthread_t thread_id;
 
    // Creating and pthread object and
    // printing the array of any
    if (pthread_create(&thread_id, NULL,
                       quick_sort,
                       info)) {
        cout << "Error in creating thread"
             << endl;
 
        // Exit in case of error
        exit(-1);
    }
 
    // Joining the pthread object
    int r1 = pthread_join(thread_id, NULL);
 
    // Printing the array if any in case
    // of joining
    if (r1) {
        cout << "Error in joining thread"
             << endl;
 
        // Exiting in case of error
        exit(-1);
    }
 
    // Printing the array after sorting
    cout << "Sorted Array is: " << endl;
 
    for (int i = 0; i < N; i++) {
        cout << A[i] << " ";
    }
    cout << endl;
 
    // Exiting from pthread programming
    pthread_exit(NULL);
 
    return 0;
}


Output:

 


Feeling lost in the world of random DSA topics, wasting time without progress? It's time for a change! Join our DSA course, where we'll guide you on an exciting journey to master DSA efficiently and on schedule.
Ready to dive in? Explore our Free Demo Content and join our DSA course, trusted by over 100,000 geeks!

Last Updated : 20 Mar, 2023
Like Article
Save Article
Previous
Next
Similar Reads
Complete Tutorials