简体   繁体   中英

Trying to process linked list data in parallel with OpenMP

I am trying to process linked list data in parallel with OpenMP in C++. I'm pretty new to OpenMP and pretty rusty with C++. What I want to do is get several threads to break up the linked list, and output the data of the Nodes in their particular range. I don't care about the order in which the output occurs. If I can get this working, I want to replace the simple output with some actual processing of the Node data.

I've found several things on the internet (including a few questions on this site) and from what I found, I cobbled together a code like this:

        #include <iostream>
        #include <omp.h>

        // various and sundry other stuff ...

        struct Node {
                int data;
                Node* next;
        };

        int main() {

            struct Node *newHead;
            struct Node *head = new Node;
            struct Node *currNode;
            int n;
            int tid;

            //create a bunch of Nodes in linked list with "data" ...

            // traverse the linked list:
            // examine data
            #pragma omp parallel private(tid)
            {
            currNode = head;
            tid=omp_get_thread_num();
            #pragma omp single
            {
            while (currNode) {
               #pragma omp task firstprivate(currNode)
               {
               cout << "Node data: " << currNode->data << " " << tid << "\n";
               } // end of pragma omp task
               currNode = currNode->next;
            } // end of while
            } //end of pragma omp single

            }  // end of pragma omp parallel


    // clean up etc. ...

    }  // end of main

So I run:

>: export OMP_NUM_THREADS=6
>: g++ -fopenmp ll_code.cpp
>: ./a.out

And the output is:

Node data: 5 0
Node data: 10 0
Node data: 20 0
Node data: 30 0
Node data: 35 0
Node data: 40 0
Node data: 45 0
Node data: 50 0
Node data: 55 0
Node data: 60 0
Node data: 65 0
Node data: 70 0
Node data: 75 0

So, tid is always 0. And that means, unless I'm really misunderstanding something, only one thread did anything with the linked list, and so the linked list was not traversed in parallel at all.

When I get rid of single , the code fails with a seg fault. I have tried moving a few variables in and out of the OpenMP directive scopes, with no change. Changing the number of threads has no effect. How can this be made to work?

A secondary question: Some sites say the firstprivate(currNode) is necessary and others say currNode is firstprivate by default. Who is right?

You certainly can traverse a linked list using multiple threads, but it will be actually slower than just using a single thread.

The reason is that, to know the address of a node N != 0 , you must know the address of node N-1 .

Assume now that you have N threads, each responsible for "starting at i position". The above paragraph implies that a thread i will depend on the result of thread i-1 , which in turn will depend on the result of thread i-2 , and so on.

What you end up with is a serial traversal anyway. But now, instead of just a simple for , you have to synchronize threads too, making things inherently slower.

But, if you're trying to do some heavy processing that would benefit from being run in parallel, then yes, you're going for the right approach. You can just change how you're getting the thread id:

#include <iostream>
#include <omp.h>

struct Node {
        int data;
        Node* next;
};

int main() {

    struct Node *head = new Node;
    struct Node *currNode = head;

    head->data = 0;
    for (int i=1;i<10;++i) {
        currNode->next = new Node;
        currNode = currNode->next;
        currNode->data = i;
    }

    // traverse the linked list:
    // examine data
    #pragma omp parallel
    {
        currNode = head;
        #pragma omp single
        {
            while (currNode) {
               #pragma omp task firstprivate(currNode)
               {
                   #pragma omp critical (cout)
                   std::cout << "Node data: " << currNode->data << " " << omp_get_thread_num() << "\n";
               }
               currNode = currNode->next;
            }
        }
    }
}

Possible output:

Node data: 0 4
Node data: 6 4
Node data: 7 4
Node data: 8 4
Node data: 9 4
Node data: 1 3
Node data: 2 5
Node data: 3 2
Node data: 4 1
Node data: 5 0

See it live!

Finally, for a more idiomatic approach, consider using a std::forward_list :

#include <forward_list>
#include <iostream>
#include <omp.h>

int main() {

    std::forward_list<int> list;
    for (int i=0;i<10;++i) list.push_front(i);

    #pragma omp parallel
    #pragma omp single
    for(auto data : list) {
       #pragma omp task firstprivate(data)
       #pragma omp critical (cout)
       std::cout << "Node data: " << data << " " << omp_get_thread_num() << "\n";
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM