Advertisement
sepi0l

hpc_dfsbfs

Apr 25th, 2024
618
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 6.65 KB | None | 0 0
  1. #include <iostream>
  2. #include <vector>
  3. #include <queue>
  4. #include <omp.h>
  5.  
  6. using namespace std;
  7.  
  8. // Graph class representing the adjacency list
  9. class Graph {
  10.     int V;  // Number of vertices
  11.     vector<vector<int>> adj;  // Adjacency list
  12.  
  13. public:
  14.     Graph(int V) : V(V), adj(V) {}
  15.     // Add an edge to the graph
  16.     void addEdge(int v, int w) {
  17.         adj[v].push_back(w);
  18.     }
  19.     // Parallel Depth-First Search
  20.     void parallelDFS(int startVertex) {
  21.         vector<bool> visited(V, false);
  22.         parallelDFSUtil(startVertex, visited);
  23.     }
  24.  
  25.     void parallelDFSUtil(int v, vector<bool>& visited) {
  26.         visited[v] = true;
  27.         cout << v << " ";
  28.         // Iterate through adjacent vertices
  29.         for (int i = 0; i < adj[v].size(); ++i) {
  30.             int n = adj[v][i];
  31.             // Check if the neighbor is unvisited
  32.             if (!visited[n]) {
  33.                 // Ensure only one thread explores the unvisited neighbor
  34.                 {
  35.                     if (!visited[n])
  36.                         parallelDFSUtil(n, visited);
  37.                 }
  38.             }
  39.         }
  40.     }
  41.  
  42.     // Parallel Breadth-First Search
  43.     void parallelBFS(int startVertex) {
  44.         vector<bool> visited(V, false);
  45.         queue<int> q;
  46.         visited[startVertex] = true;
  47.         q.push(startVertex);
  48.         while (!q.empty()) {
  49.             int v = q.front();
  50.             q.pop();
  51.             cout << v << " ";
  52.             // Iterate through adjacent vertices
  53.             #pragma omp parallel for
  54.             for (int i = 0; i < adj[v].size(); ++i) {
  55.                 int n = adj[v][i];
  56.                 // Check if the neighbor is unvisited
  57.                 if (!visited[n]) {
  58.                     // Ensure only one thread updates the visited array
  59.                     #pragma omp critical
  60.                     {
  61.                         if (!visited[n]) {
  62.                             visited[n] = true;
  63.                             q.push(n);
  64.                         }
  65.                     }
  66.                 }
  67.             }
  68.         }
  69.     }
  70. };
  71.  
  72. int main() {
  73.  
  74.     // Create a graph
  75.     double start_time, end_time;
  76.  
  77.     Graph g(7);
  78.     g.addEdge(0, 1);
  79.     g.addEdge(0, 2);
  80.     g.addEdge(1, 3);
  81.     g.addEdge(1, 4);
  82.     g.addEdge(2, 5);
  83.     g.addEdge(2, 6);
  84.  
  85.     /*
  86.               0      DFS - 0 1 3 4 2 5 6 🍾 | 0 2 6 5 1 4 3
  87.             /   \    BFS - 0 1 2 3 4 5 6 🍾 | 0 2 1 6 5 4 3
  88.            1     2
  89.           / \   / \
  90.          3   4 5   6
  91.     */
  92.    
  93.     cout << "Depth-First Search (DFS): ";
  94.     start_time = omp_get_wtime();
  95.     g.parallelDFS(0);
  96.     end_time = omp_get_wtime();  
  97.     cout << endl;
  98.     cout << "Parallel DFS took : " << end_time - start_time << " seconds.\n";
  99.  
  100.     cout << "Breadth-First Search (BFS): ";
  101.     start_time = omp_get_wtime();
  102.     g.parallelBFS(0);
  103.     end_time = omp_get_wtime();  
  104.     cout << endl;
  105.     cout << "Parallel BFS took : " << end_time - start_time << " seconds.\n";
  106.  
  107.     return 0;
  108. }
  109.  
  110.  
  111.  
  112. /*
  113. This code implements parallel Depth-First Search (DFS) and parallel Breadth-First
  114. Search (BFS) algorithms using OpenMP. Let's go through it:
  115.  
  116. **Graph Class:**
  117. - The `Graph` class represents an undirected graph using an adjacency list.
  118. - It has methods to add edges to the graph and to perform parallel DFS and parallel BFS.
  119.  
  120. **Parallel Depth-First Search (DFS):**
  121. - The `parallelDFS` function initiates the DFS traversal from a specified start vertex.
  122. - The `parallelDFSUtil` function is a recursive utility function that performs the DFS traversal.
  123. - Inside `parallelDFSUtil`, the `visited` array is accessed to mark vertices as visited.
  124. - Parallelism is introduced by OpenMP pragmas to explore adjacent vertices concurrently.
  125. However, a critical section is used to ensure that only one thread updates the `visited` array.
  126.  
  127. **Parallel Breadth-First Search (BFS):**
  128. - The `parallelBFS` function initiates the BFS traversal from a specified start vertex.
  129. - It uses a queue to store vertices to be processed.
  130. - Inside the BFS loop, OpenMP parallelism is introduced to explore adjacent vertices concurrently. Again,
  131.     a critical section is used to ensure thread safety when updating the `visited` array and the queue.
  132.  
  133. **Main Function:**
  134. - In the `main` function, a `Graph` object `g` is created with 7 vertices and edges connecting them.
  135. - DFS and BFS are performed starting from vertex 0, and the time taken for each operation is measured and printed.
  136.  
  137. **Parallelism in DFS and BFS:**
  138. - In both algorithms, OpenMP directives (`#pragma omp parallel for`) are used to parallelize the loop
  139.     that iterates through adjacent vertices.
  140. - The critical sections (`#pragma omp critical`) ensure that shared data structures (`visited` array and
  141.     queue) are accessed safely by multiple threads.
  142.  
  143. **Output:**
  144. - The DFS and BFS traversals are printed along with the time taken for each parallel traversal.
  145.  
  146. This code demonstrates how to parallelize graph traversal algorithms using OpenMP, improving performance
  147. by leveraging multiple threads to explore vertices concurrently.
  148.  
  149. Depth-First Search (DFS):
  150.  
  151. Normal DFS: In the normal depth-first search, the time complexity depends on the number of vertices (
  152. 𝑉) and edges (𝐸) in the graph. In the worst-case scenario, where the graph is represented using
  153. an adjacency list, and every vertex is visited exactly once, the time complexity is 𝑂(𝑉+𝐸).
  154. This is because each vertex and each edge is visited once.
  155.  
  156. Parallel DFS: Parallel depth-first search can offer potential speedup by exploring different
  157. branches of the search tree concurrently. However, the time complexity remains 𝑂(𝑉+𝐸) because the
  158. fundamental operation of visiting each vertex and edge remains the same.
  159.  
  160. Breadth-First Search (BFS):
  161.  
  162. Normal BFS: Similar to DFS, the time complexity of normal breadth-first search depends on the number of vertices (
  163. 𝑉) and edges (𝐸) in the graph. In the worst-case scenario, where the graph is
  164. represented using an adjacency list, and every vertex is visited exactly once, the time complexity is  𝑂(𝑉+𝐸).
  165.  
  166. Parallel BFS: Parallel breadth-first search explores different levels of the
  167. search tree concurrently, potentially offering speedup. However, like the parallel DFS,
  168. the time complexity remains 𝑂(𝑉+𝐸)
  169. O(V+E) because each vertex and edge still need to be visited once.
  170.  
  171. In summary, while parallel DFS and BFS algorithms can improve performance by
  172. exploiting concurrency, the fundamental time complexity remains the same as their
  173. sequential counterparts. This is because the number of vertices and edges still
  174. determine the overall computational cost of exploring the entire graph.
  175. */
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement