CPU-GPU异构并行化APSP算法

一、Floyd-Warshall算法

介绍

Floyd-Warshall算法(英语:Floyd-Warshall algorithm),中文亦称弗洛伊德算法或佛洛依德算法,是解决任意两点间的最短路径的一种算法,可以正确处理有向图或负权(但不可存在负权回路)的最短路径问题,同时也被用于计算有向图的闭包传递。

原理

其本质为动态规划,给定有向图图 G = ( V , E ) G = (V, E) G=(V,E),其中 V ( v e r t i c e s ) V(vertices) V(vertices)为顶点数, E ( e d g e s ) E(edges) E(edges)为边数,并给出初始权重矩阵 w [ i ] [ j ] w[i][j] w[i][j],表示顶点 i → j i \rightarrow j ij的权重,其表达式为:
w i , j = { weight of edge ( i , j ) if ( i , j ) ∈ E ; ∞ if ( i , j ) ∉ E . \left.w_{i,j}=\left\{\begin{array}{ll}\text{weight of edge}\left(i,j\right)&\text{if}\left(i,j\right)\in E;\\\infty&\text{if}\left(i,j\right)\notin E.\end{array}\right.\right. wi,j={weight of edge(i,j)if(i,j)E;if(i,j)/E.
即,对于 i → j i \rightarrow j ij未连通的边通常设置为一个无穷大的数 I N F INF INF;对于动态规划算法需要定义状态 D i , j , k D_{i,j,k} Di,j,k:从 i i i j j j只以( 1.. k 1..k 1..k)集合中的节点为中间节点的最短路径的长度;则可分为以下2种情况讨论:

  1. 如果最短路经过点 k k k D i , j , k = D i , k , k − 1 + D k , j , k − 1 . D_{i,j,k}=D_{i,k,k-1}+D_{k,j,k-1}. Di,j,k=Di,k,k1+Dk,j,k1.

  2. 若最短路径不经过点 k k k: D i , j , k = D i , j , k − 1 。  D_{i,j,k}=D_{i,j,k-1\text{ 。 }} Di,j,k=Di,j,k1  

若不能理解 k − 1 k - 1 k1的含义,则可理解为下一层 k k k的状态需要上一层 k − 1 k - 1 k1推导出(因为要逐个枚举中间节点,例如 D 1 , 3 = D 1 , 2 + D 2 , 3 D_{1,3} = D_{1,2} + D_{2,3} D1,3=D1,2+D2,3,那么需要保证 D 1 , 2 , D 2 , 3 D_{1,2},D_{2,3} D1,2,D2,3是对应的最短距离,才能导致 D 1 , 3 D_{1,3} D1,3是1号节点到3号节点的最短距离)即第 k k k层状态依赖于第 k − 1 k-1 k1层状态,故不可对 k k k层循环做并行化处理;最后可以得到状态转移方程:
D i , j , k = min ⁡ ( D i , j , k − 1 , D i , k , k − 1 + D k , j , k − 1 ) D_{i,j,k}=\min(D_{i,j,k-1},D_{i,k,k-1}+D_{k,j,k-1}) Di,j,k=min(Di,j,k1,Di,k,k1+Dk,j,k1)
在实际算法中,为了节约空间,可以直接在原来空间上进行迭代,这样空间可降至二维。

分析

  1. 时间复杂度: O ( V 3 ) O(V^3) O(V3),其中 V V V是点集,对于 i , j i,j i,j两层for循环可使用OpenMP优化到线性
  2. 空间复杂度: O ( V 2 ) O(V^2) O(V2)

二、CPU-GPU并行化Floyd-APSP算法

为了求到全部的最短路径,不仅需要计算最短路径距离矩阵 D D D,还需要计算最短路径构造矩阵 C C C。其中 C C C矩阵的定义为:如果在顶点 i i i和顶点 j j j之间至少存在一条最短路径,则 C i , j C_{i,j} Ci,j表示最短路径上编号最高的中间顶点,否则为undefined (NULL)。构造矩阵的初值都是未定义的,用数学表示如下:
c i , j ( k ) = { NULL i f k = 0 ; k i f k ≥ 1 a n d d i , j ( k − 1 ) > d i , k ( k − 1 ) + d k , j ( k − 1 ) ; c i , j ( k − 1 ) otherwise. , \left.c_{i,j}^{(k)}=\left\{\begin{array}{ll}\text{NULL}&\mathrm{if~}k=0;\\k&\mathrm{if~}k\geq1\mathrm{~and~}d_{i,j}^{(k-1)}>d_{i,k}^{(k-1)}+d_{k,j}^{(k-1)};\\c_{i,j}^{(k-1)}&\text{otherwise.}\end{array}\right.\right., ci,j(k)= NULLkci,j(k1)if k=0;if k1 and di,j(k1)>di,k(k1)+dk,j(k1);otherwise.,
其中 C i , j k − 1 C_{i,j}^{k-1} Ci,jk1与上相同,由于下一层受到上一层的制约,为递推关系。

Algorithm1: Floyd-Warshall

  • Floyd-Warshall算法用于计算最短路径距离矩阵 D i , j D_{i,j} Di,j和最短路径构造矩阵 C i , j C_{i,j} Ci,j

在这里插入图片描述

Algorithm2:

  • 输出一对顶点 ( i , j ) (i, j) (i,j)之间最短路径的中间顶点的递归过程

在这里插入图片描述

可以想象为二叉树,一边是往左子树遍历,一边是往右子树遍历,即左根右的中序遍历。

分块联合算法

  • 该算法是为在CPU-GPU混合系统上实现高GPU利用率的快速APSP解决方案而设计的。

在分块联合算法中,将 n × n n \times n n×n的距离矩阵 D i , j D_{i,j} Di,j和构造矩阵 C i , j C_{i,j} Ci,j划分为 b × b b \times b b×b的子矩阵的分块,其中 b b b为分块因子,为以下问题讨论方便,假设 n % b = = 0 n \% b ==0 n%b==0,即 n n n能整除 b b b,并在每个块内有定义 A I , J = a [ i , j ] A_{I, J} = a[i, j] AI,J=a[i,j]来标识块索引为 ( I , J ) (I,J) (I,J)的子矩阵,用数学符号表示为:
1 ≤ I , J ≤ [ n b ] , 1 ≤ i , j ≤ b 1 \leq I, J \leq [\frac{n}{b}] , \\ 1 \leq i,j \leq b 1I,J[bn],1i,jb
如下图所示,展现了 n = 12 n = 12 n=12矩阵的示例,其中 b = 3 b=3 b=3

在这里插入图片描述

Algorithm3
  • 针对APSP问题的分块联合算法

在这里插入图片描述

将该算法划分4个阶段为:

  1. 首先将 n × n n \times n n×n的矩阵分解为长度为 [ n b ] × [ n b ] [\frac{n}{b}] \times [\frac{n}{b}] [bn]×[bn]的以 b × b b \times b b×b的矩阵,并外层枚举节点 ( K , K ) (K, K) (K,K),其中 1 ≤ K ≤ [ n b ] 1 \leq K \leq [\frac{n}{b}] 1K[bn],并在子矩阵 b × b b \times b b×b中使用Floyd-WarShall方法,求解 D K , K , C K , K D_{K, K}, C_{K,K} DK,K,CK,K
  2. 对节点 ( K , K ) (K, K) (K,K)所在的第 K K K列进行MMA即矩阵乘法加法操作
  3. 对节点 ( K , K ) (K, K) (K,K)所在的第 K K K行进行MMA即矩阵乘法加法操作
  4. 对于除以上涉及到的剩余节点
Algorithm4
  • APSP子问题的阻塞联合算法
  • 区别在于:算法4的4-16行运行在GPU中,算法4的合并操作17-20运行在CPU中。

在这里插入图片描述

在这里插入图片描述

Algorithm5
  • 子分块联合APSP的矩阵-矩阵""乘-加"算法

  • 代数中的MMA算法可以扩展为同时计算路径矩阵 D i , j D_{i,j} Di,j和构造矩阵 C i , j C_{i,j} Ci,j

z i , j ← min ⁡ ( z i , j , ∑ k = 1 b x i , k + y k , j ) z_{i,j} \leftarrow \min(z_{i,j}, \sum_{k=1}^b x_{i,k}+y_{k,j}) zi,jmin(zi,j,k=1bxi,k+yk,j)

其中, z i , j z_{i,j} zi,j b × b b \times b b×b的子矩阵。

在这里插入图片描述

Algorithm6
  • 阶段2-阶段4可以使用矩阵乘法更新,在本问题中,就是极小加代数。极小加代数的乘法和加法是分离执行的,极小加操作(MINPLUS)是运行在GPU中,矩阵加(MMA)运行在CPU中。

  • 这个操作减少了 Z , C Z,C ZC从CPU到GPU的数据传输,也就允许了CPU和GPU之间的高速通信。

在这里插入图片描述

Code

  • 以下代码均来自:https://github.com/EricLu1218/Parallel_Programming

模拟GPU的串行

#include <stdio.h>
#include <stdlib.h>const int INF = ((1 << 30) - 1);
const int V = 50010;
void input(char *inFileName);
void output(char *outFileName);void block_FW(int B);
int ceil(int a, int b);
void cal(int B, int Round, int block_start_x, int block_start_y, int block_width, int block_height);int n, m;
static int Dist[V][V];int main(int argc, char *argv[])
{input(argv[1]);int B = 512;block_FW(B);output(argv[2]);return 0;
}void input(char *infile)
{FILE *file = fopen(infile, "rb");fread(&n, sizeof(int), 1, file);fread(&m, sizeof(int), 1, file);for (int i = 0; i < n; ++i){for (int j = 0; j < n; ++j){if (i == j){Dist[i][j] = 0;}else{Dist[i][j] = INF;}}}int pair[3];for (int i = 0; i < m; ++i){fread(pair, sizeof(int), 3, file);Dist[pair[0]][pair[1]] = pair[2];}fclose(file);
}void output(char *outFileName)
{FILE *outfile = fopen(outFileName, "w");for (int i = 0; i < n; ++i){for (int j = 0; j < n; ++j){if (Dist[i][j] >= INF)Dist[i][j] = INF;}fwrite(Dist[i], sizeof(int), n, outfile);}fclose(outfile);
}int ceil(int a, int b) { return (a + b - 1) / b; }void block_FW(int B)
{int round = ceil(n, B);for (int r = 0; r < round; ++r){printf("%d %d\n", r, round);fflush(stdout);/* Phase 1 */cal(B, r, r, r, 1, 1);/* Phase 2 */cal(B, r, r, 0, r, 1);cal(B, r, r, r + 1, round - r - 1, 1);cal(B, r, 0, r, 1, r);cal(B, r, r + 1, r, 1, round - r - 1);/* Phase 3 */cal(B, r, 0, 0, r, r);cal(B, r, 0, r + 1, round - r - 1, r);cal(B, r, r + 1, 0, r, round - r - 1);cal(B, r, r + 1, r + 1, round - r - 1, round - r - 1);}
}void cal(int B, int Round, int block_start_x, int block_start_y, int block_width, int block_height)
{int block_end_x = block_start_x + block_height;int block_end_y = block_start_y + block_width;for (int b_i = block_start_x; b_i < block_end_x; ++b_i){for (int b_j = block_start_y; b_j < block_end_y; ++b_j){// To calculate B*B elements in the block (b_i, b_j)// For each block, it need to compute B timesfor (int k = Round * B; k < (Round + 1) * B && k < n; ++k){// To calculate original index of elements in the block (b_i, b_j)// For instance, original index of (0,0) in block (1,2) is (2,5) for V=6,B=2int block_internal_start_x = b_i * B;int block_internal_end_x = (b_i + 1) * B;int block_internal_start_y = b_j * B;int block_internal_end_y = (b_j + 1) * B;if (block_internal_end_x > n)block_internal_end_x = n;if (block_internal_end_y > n)block_internal_end_y = n;for (int i = block_internal_start_x; i < block_internal_end_x; ++i){for (int j = block_internal_start_y; j < block_internal_end_y; ++j){if (Dist[i][k] + Dist[k][j] < Dist[i][j]){Dist[i][j] = Dist[i][k] + Dist[k][j];}}}}}}
}

单GPU的CUDA代码

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>const int INF = (1 << 30) - 1;
int vertex_num, edge_num, matrix_size;
int *dist;double cal_time(struct timespec start, struct timespec end)
{struct timespec temp;if ((end.tv_nsec - start.tv_nsec) < 0){temp.tv_sec = end.tv_sec - start.tv_sec - 1;temp.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec;}else{temp.tv_sec = end.tv_sec - start.tv_sec;temp.tv_nsec = end.tv_nsec - start.tv_nsec;}return temp.tv_sec + (double)temp.tv_nsec / 1000000000.0;
}__device__ __host__ size_t index_convert(int i, int j, int row_size)
{return i * row_size + j;
}void input(char *input_file_path, int &block_factor)
{FILE *input_file = fopen(input_file_path, "rb");fread(&vertex_num, sizeof(int), 1, input_file);fread(&edge_num, sizeof(int), 1, input_file);matrix_size = ceil((double)vertex_num / (double)block_factor) * block_factor;cudaMallocHost((void **)&dist, matrix_size * matrix_size * sizeof(int));for (int i = 0; i < matrix_size; ++i){for (int j = 0; j < matrix_size; ++j){if (i != j)dist[index_convert(i, j, matrix_size)] = INF;else if (i < vertex_num)dist[index_convert(i, j, matrix_size)] = 0;elsedist[index_convert(i, j, matrix_size)] = INF;}}int data[3];for (int i = 0; i < edge_num; ++i){fread(data, sizeof(int), 3, input_file);dist[index_convert(data[0], data[1], matrix_size)] = data[2];}fclose(input_file);
}void output(char *output_file_path)
{FILE *output_file = fopen(output_file_path, "w");for (int i = 0; i < vertex_num; ++i){fwrite(&dist[index_convert(i, 0, matrix_size)], sizeof(int), vertex_num, output_file);}fclose(output_file);
}__constant__ int size[3]; //matrix size, block_factor, grid_size__global__ void phase1(int *d_dist, int round)
{__shared__ int share[4 * 1024];int i = threadIdx.y;int j = threadIdx.x;int i_offset = size[1] * round;int j_offset = size[1] * round;share[index_convert(j, i, size[1])] = d_dist[index_convert(i_offset + i, j_offset + j, size[0])];
#pragma unroll 32for (int k = 0; k < size[1]; ++k){__syncthreads();if (share[index_convert(j, i, size[1])] > share[index_convert(j, k, size[1])] + share[index_convert(k, i, size[1])])share[index_convert(j, i, size[1])] = share[index_convert(j, k, size[1])] + share[index_convert(k, i, size[1])];}d_dist[index_convert(i_offset + i, j_offset + j, size[0])] = share[index_convert(j, i, size[1])];
}__global__ void phase2(int *d_dist, int round)
{__shared__ int share[3 * 4 * 1024];int i = threadIdx.y;int j = threadIdx.x;int i_offset, j_offset;if (blockIdx.x == 0){i_offset = size[1] * ((round + blockIdx.y + 1) % size[2]);j_offset = size[1] * round;share[index_convert(i, j, size[1])] = d_dist[index_convert(i_offset + i, j_offset + j, size[0])];share[index_convert(i + size[1], j, size[1])] = share[index_convert(i, j, size[1])];share[index_convert(i + 2 * size[1], j, size[1])] = d_dist[index_convert(j_offset + i, j_offset + j, size[0])];}else{i_offset = size[1] * round;j_offset = size[1] * ((round + blockIdx.y + 1) % size[2]);share[index_convert(i, j, size[1])] = d_dist[index_convert(i_offset + i, j_offset + j, size[0])];share[index_convert(i + size[1], j, size[1])] = d_dist[index_convert(i_offset + i, i_offset + j, size[0])];share[index_convert(i + 2 * size[1], j, size[1])] = share[index_convert(i, j, size[1])];}#pragma unroll 32for (int k = 0; k < size[1]; ++k){__syncthreads();if (share[index_convert(i, j, size[1])] >share[index_convert(i + size[1], k, size[1])] + share[index_convert(k + 2 * size[1], j, size[1])])share[index_convert(i, j, size[1])] =share[index_convert(i + size[1], k, size[1])] + share[index_convert(k + 2 * size[1], j, size[1])];}d_dist[index_convert(i_offset + i, j_offset + j, size[0])] = share[index_convert(i, j, size[1])];
}__global__ void phase3(int *d_dist, int round)
{__shared__ int share[3 * 4 * 1024];int i = threadIdx.y;int j = threadIdx.x;int i_offset = size[1] * ((round + blockIdx.y + 1) % size[2]);int j_offset = size[1] * ((round + blockIdx.x + 1) % size[2]);int r_offset = size[1] * round;share[index_convert(i, j, size[1])] = d_dist[index_convert(i_offset + i, j_offset + j, size[0])];share[index_convert(i + size[1], j, size[1])] = d_dist[index_convert(i_offset + i, r_offset + j, size[0])];share[index_convert(i + 2 * size[1], j, size[1])] = d_dist[index_convert(r_offset + i, j_offset + j, size[0])];
#pragma unroll 32for (int k = 0; k < size[1]; ++k){__syncthreads();if (share[index_convert(i, j, size[1])] >share[index_convert(i + size[1], k, size[1])] + share[index_convert(k + 2 * size[1], j, size[1])])share[index_convert(i, j, size[1])] =share[index_convert(i + size[1], k, size[1])] + share[index_convert(k + 2 * size[1], j, size[1])];}d_dist[index_convert(i_offset + i, j_offset + j, size[0])] = share[index_convert(i, j, size[1])];
}int main(int argc, char **argv)
{double total_time, bfd_time;timespec total_time1, total_time2, bfd_time1, bfd_time2;clock_gettime(CLOCK_MONOTONIC, &total_time1);cudaSetDevice(0); // 设置运行的为第0块GPUint block_factor = 32;if (argc == 4)block_factor = atoi(argv[3]);input(argv[1], block_factor); // 读取数据并初始化distint grid_size = matrix_size / block_factor; // 划分后的网格大小N = [n / b]int size_info[3] = {matrix_size, block_factor, grid_size}; // n, b, N = [n / b]cudaMemcpyToSymbol(size, size_info, 3 * sizeof(int)); //  将矩阵大小、块大小和网格大小的信息传递给CUDA设备int *d_dist;clock_gettime(CLOCK_MONOTONIC, &bfd_time1);cudaMalloc(&d_dist, (size_t)sizeof(int) * matrix_size * matrix_size); // 在GPU上分配内存// 在GPU上分配和复制内存,将距离矩阵dist从主机(CPU)内存拷贝到设备(GPU)内存cudaMemcpy(d_dist, dist, (size_t)sizeof(int) * matrix_size * matrix_size, cudaMemcpyHostToDevice);// 定义了CUDA的线程块和网格的维度dim3 block(block_factor, block_factor); // (b, b)dim3 grid2(2, grid_size - 1); // (2, N - 1)dim3 grid3(grid_size - 1, grid_size - 1); // (N - 1, N - 1)for (int r = 0; r < grid_size; ++r){phase1<<<1, block>>>(d_dist, r);phase2<<<grid2, block>>>(d_dist, r);phase3<<<grid3, block>>>(d_dist, r);}cudaMemcpy(dist, d_dist, (size_t)sizeof(int) * matrix_size * matrix_size, cudaMemcpyDeviceToHost);clock_gettime(CLOCK_MONOTONIC, &bfd_time2);output(argv[2]);cudaFree(d_dist);cudaFree(dist);clock_gettime(CLOCK_MONOTONIC, &total_time2);bfd_time = cal_time(bfd_time1, bfd_time2);total_time = cal_time(total_time1, total_time2);printf(" vertex:   %d\n", vertex_num);printf(" I/O time: %.5f\n", total_time - bfd_time);printf(" cal time: %.5f\n", bfd_time);printf(" runtime:  %.5f\n", total_time);return 0;
}

2个GPU代码

#include <math.h>
#include <omp.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>const int INF = (1 << 30) - 1;
int vertex_num, edge_num, matrix_size;
int *dist;double cal_time(struct timespec start, struct timespec end)
{struct timespec temp;if ((end.tv_nsec - start.tv_nsec) < 0){temp.tv_sec = end.tv_sec - start.tv_sec - 1;temp.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec;}else{temp.tv_sec = end.tv_sec - start.tv_sec;temp.tv_nsec = end.tv_nsec - start.tv_nsec;}return temp.tv_sec + (double)temp.tv_nsec / 1000000000.0;
}__device__ __host__ size_t index_convert(int i, int j, int row_size)
{return i * row_size + j;
}void input(char *input_file_path, int block_factor)
{FILE *input_file = fopen(input_file_path, "rb");fread(&vertex_num, sizeof(int), 1, input_file);fread(&edge_num, sizeof(int), 1, input_file);matrix_size = ceil((double)vertex_num / (double)block_factor) * block_factor;cudaMallocHost((void **)&dist, matrix_size * matrix_size * sizeof(int));for (int i = 0; i < matrix_size; ++i){for (int j = 0; j < matrix_size; ++j){if (i != j)dist[index_convert(i, j, matrix_size)] = INF;else if (i < vertex_num)dist[index_convert(i, j, matrix_size)] = 0;elsedist[index_convert(i, j, matrix_size)] = INF;}}int data[3];for (int i = 0; i < edge_num; ++i){fread(data, sizeof(int), 3, input_file);dist[index_convert(data[0], data[1], matrix_size)] = data[2];}fclose(input_file);
}void output(char *output_file_path)
{FILE *output_file = fopen(output_file_path, "w");for (int i = 0; i < vertex_num; ++i){fwrite(&dist[index_convert(i, 0, matrix_size)], sizeof(int), vertex_num, output_file);}fclose(output_file);
}__constant__ int size[3]; //matrix size, block_factor, grid_size__global__ void phase1(int *d_dist, int round)
{__shared__ int pivot[1024];int i = threadIdx.y;int j = threadIdx.x;int i_offset = 32 * round;int j_offset = 32 * round;pivot[index_convert(i, j, 32)] = d_dist[index_convert(i_offset + i, j_offset + j, size[0])];
#pragma unroll 32for (int k = 0; k < 32; ++k){__syncthreads();if (pivot[index_convert(i, j, 32)] > pivot[index_convert(i, k, 32)] + pivot[index_convert(k, j, 32)])pivot[index_convert(i, j, 32)] = pivot[index_convert(i, k, 32)] + pivot[index_convert(k, j, 32)];}d_dist[index_convert(i_offset + i, j_offset + j, size[0])] = pivot[index_convert(i, j, 32)];
}__global__ void phase2(int *d_dist, int round)
{__shared__ int self[1024], pivot[1024];int i = threadIdx.y;int j = threadIdx.x;int i_offset, j_offset;if (blockIdx.x == 0 && blockIdx.y != round){i_offset = 32 * blockIdx.y;j_offset = 32 * round;self[index_convert(i, j, 32)] = d_dist[index_convert(i_offset + i, j_offset + j, size[0])];pivot[index_convert(i, j, 32)] = d_dist[index_convert(j_offset + i, j_offset + j, size[0])];
#pragma unroll 32for (int k = 0; k < 32; ++k){__syncthreads();if (self[index_convert(i, j, 32)] > self[index_convert(i, k, 32)] + pivot[index_convert(k, j, 32)])self[index_convert(i, j, 32)] = self[index_convert(i, k, 32)] + pivot[index_convert(k, j, 32)];}d_dist[index_convert(i_offset + i, j_offset + j, size[0])] = self[index_convert(i, j, 32)];}else if (blockIdx.y != round){i_offset = 32 * round;j_offset = 32 * blockIdx.y;self[index_convert(i, j, 32)] = d_dist[index_convert(i_offset + i, j_offset + j, size[0])];pivot[index_convert(i, j, 32)] = d_dist[index_convert(i_offset + i, i_offset + j, size[0])];
#pragma unroll 32for (int k = 0; k < 32; ++k){__syncthreads();if (self[index_convert(i, j, 32)] > pivot[index_convert(i, k, 32)] + self[index_convert(k, j, 32)])self[index_convert(i, j, 32)] = pivot[index_convert(i, k, 32)] + self[index_convert(k, j, 32)];}d_dist[index_convert(i_offset + i, j_offset + j, size[0])] = self[index_convert(i, j, 32)];}
}__global__ void phase3(int *d_dist, int round, int grid_offset)
{__shared__ int col[1024], row[1024];int self;int block_i = grid_offset + blockIdx.y;int block_j = blockIdx.x;if (block_i == round || block_j == round)return;int i = threadIdx.y;int j = threadIdx.x;int i_offset = 32 * block_i;int j_offset = 32 * block_j;int r_offset = 32 * round;self = d_dist[index_convert(i_offset + i, j_offset + j, size[0])];col[index_convert(i, j, 32)] = d_dist[index_convert(i_offset + i, r_offset + j, size[0])];row[index_convert(i, j, 32)] = d_dist[index_convert(r_offset + i, j_offset + j, size[0])];#pragma unroll 32for (int k = 0; k < 32; ++k){__syncthreads();if (self > col[index_convert(i, k, 32)] + row[index_convert(k, j, 32)])self = col[index_convert(i, k, 32)] + row[index_convert(k, j, 32)];}d_dist[index_convert(i_offset + i, j_offset + j, size[0])] = self;
}int main(int argc, char **argv)
{const int block_factor = 32, device_num = 2;input(argv[1], block_factor);int grid_size = matrix_size / block_factor;int *d_dist[2];
#pragma omp parallel num_threads(device_num){int device_id = omp_get_thread_num();cudaSetDevice(device_id);int size_info[3] = {matrix_size, block_factor, grid_size};cudaMemcpyToSymbol(size, size_info, 3 * sizeof(int));int grid_partition = grid_size / device_num;int grid_offset = device_id * grid_partition;int grid_count = grid_partition;if (device_id == device_num - 1)grid_count += grid_size % device_num;size_t grid_start = grid_offset * block_factor * matrix_size;cudaMalloc(&(d_dist[device_id]), (size_t)sizeof(int) * matrix_size * matrix_size);
#pragma omp barriercudaMemcpy(&(d_dist[device_id][grid_start]), &(dist[grid_start]),(size_t)sizeof(int) * block_factor * grid_count * matrix_size, cudaMemcpyHostToDevice);dim3 block(block_factor, block_factor);dim3 grid2(2, grid_size);dim3 grid3(grid_size, grid_count);for (int r = 0; r < grid_size; ++r){if (grid_offset <= r && r < grid_offset + grid_count){size_t copy_start = r * block_factor * matrix_size;if (device_id == 0)cudaMemcpy(&(d_dist[1][copy_start]), &(d_dist[0][copy_start]),(size_t)sizeof(int) * block_factor * matrix_size, cudaMemcpyDeviceToDevice);elsecudaMemcpy(&(d_dist[0][copy_start]), &(d_dist[1][copy_start]),(size_t)sizeof(int) * block_factor * matrix_size, cudaMemcpyDeviceToDevice);}
#pragma omp barrierphase1<<<1, block>>>(d_dist[device_id], r);phase2<<<grid2, block>>>(d_dist[device_id], r);phase3<<<grid3, block>>>(d_dist[device_id], r, grid_offset);}cudaMemcpy(&(dist[grid_start]), &(d_dist[device_id][grid_start]),(size_t)sizeof(int) * block_factor * grid_count * matrix_size, cudaMemcpyDeviceToHost);cudaFree(d_dist[omp_get_thread_num()]);
#pragma omp barrier}output(argv[2]);cudaFree(dist);return 0;
}

Reference

  1. https://zh.wikipedia.org/wiki/Floyd-Warshall%E7%AE%97%E6%B3%95
  2. Blocked United Algorithm for the All-Pairs Shortest Paths Problem on Hybrid CPU-GPU Systems
  3. https://github.com/EricLu1218/Parallel_Programming

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/683627.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

奔跑吧小恐龙(Java)

前言 Google浏览器内含了一个小彩蛋当没有网络连接时&#xff0c;浏览器会弹出一个小恐龙&#xff0c;当我们点击它时游戏就会开始进行&#xff0c;大家也可以玩一下试试&#xff0c;网址&#xff1a;恐龙快跑 - 霸王龙游戏. (ur1.fun) 今天我们也可以用Java来简单的实现一下这…

FileZilla Server 1.8.1内网搭建

配置环境服务器服务器下载服务器配置服务器配置 Server - ConfigureServer Listeners - Port 协议设置 Protocols settingsFTP and FTP over TLS(FTPS) Rights management(权利管理)Users(用户) 客户端建立连接 配置环境 服务器处于局域网内: 客户端 < -访问- > 公网 &l…

[嵌入式系统-17]:RT-Thread -3- 源代码目录结构

目录 前言&#xff1a;功能模块 一、RT-Thread 源代码目录结构 二、支持的CPU架构 三、SRC内核代码 前言&#xff1a;功能模块 一、RT-Thread 源代码目录结构 从RT-Thread的GitHub官网上面下载了内核源码&#xff0c;下载链接如下 https://github.com/RT-Thread/rt-thread…

HarmonyOS 通过getInspectorByKey获取指定元素高宽等属性

例如 这里 我们有这样一个组件 Entry Component struct Dom {build() {Column() {Row() {Circle({ width: 200, height: 200 }).fill(#20101010)}.id(ES)}.width(100%).height(100%)} }这里 我们就写了个很基本的组件结构 然后 我们写了个 Circle 组件 定义了宽高 然后 如果我…

数据接收程序

#include<reg51.h> //包含单片机寄存器的头文件 sbit pPSW^0; /***************************************************** 函数功能&#xff1a;接收一个字节数据 ***************************************************/ unsigned char Receive(void) { unsigned…

116. 填充每个节点的下一个右侧节点指针

给定一个 完美二叉树 &#xff0c;其所有叶子节点都在同一层&#xff0c;每个父节点都有两个子节点。二叉树定义如下&#xff1a; struct Node {int val;Node *left;Node *right;Node *next; } 填充它的每个 next 指针&#xff0c;让这个指针指向其下一个右侧节点。如果找不到…

Flink理论—容错之状态

Flink理论—容错之状态 在 Flink 的框架中&#xff0c;进行有状态的计算是 Flink 最重要的特性之一。所谓的状态&#xff0c;其实指的是 Flink 程序的中间计算结果。Flink 支持了不同类型的状态&#xff0c;并且针对状态的持久化还提供了专门的机制和状态管理器。 Flink 使用…

7 大 Android 数据恢复软件,可轻松找回丢失的数据

每年&#xff0c;由于各种原因&#xff0c;数百万人从他们的 Android 设备中丢失数据。它可能像意外删除文件一样简单&#xff0c;也可能像系统崩溃一样复杂。在这种情况下&#xff0c;拥有高效的数据恢复工具可以证明是救命稻草。Mac 用户尤其需要找到与其系统兼容的软件。好消…

不止于浏览器:掌握Node.js,开启全栈开发新篇章!

介绍&#xff1a;Node.js是一个基于Chrome V8引擎的JavaScript运行时环境&#xff0c;特别适合构建高性能的网络服务器和实时应用。具体介绍如下&#xff1a; 服务器端JavaScript&#xff1a;Node.js的核心优势之一是在服务器端运行JavaScript&#xff0c;这使得前端开发者可以…

如何利用SpringSecurity进行认证与授权

目录 一、SpringSecurity简介 1.1 入门Demo 二、认证 ​编辑 2.1 SpringSecurity完整流程 2.2 认证流程详解 2.3 自定义认证实现 2.3.1 数据库校验用户 2.3.2 密码加密存储 2.3.3 登录接口实现 2.3.4 认证过滤器 2.3.5 退出登录 三、授权 3.1 权限系统作用 3.2 授…

软件实例分享,门诊处方软件存储模板处方笺教程,个体诊所电子处方开单系统软件教程

软件实例分享&#xff0c;门诊处方软件存储模板处方笺教程&#xff0c;个体诊所电子处方开单系统软件教程、 一、前言 以下软件教程以 佳易王诊所电子处方管理软件V17.0为例说明 软件文件下载可以点击最下方官网卡片——软件下载——试用版软件下载 电子处方软件支持病历汇总…

CGAL Mesh分割

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 网格分割是将一个网格分解成更小的、有意义的子网格的过程。该过程用于建模,索具,纹理,形状检索,变形等应用。CGAL为我们提供了一个依赖于形状直径函数(SDF)的算法实现,即给定一个三角形表面网格包围一个3D实体…

对进程与线程的理解

目录 1、进程/任务&#xff08;Process/Task&#xff09; 2、进程控制块抽象(PCB Process Control Block) 2.1、PCB重要属性 2.2、PCB中支持进程调度的一些属性 3、 内存分配 —— 内存管理&#xff08;Memory Manage&#xff09; 4、线程&#xff08;Thread&#xff09;…

auto关键字详讲

目录 1.问题思考 2.auto关键字介绍 3. 早期auto的缺陷&#xff1a; 4.什么叫自动存储器&#xff1f; 5. c标准auto关键字 5.1auto的使用细节 5.2 auto什么时候不能推导变量的类型呢&#xff1f; 5.3基于范围的for循环 5.3.1范围for的用法 5.3.2 范围for的使用条件 6.…

书生浦语大模型实战营-课程笔记(3)

本节课主要是跟着教程做的&#xff0c;操作的东西放到作业里记录了。 这里主要记录一些视频里讲的非操作性的东西。 RAG外挂知识库&#xff1f;优点是成本低&#xff0c;不用重新训练 RAG的一个整体流程。 涉及了文本相似度匹配&#xff0c;是不是和传统的问答系统&#xff0…

【Linux学习】线程池

目录 23.线程池 23.1 什么是线程池 23.2 为什么需要线程池 23.3 线程池的应用场景 23.4 实现一个简单的线程池 23.4.1 RAII风格信号锁 23.4.2 线程的封装 23.4.3 日志打印 22.4.4 定义队列中存放Task类任务 23.4.5 线程池的实现(懒汉模式) 为什么线程池中需要有互斥锁和条件变…

片上网络NoC(3)——拓扑指标

目录 一、概述 二、指标 2.1 与网络流量无关的指标 2.1.1 度&#xff08;degree&#xff09; 2.1.2 对分带宽&#xff08;bisection bandwidth&#xff09; 2.1.3 网络直径&#xff08;diameter&#xff09; 2.2 与网络流量相关的指标 2.2.1 跳数&#xff08;hop coun…

【51单片机】初学者必读的一文【探究定时计数器与中断系统是如何配合起来的?】(9)

前言 大家好吖&#xff0c;欢迎来到 YY 滴单片机系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过单片机的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; YY的《C》专栏YY的《C11》专栏YY的…

Nvidia 推出了本地版聊天 Chat with RTX;OpenAI联创Karpathy宣布离职专注个人项目

&#x1f989; AI新闻 Nvidia 推出了本地版聊天 Chat with RTX 摘要&#xff1a;英伟达最近发布了名为“Chat with RTX”的Demo版个性化AI聊天机器人&#xff0c;适用于Windows平台&#xff0c;需要Nvidia的30系/40系显卡&#xff0c;显存至少为8GB&#xff0c;系统配置包括1…

【C++关联式容器】unordered_set

目录 unordered_set 1. 关联式容器额外的类型别名 2. 哈希桶 3. 无序容器对关键字类型的要求 4. Member functions 4.1 constructor、destructor、operator 4.1.1 constructor 4.1.2 destructor 4.1.3 operator 4.2 Capacity ​4.2.1 empty 4.2.2 size 4.2.3 max…