chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:36:30 +08:00
commit 55ab4e4a73
473 changed files with 72932 additions and 0 deletions
+24
View File
@@ -0,0 +1,24 @@
cmake_minimum_required(VERSION 3.23)
project(gpu_easygraph LANGUAGES CXX CUDA)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
file(GLOB SOURCES
functions/*/*.c
functions/*/*.cpp
functions/*/*.cu
common/*.c
common/*.cpp
)
add_library(gpu_easygraph OBJECT ${SOURCES})
target_include_directories(gpu_easygraph
PRIVATE common
PRIVATE functions
)
set_target_properties(gpu_easygraph PROPERTIES
LINK_SEARCH_START_STATIC ON
LINK_SEARCH_END_STATIC ON
)
+23
View File
@@ -0,0 +1,23 @@
#pragma once
#include "err.h"
#define EG_DOUBLE_INF 1e100
#define EXIT_IF_CUDA_FAILED(condition) \
cuda_ret = condition; \
if (cuda_ret != cudaSuccess) { \
goto exit; \
} \
#ifndef _IN_
#define _IN_
#endif
#ifndef _OUT_
#define _OUT_
#endif
#ifndef _BUFFER_
#define _BUFFER_
#endif
+31
View File
@@ -0,0 +1,31 @@
#include <string>
#include "err.h"
namespace gpu_easygraph {
using std::string;
std::string err_code_detail(
int status
) {
switch (status) {
case EG_GPU_SUCC:
return "EasyGraph GPU: success";
case EG_GPU_FAILED_TO_ALLOCATE_HOST_MEM:
return "EasyGraph GPU: failed to allocate host mem";
case EG_GPU_FAILED_TO_ALLOCATE_DEVICE_MEM:
return "EasyGraph GPU: failed to allocate gpu mem";
case EG_GPU_DEVICE_ERR:
return "EasyGraph GPU: gpu error occurred";
case EG_GPU_UNKNOW_ERROR:
return "EasyGraph GPU: gpu unkonw error";
case EG_UNSUPPORTED_GRAPH:
return "EasyGraph GPU: unsupported graph type";
default:
break;
}
return "EasyGraph GPU: not a valid err_code";
}
} // namespace gpu_easygraph
+20
View File
@@ -0,0 +1,20 @@
#pragma once
#include <string>
namespace gpu_easygraph {
typedef enum {
EG_GPU_SUCC = 0,
EG_GPU_FAILED_TO_ALLOCATE_HOST_MEM,
EG_GPU_FAILED_TO_ALLOCATE_DEVICE_MEM,
EG_GPU_DEVICE_ERR,
EG_GPU_UNKNOW_ERROR,
EG_UNSUPPORTED_GRAPH
} EG_GPU_STATUS_CODE;
std::string err_code_detail(
int status
);
} // namespace gpu_easygraph
+423
View File
@@ -0,0 +1,423 @@
//
// This file is sourcing from here: https://peerj.com/articles/cs-140/
// Something such as vars' name, graph format, etc were changed
// for adapting easygraph's GPU framework
//
#include <cuda.h>
#include <cuda_runtime.h>
#include <stdlib.h>
#include "common.h"
namespace gpu_easygraph {
static __device__ double atomicAddDouble (
_OUT_ double* address,
_IN_ double val
)
{
unsigned long long int* address_as_ull =
(unsigned long long int*)address;
unsigned long long int old = *address_as_ull, assumed;
do {
assumed = old;
old = atomicCAS(address_as_ull, assumed,
__double_as_longlong(val +
__longlong_as_double(assumed)));
} while (assumed != old);
return __longlong_as_double(old);
}
static __device__ double atomicMinDouble (
_OUT_ double *address,
_IN_ double val
)
{
unsigned long long ret = __double_as_longlong(*address);
while (val < __longlong_as_double(ret))
{
unsigned long long old = ret;
if ((ret = atomicCAS((unsigned long long *)address, old, __double_as_longlong(val))) == old)
break;
}
return __longlong_as_double(ret);
}
static __global__ void d_calc_min_edge (
_IN_ int* d_V,
_IN_ int* d_E,
_IN_ double* d_W,
_IN_ int len_V,
_IN_ int len_E,
_OUT_ double* d_min_edge
)
{
int tid = blockIdx.x * blockDim.x + threadIdx.x;
if (tid < len_V) {
double curr_min = EG_DOUBLE_INF;
int edge_start = d_V[tid];
int edge_end = d_V[tid + 1];
for(int i = edge_start; i < edge_end; ++i) {
curr_min = min(curr_min, d_W[i]);
}
d_min_edge[tid] = curr_min;
}
}
static __global__ void d_dijkstra_bc (
_IN_ int* d_V,
_IN_ int* d_E,
_IN_ double* d_W,
_IN_ double* d_min_edge,
_IN_ int* d_sources,
_BUFFER_ double* d_dist_2D,
_BUFFER_ double* d_sigma_2D,
_BUFFER_ double* d_delta_2D,
_BUFFER_ int* d_U_2D,
_BUFFER_ int* d_F_2D,
_BUFFER_ int* d_lock_flag_2D,
_BUFFER_ int* d_st_2D,
_BUFFER_ int* d_st_idx_2D,
_IN_ int len_V,
_IN_ int len_E,
_IN_ int len_sources,
_IN_ int warp_size,
_IN_ int endpoints,
_OUT_ double* d_BC
)
{
for (int s_idx = blockIdx.x; s_idx < len_sources; s_idx += gridDim.x) {
int s = d_sources[s_idx];
double* d_dist = d_dist_2D + blockIdx.x * len_V;
double* d_sigma = d_sigma_2D + blockIdx.x * len_V;
double* d_delta = d_delta_2D + blockIdx.x * len_V;
int* d_U = d_U_2D + blockIdx.x * len_V;
int* d_F = d_F_2D + blockIdx.x * len_V;
int* d_lock_flag = d_lock_flag_2D + blockIdx.x * len_V;
int* d_st = d_st_2D + blockIdx.x * len_V;
int* d_st_idx = d_st_idx_2D + blockIdx.x * (len_V + 2);
__shared__ int len_F;
__shared__ int len_st;
__shared__ int len_st_idx;
__shared__ double delta;
for (int i = threadIdx.x; i < len_V; i += blockDim.x) {
d_dist[i] = EG_DOUBLE_INF;
d_sigma[i] = 0;
d_delta[i] = 0;
d_U[i] = 1;
d_lock_flag[i] = 0;
}
__syncthreads();
if (threadIdx.x == 0) {
d_dist[s] = 0;
d_sigma[s] = 1;
d_U[s] = 0;
d_F[0] = s;
len_F = 1;
d_st[0] = s;
len_st = 1;
d_st_idx[0] = 0;
d_st_idx[1] = 1;
len_st_idx = 2;
delta = 0.0;
}
__syncthreads();
int needlock = 1;
while (delta < EG_DOUBLE_INF) {
for (int j = threadIdx.x; j < len_F * warp_size; j += blockDim.x) {
int f = d_F[j / warp_size];
int edge_start = d_V[f];
int edge_end = d_V[f + 1];
double dist = d_dist[f];
for (int e = j % warp_size; e < edge_end - edge_start; e += warp_size) {
int adj = d_E[e + edge_start];
double relax_w = dist + d_W[e + edge_start];
needlock = 1;
while (needlock) {
if (atomicCAS(d_lock_flag + adj, 0, 1) == 0) {
if (relax_w < d_dist[adj]) {
d_dist[adj] = relax_w;
d_sigma[adj] = 0;
}
if (d_dist[adj] == relax_w) {
d_sigma[adj] += d_sigma[f];
}
atomicExch(d_lock_flag + adj, 0);
needlock = 0;
}
}
}
__threadfence_block();
}
__syncthreads();
if (threadIdx.x == 0) {
delta = EG_DOUBLE_INF;
}
__syncthreads();
for (int i = threadIdx.x; i < len_V; i += blockDim.x) {
double dist_i = d_dist[i];
if (d_U[i] == 1 && dist_i < EG_DOUBLE_INF) {
atomicMinDouble(&delta, dist_i + d_min_edge[i]);
}
}
__syncthreads();
if (threadIdx.x == 0) {
len_F = 0;
}
__syncthreads();
for (int i = threadIdx.x; i < len_V; i += blockDim.x) {
double dist_i = d_dist[i];
if (d_U[i] && dist_i < delta && dist_i < EG_DOUBLE_INF) {
d_U[i] = 0;
int f_idx = atomicAdd(&len_F, 1);
d_F[f_idx] = i;
}
}
__syncthreads();
for (int i = threadIdx.x; i < len_F; i += blockDim.x) {
int st_idx = atomicAdd(&len_st, 1);
d_st[st_idx] = d_F[i];
}
__syncthreads();
if (threadIdx.x == 0) {
d_st_idx[len_st_idx] = d_st_idx[len_st_idx - 1] + len_F;
++len_st_idx;
}
__syncthreads();
}
__shared__ int depth, st_start, st_end;
if (threadIdx.x == 0) {
depth = len_st_idx - 1;
}
__syncthreads();
if (threadIdx.x == 0 && endpoints) {
atomicAddDouble(d_BC + s, d_st_idx[depth] - 1);
}
__syncthreads();
while (depth > 0) {
if (threadIdx.x == 0) {
st_start = d_st_idx[depth - 1];
st_end = d_st_idx[depth];
}
__syncthreads();
for (int j = threadIdx.x; j < (st_end - st_start) * warp_size; j += blockDim.x) {
int pred = d_st[st_start + j / warp_size];
int edge_start = d_V[pred];
int edge_end = d_V[pred + 1];
double pred_sigma = d_sigma[pred];
double pred_dist = d_dist[pred];
for (int e = j % warp_size; e < edge_end - edge_start; e += warp_size) {
int succ = d_E[e + edge_start];
double weight = d_W[e + edge_start];
double succ_dist = d_dist[succ];
if (succ_dist == pred_dist + weight) {
atomicAddDouble(d_delta + pred,
pred_sigma / d_sigma[succ] * (1 + d_delta[succ]));
}
}
__threadfence_block();
if (j % warp_size == 0 && s != pred) {
atomicAddDouble(d_BC + pred, d_delta[pred] + endpoints);
}
}
__syncthreads();
if (threadIdx.x == 0) {
--depth;
}
__syncthreads();
}
}
}
static __global__ void d_rescale(
_IN_ int len_V,
_IN_ double scale,
_OUT_ double* d_BC
)
{
int tid = threadIdx.x + blockIdx.x * blockDim.x;
if (tid < len_V) {
d_BC[tid] *= scale;
}
}
static double calc_scale(
_IN_ int len_V,
_IN_ int is_directed,
_IN_ int normalized,
_IN_ int endpoints
)
{
double scale = 1.0;
if (normalized) {
if (endpoints) {
if (len_V < 2) {
scale = 1.0;
} else {
scale = 1.0 / (double(len_V) * (len_V - 1));
}
} else if (len_V <= 2) {
scale = 1.0;
} else {
scale = 1.0 / ((double(len_V) - 1) * (len_V - 2));
}
} else {
if (!is_directed) {
scale = 0.5;
} else {
scale = 1.0;
}
}
return scale;
}
int cuda_betweenness_centrality (
_IN_ int* V,
_IN_ int* E,
_IN_ double* W,
_IN_ int* sources,
_IN_ int len_V,
_IN_ int len_E,
_IN_ int len_sources,
_IN_ int warp_size,
_IN_ int is_directed,
_IN_ int normalized,
_IN_ int endpoints,
_OUT_ double* BC
)
{
int cuda_ret = cudaSuccess;
int EG_ret = EG_GPU_SUCC;
int block_size = 256;
size_t grid_size = len_V / block_size + (len_V % block_size != 0);
size_t mem_free = 0, mem_total = 0;
double scale = calc_scale(len_V, is_directed, normalized, endpoints);
int *d_V = NULL, *d_E = NULL, *d_sources= NULL, *d_lock_flag_2D = NULL;
int *d_U_2D = NULL, *d_F_2D = NULL, *d_st_2D = NULL, *d_st_idx_2D = NULL;
double *d_W = NULL, *d_min_edge = NULL, *d_dist_2D = NULL,
*d_sigma_2D = NULL, *d_delta_2D = NULL, *d_BC = NULL;
EXIT_IF_CUDA_FAILED(cudaMemGetInfo(&mem_free, &mem_total));
while (true) {
size_t mem_needed = sizeof(int) * len_V // d_V
+ sizeof(int) * len_E // d_E
+ sizeof(int) * len_sources // d_sources
+ sizeof(int) * grid_size * len_V // d_lock_flag_2D
+ sizeof(int) * grid_size * len_V // d_U_2D
+ sizeof(int) * grid_size * len_V // d_F_2D
+ sizeof(int) * grid_size * len_V // d_st_2D
+ sizeof(int) * grid_size * (len_V + 2) // d_st_idx_2D
+ sizeof(double) * len_E // d_W
+ sizeof(double) * len_V // d_min_edge
+ sizeof(double) * grid_size * len_V // d_dist_2D
+ sizeof(double) * grid_size * len_V // d_sigma_2D
+ sizeof(double) * grid_size * len_V // d_delta_2D
+ sizeof(double) * len_V // d_BC
;
if (mem_needed < mem_free / 2) {
break;
} else {
grid_size /= 2;
}
}
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_V, sizeof(int) * len_V));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_E, sizeof(int) * len_E));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_sources, sizeof(int) * len_sources));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_lock_flag_2D, sizeof(int) * grid_size * len_V));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_U_2D, sizeof(int) * grid_size * len_V));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_F_2D, sizeof(int) * grid_size * len_V));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_st_2D, sizeof(int) * grid_size * len_V));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_st_idx_2D, sizeof(int) * grid_size * (len_V + 2)));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_W, sizeof(double) * len_E));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_min_edge, sizeof(double) * len_V));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_dist_2D, sizeof(double) * grid_size * len_V));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_sigma_2D, sizeof(double) * grid_size * len_V));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_delta_2D, sizeof(double) * grid_size * len_V));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_BC, sizeof(double) * len_V));
EXIT_IF_CUDA_FAILED(cudaMemcpy(d_V, V, sizeof(int) * len_V, cudaMemcpyHostToDevice));
EXIT_IF_CUDA_FAILED(cudaMemcpy(d_E, E, sizeof(int) * len_E, cudaMemcpyHostToDevice));
EXIT_IF_CUDA_FAILED(cudaMemcpy(d_sources, sources, sizeof(int) * len_sources, cudaMemcpyHostToDevice));
EXIT_IF_CUDA_FAILED(cudaMemcpy(d_W, W, sizeof(double) * len_E, cudaMemcpyHostToDevice));
d_calc_min_edge<<<grid_size, block_size>>>(d_V, d_E, d_W, len_V, len_E, d_min_edge);
d_dijkstra_bc<<<grid_size, block_size>>>(d_V, d_E, d_W, d_min_edge, d_sources, d_dist_2D, d_sigma_2D,
d_delta_2D, d_U_2D, d_F_2D, d_lock_flag_2D, d_st_2D,
d_st_idx_2D, len_V, len_E, len_sources, warp_size,
endpoints, d_BC);
if (scale != 1.0) {
d_rescale<<<grid_size, block_size>>>(len_V, scale, d_BC);
}
EXIT_IF_CUDA_FAILED(cudaMemcpy(BC, d_BC, sizeof(double) * len_V, cudaMemcpyDeviceToHost));
exit:
cudaFree(d_V);
cudaFree(d_E);
cudaFree(d_sources);
cudaFree(d_lock_flag_2D);
cudaFree(d_U_2D);
cudaFree(d_F_2D);
cudaFree(d_st_2D);
cudaFree(d_st_idx_2D);
cudaFree(d_W);
cudaFree(d_min_edge);
cudaFree(d_dist_2D);
cudaFree(d_sigma_2D);
cudaFree(d_delta_2D);
cudaFree(d_BC);
if (cuda_ret != cudaSuccess) {
switch (cuda_ret) {
case cudaErrorMemoryAllocation:
EG_ret = EG_GPU_FAILED_TO_ALLOCATE_DEVICE_MEM;
break;
default:
EG_ret = EG_GPU_DEVICE_ERR;
break;
}
}
return EG_ret;
}
} // namespace gpu_easygraph
@@ -0,0 +1,399 @@
#include <cuda.h>
#include <cuda_runtime.h>
#include <stdlib.h>
#include "common.h"
namespace gpu_easygraph {
static __device__ double atomicAddDouble (
_OUT_ double* address,
_IN_ double val
)
{
unsigned long long int* address_as_ull =
(unsigned long long int*)address;
unsigned long long int old = *address_as_ull, assumed;
do {
assumed = old;
old = atomicCAS(address_as_ull, assumed,
__double_as_longlong(val +
__longlong_as_double(assumed)));
} while (assumed != old);
return __longlong_as_double(old);
}
static __device__ double atomicMinDouble (
_OUT_ double *address,
_IN_ double val
)
{
unsigned long long ret = __double_as_longlong(*address);
while (val < __longlong_as_double(ret))
{
unsigned long long old = ret;
if ((ret = atomicCAS((unsigned long long *)address, old, __double_as_longlong(val))) == old)
break;
}
return __longlong_as_double(ret);
}
static __global__ void d_calc_min_edge (
_IN_ int* d_V,
_IN_ int* d_E,
_IN_ double* d_W,
_IN_ int len_V,
_IN_ int len_E,
_OUT_ double* d_min_edge
)
{
int tid = blockIdx.x * blockDim.x + threadIdx.x;
if (tid < len_V) {
double curr_min = EG_DOUBLE_INF;
int edge_start = d_V[tid];
int edge_end = d_V[tid + 1];
for(int i = edge_start; i < edge_end; ++i) {
curr_min = min(curr_min, d_W[i]);
}
d_min_edge[tid] = curr_min;
}
}
static __global__ void d_dijkstra_bc (
_IN_ int* d_V,
_IN_ int* d_E,
_IN_ double* d_W,
_IN_ double* d_min_edge,
_IN_ int* d_sources,
_BUFFER_ double* d_dist_2D,
_BUFFER_ double* d_sigma_2D,
_BUFFER_ double* d_delta_2D,
_BUFFER_ int* d_U_2D,
_BUFFER_ int* d_F_2D,
_BUFFER_ int* d_st_2D,
_BUFFER_ int* d_st_idx_2D,
_IN_ int len_V,
_IN_ int len_E,
_IN_ int len_sources,
_IN_ int warp_size,
_IN_ int endpoints,
_OUT_ double* d_BC
)
{
for (int s_idx = blockIdx.x; s_idx < len_sources; s_idx += gridDim.x) {
int s = d_sources[s_idx];
double* d_dist = d_dist_2D + blockIdx.x * len_V;
double* d_sigma = d_sigma_2D + blockIdx.x * len_V;
double* d_delta = d_delta_2D + blockIdx.x * len_V;
int* d_U = d_U_2D + blockIdx.x * len_V;
int* d_F = d_F_2D + blockIdx.x * len_V;
int* d_st = d_st_2D + blockIdx.x * len_V;
int* d_st_idx = d_st_idx_2D + blockIdx.x * (len_V + 2);
__shared__ int len_F;
__shared__ int len_st;
__shared__ int len_st_idx;
__shared__ double delta;
for (int i = threadIdx.x; i < len_V; i += blockDim.x) {
d_dist[i] = EG_DOUBLE_INF;
d_sigma[i] = 0;
d_delta[i] = 0;
d_U[i] = 1;
}
__syncthreads();
if (threadIdx.x == 0) {
d_dist[s] = 0;
d_sigma[s] = 1;
d_U[s] = 0;
d_F[0] = s;
len_F = 1;
d_st[0] = s;
len_st = 1;
d_st_idx[0] = 0;
d_st_idx[1] = 1;
len_st_idx = 2;
delta = 0.0;
}
__syncthreads();
while (delta < EG_DOUBLE_INF) {
for (int j = threadIdx.x; j < len_F * warp_size; j += blockDim.x) {
int f = d_F[j / warp_size];
int edge_start = d_V[f];
int edge_end = d_V[f + 1];
double dist = d_dist[f];
for (int e = j % warp_size; e < edge_end - edge_start; e += warp_size) {
int adj = d_E[e + edge_start];
double relax_w = dist + d_W[e + edge_start];
atomicMinDouble(d_dist + adj, relax_w);
}
__threadfence_block();
}
__syncthreads();
if (threadIdx.x == 0) {
delta = EG_DOUBLE_INF;
}
__syncthreads();
for (int i = threadIdx.x; i < len_V; i += blockDim.x) {
double dist_i = d_dist[i];
if (d_U[i] == 1 && dist_i < EG_DOUBLE_INF) {
atomicMinDouble(&delta, dist_i + d_min_edge[i]);
}
}
__syncthreads();
if (threadIdx.x == 0) {
len_F = 0;
}
__syncthreads();
for (int i = threadIdx.x; i < len_V; i += blockDim.x) {
double dist_i = d_dist[i];
if (d_U[i] && dist_i < delta && dist_i < EG_DOUBLE_INF) {
d_U[i] = 0;
int f_idx = atomicAdd(&len_F, 1);
d_F[f_idx] = i;
}
}
__syncthreads();
for (int i = threadIdx.x; i < len_F; i += blockDim.x) {
int st_idx = atomicAdd(&len_st, 1);
d_st[st_idx] = d_F[i];
}
__syncthreads();
if (threadIdx.x == 0) {
d_st_idx[len_st_idx] = d_st_idx[len_st_idx - 1] + len_F;
++len_st_idx;
}
__syncthreads();
}
// calculate single source shortest path END
// calculate sigma START
for (int curr_lvl = 0; curr_lvl + 1 < len_st_idx; ++curr_lvl) {
int lvl_start = d_st_idx[curr_lvl];
int lvl_end = d_st_idx[curr_lvl + 1];
for (int j = threadIdx.x; j < (lvl_end - lvl_start) * warp_size; j += blockDim.x) {
int v = d_st[lvl_start + j / warp_size];
double dist_v = d_dist[v];
int edge_start = d_V[v];
int edge_end = d_V[v + 1];
for (int e = j % warp_size; e < edge_end - edge_start; e += warp_size) {
int adj = d_E[e + edge_start];
if (dist_v + d_W[e + edge_start] == d_dist[adj]) {
atomicAddDouble(d_sigma + adj, d_sigma[v]);
}
}
__threadfence_block();
}
__syncthreads();
}
// calculate sigma END
__shared__ int depth, st_start, st_end;
if (threadIdx.x == 0) {
depth = len_st_idx - 1;
}
__syncthreads();
if (threadIdx.x == 0 && endpoints) {
atomicAddDouble(d_BC + s, d_st_idx[depth] - 1);
}
__syncthreads();
while (depth > 0) {
if (threadIdx.x == 0) {
st_start = d_st_idx[depth - 1];
st_end = d_st_idx[depth];
}
__syncthreads();
for (int j = threadIdx.x; j < (st_end - st_start) * warp_size; j += blockDim.x) {
int pred = d_st[st_start + j / warp_size];
int edge_start = d_V[pred];
int edge_end = d_V[pred + 1];
double pred_sigma = d_sigma[pred];
double pred_dist = d_dist[pred];
for (int e = j % warp_size; e < edge_end - edge_start; e += warp_size) {
int succ = d_E[e + edge_start];
double weight = d_W[e + edge_start];
double succ_dist = d_dist[succ];
if (succ_dist == pred_dist + weight) {
atomicAddDouble(d_delta + pred,
pred_sigma / d_sigma[succ] * (1 + d_delta[succ]));
}
}
__threadfence_block();
if (j % warp_size == 0 && s != pred) {
atomicAddDouble(d_BC + pred, d_delta[pred] + endpoints);
}
}
__syncthreads();
if (threadIdx.x == 0) {
--depth;
}
__syncthreads();
}
}
}
static __global__ void d_rescale(
_IN_ int len_V,
_IN_ double scale,
_OUT_ double* d_BC
)
{
int tid = threadIdx.x + blockIdx.x * blockDim.x;
if (tid < len_V) {
d_BC[tid] *= scale;
}
}
static double calc_scale(
_IN_ int len_V,
_IN_ int is_directed,
_IN_ int normalized,
_IN_ int endpoints
)
{
double scale = 1.0;
if (normalized) {
if (endpoints) {
if (len_V < 2) {
scale = 1.0;
} else {
scale = 1.0 / (double(len_V) * (len_V - 1));
}
} else if (len_V <= 2) {
scale = 1.0;
} else {
scale = 1.0 / ((double(len_V) - 1) * (len_V - 2));
}
} else {
if (!is_directed) {
scale = 0.5;
} else {
scale = 1.0;
}
}
return scale;
}
int cuda_betweenness_centrality (
_IN_ int* V,
_IN_ int* E,
_IN_ double* W,
_IN_ int* sources,
_IN_ int len_V,
_IN_ int len_E,
_IN_ int len_sources,
_IN_ int warp_size,
_IN_ int is_directed,
_IN_ int normalized,
_IN_ int endpoints,
_OUT_ double* BC
)
{
int cuda_ret = cudaSuccess;
int EG_ret = EG_GPU_SUCC;
int block_size = 256;
size_t grid_size = len_V / block_size + (len_V % block_size != 0);
double scale = calc_scale(len_V, is_directed, normalized, endpoints);
int *d_V = NULL, *d_E = NULL, *d_sources= NULL;
int *d_U_2D = NULL, *d_F_2D = NULL, *d_st_2D = NULL, *d_st_idx_2D = NULL;
double *d_W = NULL, *d_min_edge = NULL, *d_dist_2D = NULL,
*d_sigma_2D = NULL, *d_delta_2D = NULL, *d_BC = NULL;
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_V, sizeof(int) * (len_V + 1)));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_E, sizeof(int) * len_E));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_sources, sizeof(int) * len_sources));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_U_2D, sizeof(int) * grid_size * len_V));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_F_2D, sizeof(int) * grid_size * len_V));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_st_2D, sizeof(int) * grid_size * len_V));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_st_idx_2D, sizeof(int) * grid_size * (len_V + 2)));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_W, sizeof(double) * len_E));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_min_edge, sizeof(double) * len_V));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_dist_2D, sizeof(double) * grid_size * len_V));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_sigma_2D, sizeof(double) * grid_size * len_V));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_delta_2D, sizeof(double) * grid_size * len_V));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_BC, sizeof(double) * len_V));
EXIT_IF_CUDA_FAILED(cudaMemcpy(d_V, V, sizeof(int) * (len_V + 1), cudaMemcpyHostToDevice));
EXIT_IF_CUDA_FAILED(cudaMemcpy(d_E, E, sizeof(int) * len_E, cudaMemcpyHostToDevice));
EXIT_IF_CUDA_FAILED(cudaMemcpy(d_sources, sources, sizeof(int) * len_sources, cudaMemcpyHostToDevice));
EXIT_IF_CUDA_FAILED(cudaMemcpy(d_W, W, sizeof(double) * len_E, cudaMemcpyHostToDevice));
d_calc_min_edge<<<grid_size, block_size>>>(d_V, d_E, d_W, len_V, len_E, d_min_edge);
d_dijkstra_bc<<<grid_size, block_size>>>(d_V, d_E, d_W, d_min_edge, d_sources, d_dist_2D,
d_sigma_2D, d_delta_2D, d_U_2D, d_F_2D, d_st_2D,
d_st_idx_2D, len_V, len_E, len_sources, warp_size,
endpoints, d_BC);
if (scale != 1.0) {
d_rescale<<<grid_size, block_size>>>(len_V, scale, d_BC);
}
EXIT_IF_CUDA_FAILED(cudaMemcpy(BC, d_BC, sizeof(double) * len_V, cudaMemcpyDeviceToHost));
exit:
cudaFree(d_V);
cudaFree(d_E);
cudaFree(d_sources);
cudaFree(d_U_2D);
cudaFree(d_F_2D);
cudaFree(d_st_2D);
cudaFree(d_st_idx_2D);
cudaFree(d_W);
cudaFree(d_min_edge);
cudaFree(d_dist_2D);
cudaFree(d_sigma_2D);
cudaFree(d_delta_2D);
cudaFree(d_BC);
if (cuda_ret != cudaSuccess) {
switch (cuda_ret) {
case cudaErrorMemoryAllocation:
EG_ret = EG_GPU_FAILED_TO_ALLOCATE_DEVICE_MEM;
break;
default:
EG_ret = EG_GPU_DEVICE_ERR;
break;
}
}
return EG_ret;
}
} // namespace gpu_easygraph
@@ -0,0 +1,426 @@
#include <cuda.h>
#include <cuda_runtime.h>
#include <stdlib.h>
#include "common.h"
namespace gpu_easygraph {
static __device__ double atomicAddDouble (
_OUT_ double* address,
_IN_ double val
)
{
unsigned long long int* address_as_ull =
(unsigned long long int*)address;
unsigned long long int old = *address_as_ull, assumed;
do {
assumed = old;
old = atomicCAS(address_as_ull, assumed,
__double_as_longlong(val +
__longlong_as_double(assumed)));
} while (assumed != old);
return __longlong_as_double(old);
}
static __device__ double atomicMinDouble (
_OUT_ double *address,
_IN_ double val
)
{
unsigned long long ret = __double_as_longlong(*address);
while (val < __longlong_as_double(ret))
{
unsigned long long old = ret;
if ((ret = atomicCAS((unsigned long long *)address, old, __double_as_longlong(val))) == old)
break;
}
return __longlong_as_double(ret);
}
static __global__ void d_calc_min_edge (
_IN_ int* d_V,
_IN_ int* d_E,
_IN_ double* d_W,
_IN_ int len_V,
_IN_ int len_E,
_OUT_ double* d_min_edge
)
{
int tid = blockIdx.x * blockDim.x + threadIdx.x;
int tnum = blockDim.x * gridDim.x;
for (int u = tid; u < len_V; u += tnum) {
double curr_min = EG_DOUBLE_INF;
int edge_start = d_V[u];
int edge_end = d_V[u + 1];
for(int v = edge_start; v < edge_end; ++v) {
curr_min = min(curr_min, d_W[v]);
}
d_min_edge[u] = curr_min;
}
}
static __global__ void d_dijkstra_bc (
_IN_ int* d_curr_node,
_IN_ int* d_V,
_IN_ int* d_E,
_IN_ double* d_W,
_IN_ double* d_min_edge,
_IN_ int* d_sources,
_BUFFER_ double* d_dist_2D,
_BUFFER_ double* d_sigma_2D,
_BUFFER_ double* d_delta_2D,
_BUFFER_ int* d_U_2D,
_BUFFER_ int* d_F_2D,
_BUFFER_ int* d_st_2D,
_BUFFER_ int* d_st_idx_2D,
_IN_ int len_V,
_IN_ int len_E,
_IN_ int len_sources,
_IN_ int warp_size,
_IN_ int endpoints,
_OUT_ double* d_BC
)
{
//for (int s_idx = blockIdx.x; s_idx < len_sources; s_idx += gridDim.x) {
while (1) {
__shared__ int curr_node;
if (threadIdx.x == 0) {
curr_node = atomicAdd(d_curr_node, 1);
}
__syncthreads();
if (curr_node >= len_sources) {
break;
}
int s = d_sources[curr_node];
double* d_dist = d_dist_2D + blockIdx.x * len_V;
double* d_sigma = d_sigma_2D + blockIdx.x * len_V;
double* d_delta = d_delta_2D + blockIdx.x * len_V;
int* d_U = d_U_2D + blockIdx.x * len_V;
int* d_F = d_F_2D + blockIdx.x * len_V;
int* d_st = d_st_2D + blockIdx.x * len_V;
int* d_st_idx = d_st_idx_2D + blockIdx.x * (len_V + 2);
__shared__ int len_F;
__shared__ int len_st;
__shared__ int len_st_idx;
__shared__ double delta;
for (int i = threadIdx.x; i < len_V; i += blockDim.x) {
d_dist[i] = EG_DOUBLE_INF;
d_sigma[i] = 0;
d_delta[i] = 0;
d_U[i] = 1;
}
__syncthreads();
if (threadIdx.x == 0) {
d_dist[s] = 0;
d_sigma[s] = 1;
d_U[s] = 0;
d_F[0] = s;
len_F = 1;
d_st[0] = s;
len_st = 1;
d_st_idx[0] = 0;
d_st_idx[1] = 1;
len_st_idx = 2;
delta = 0.0;
}
__syncthreads();
while (delta < EG_DOUBLE_INF) {
for (int j = threadIdx.x; j < len_F * warp_size; j += blockDim.x) {
int f = d_F[j / warp_size];
int edge_start = d_V[f];
int edge_end = d_V[f + 1];
double dist = d_dist[f];
for (int e = j % warp_size; e < edge_end - edge_start; e += warp_size) {
int adj = d_E[e + edge_start];
double relax_w = dist + d_W[e + edge_start];
atomicMinDouble(d_dist + adj, relax_w);
}
__threadfence_block();
}
__syncthreads();
if (threadIdx.x == 0) {
delta = EG_DOUBLE_INF;
}
__syncthreads();
for (int i = threadIdx.x; i < len_V; i += blockDim.x) {
double dist_i = d_dist[i];
if (d_U[i] == 1 && dist_i < EG_DOUBLE_INF) {
atomicMinDouble(&delta, dist_i + d_min_edge[i]);
}
}
__syncthreads();
if (threadIdx.x == 0) {
len_F = 0;
}
__syncthreads();
for (int i = threadIdx.x; i < len_V; i += blockDim.x) {
double dist_i = d_dist[i];
if (d_U[i] && dist_i < delta && dist_i < EG_DOUBLE_INF) {
d_U[i] = 0;
int f_idx = atomicAdd(&len_F, 1);
d_F[f_idx] = i;
}
}
__syncthreads();
for (int i = threadIdx.x; i < len_F; i += blockDim.x) {
int st_idx = atomicAdd(&len_st, 1);
d_st[st_idx] = d_F[i];
}
__syncthreads();
if (threadIdx.x == 0) {
d_st_idx[len_st_idx] = d_st_idx[len_st_idx - 1] + len_F;
++len_st_idx;
}
__syncthreads();
}
// calculate single source shortest path END
// calculate sigma START
for (int curr_lvl = 0; curr_lvl + 1 < len_st_idx; ++curr_lvl) {
int lvl_start = d_st_idx[curr_lvl];
int lvl_end = d_st_idx[curr_lvl + 1];
for (int j = threadIdx.x; j < (lvl_end - lvl_start) * warp_size; j += blockDim.x) {
int v = d_st[lvl_start + j / warp_size];
double dist_v = d_dist[v];
int edge_start = d_V[v];
int edge_end = d_V[v + 1];
for (int e = j % warp_size; e < edge_end - edge_start; e += warp_size) {
int adj = d_E[e + edge_start];
if (dist_v + d_W[e + edge_start] == d_dist[adj]) {
atomicAddDouble(d_sigma + adj, d_sigma[v]);
}
}
__threadfence_block();
}
__syncthreads();
}
// calculate sigma END
__shared__ int depth, st_start, st_end;
if (threadIdx.x == 0) {
depth = len_st_idx - 1;
}
__syncthreads();
if (threadIdx.x == 0 && endpoints) {
atomicAddDouble(d_BC + s, d_st_idx[depth] - 1);
}
__syncthreads();
while (depth > 0) {
if (threadIdx.x == 0) {
st_start = d_st_idx[depth - 1];
st_end = d_st_idx[depth];
}
__syncthreads();
for (int j = threadIdx.x; j < (st_end - st_start) * warp_size; j += blockDim.x) {
int pred = d_st[st_start + j / warp_size];
int edge_start = d_V[pred];
int edge_end = d_V[pred + 1];
double pred_sigma = d_sigma[pred];
double pred_dist = d_dist[pred];
for (int e = j % warp_size; e < edge_end - edge_start; e += warp_size) {
int succ = d_E[e + edge_start];
double weight = d_W[e + edge_start];
double succ_dist = d_dist[succ];
if (succ_dist == pred_dist + weight) {
atomicAddDouble(d_delta + pred,
pred_sigma / d_sigma[succ] * (1 + d_delta[succ]));
}
}
__threadfence_block();
if (j % warp_size == 0 && s != pred) {
atomicAddDouble(d_BC + pred, d_delta[pred] + endpoints);
}
}
__syncthreads();
if (threadIdx.x == 0) {
--depth;
}
__syncthreads();
}
}
}
static __global__ void d_rescale(
_IN_ int len_V,
_IN_ double scale,
_OUT_ double* d_BC
)
{
int tid = blockIdx.x * blockDim.x + threadIdx.x;
int tnum = blockDim.x * gridDim.x;
for (int u = tid; u < len_V; u += tnum) {
d_BC[u] *= scale;
}
}
static double calc_scale(
_IN_ int len_V,
_IN_ int is_directed,
_IN_ int normalized,
_IN_ int endpoints
)
{
double scale = 1.0;
if (normalized) {
if (endpoints) {
if (len_V < 2) {
scale = 1.0;
} else {
scale = 1.0 / (double(len_V) * (len_V - 1));
}
} else if (len_V <= 2) {
scale = 1.0;
} else {
scale = 1.0 / ((double(len_V) - 1) * (len_V - 2));
}
} else {
if (!is_directed) {
scale = 0.5;
} else {
scale = 1.0;
}
}
return scale;
}
int cuda_betweenness_centrality (
_IN_ const int* V,
_IN_ const int* E,
_IN_ const double* W,
_IN_ const int* sources,
_IN_ int len_V,
_IN_ int len_E,
_IN_ int len_sources,
_IN_ int warp_size,
_IN_ int is_directed,
_IN_ int normalized,
_IN_ int endpoints,
_OUT_ double* BC
)
{
int cuda_ret = cudaSuccess;
int EG_ret = EG_GPU_SUCC;
int min_edge_block_size;
int min_edge_grid_size;
int dijkstra_block_size;
int dijkstra_grid_size;
int rescale_block_size;
int rescale_grid_size;
cudaOccupancyMaxPotentialBlockSize(&min_edge_grid_size, &min_edge_block_size, d_calc_min_edge, 0, 0);
cudaOccupancyMaxPotentialBlockSize(&dijkstra_grid_size, &dijkstra_block_size, d_dijkstra_bc, 0, 0);
cudaOccupancyMaxPotentialBlockSize(&rescale_grid_size, &rescale_block_size, d_rescale, 0, 0);
double scale = calc_scale(len_V, is_directed, normalized, endpoints);
int *d_curr_node = NULL;
int *d_V = NULL, *d_E = NULL, *d_sources= NULL;
int *d_U_2D = NULL, *d_F_2D = NULL, *d_st_2D = NULL, *d_st_idx_2D = NULL;
double *d_W = NULL, *d_min_edge = NULL, *d_dist_2D = NULL,
*d_sigma_2D = NULL, *d_delta_2D = NULL, *d_BC = NULL;
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_curr_node, sizeof(int)));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_V, sizeof(int) * (len_V + 1)));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_E, sizeof(int) * len_E));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_sources, sizeof(int) * len_sources));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_U_2D, sizeof(int) * dijkstra_grid_size * len_V));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_F_2D, sizeof(int) * dijkstra_grid_size * len_V));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_st_2D, sizeof(int) * dijkstra_grid_size * len_V));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_st_idx_2D, sizeof(int) * dijkstra_grid_size * (len_V + 2)));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_W, sizeof(double) * len_E));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_min_edge, sizeof(double) * len_V));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_dist_2D, sizeof(double) * dijkstra_grid_size * len_V));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_sigma_2D, sizeof(double) * dijkstra_grid_size * len_V));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_delta_2D, sizeof(double) * dijkstra_grid_size * len_V));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_BC, sizeof(double) * len_V));
EXIT_IF_CUDA_FAILED(cudaMemset(d_curr_node, 0, 1));
EXIT_IF_CUDA_FAILED(cudaMemcpy(d_V, V, sizeof(int) * (len_V + 1), cudaMemcpyHostToDevice));
EXIT_IF_CUDA_FAILED(cudaMemcpy(d_E, E, sizeof(int) * len_E, cudaMemcpyHostToDevice));
EXIT_IF_CUDA_FAILED(cudaMemcpy(d_sources, sources, sizeof(int) * len_sources, cudaMemcpyHostToDevice));
EXIT_IF_CUDA_FAILED(cudaMemcpy(d_W, W, sizeof(double) * len_E, cudaMemcpyHostToDevice));
d_calc_min_edge<<<min_edge_grid_size, min_edge_block_size>>>(d_V, d_E, d_W, len_V, len_E, d_min_edge);
d_dijkstra_bc<<<dijkstra_grid_size, dijkstra_block_size>>>(d_curr_node, d_V, d_E, d_W, d_min_edge,
d_sources, d_dist_2D, d_sigma_2D, d_delta_2D, d_U_2D,
d_F_2D, d_st_2D, d_st_idx_2D, len_V, len_E, len_sources,
warp_size, endpoints, d_BC);
if (scale != 1.0) {
d_rescale<<<rescale_grid_size, rescale_block_size>>>(len_V, scale, d_BC);
}
EXIT_IF_CUDA_FAILED(cudaMemcpy(BC, d_BC, sizeof(double) * len_V, cudaMemcpyDeviceToHost));
exit:
cudaFree(d_curr_node);
cudaFree(d_V);
cudaFree(d_E);
cudaFree(d_sources);
cudaFree(d_U_2D);
cudaFree(d_F_2D);
cudaFree(d_st_2D);
cudaFree(d_st_idx_2D);
cudaFree(d_W);
cudaFree(d_min_edge);
cudaFree(d_dist_2D);
cudaFree(d_sigma_2D);
cudaFree(d_delta_2D);
cudaFree(d_BC);
if (cuda_ret != cudaSuccess) {
switch (cuda_ret) {
case cudaErrorMemoryAllocation:
EG_ret = EG_GPU_FAILED_TO_ALLOCATE_DEVICE_MEM;
break;
default:
EG_ret = EG_GPU_DEVICE_ERR;
break;
}
}
return EG_ret;
}
} // namespace gpu_easygraph
@@ -0,0 +1,64 @@
#pragma once
#include "common.h"
namespace gpu_easygraph {
/**
* description:
* use cuda to calculate betweenness_centrality. the graph must be
* in CSR format.
*
* arguments:
* V -
* the vertices in CSR format
*
* E -
* the edges in CSR format
*
* W -
* the weight of edges in CSR format
*
* sources -
* set of source vertices to consider when calculating shortest paths.
*
* len_V -
* len of V
*
* len_E -
* len of E
*
* warp_size -
* the number of threads assigned to a vertex
*
* is_directed -
* if this graph is directed
*
* normalized -
* if the answer needs to be normalized
*
* endpoints -
* if true include the endpoints in the shortest basic counts.
*
* BC -
* betweenness centrality output
*
* return:
* EG_GPU_STATUS_CODE
*/
int cuda_betweenness_centrality (
_IN_ const int* V,
_IN_ const int* E,
_IN_ const double* W,
_IN_ const int* sources,
_IN_ int len_V,
_IN_ int len_E,
_IN_ int len_sources,
_IN_ int warp_size,
_IN_ int is_directed,
_IN_ int normalized,
_IN_ int endpoints,
_OUT_ double* BC
);
} // namespace gpu_easygraph
@@ -0,0 +1,83 @@
#include <vector>
#include <string>
#include "centrality/closeness_centrality.cuh"
#include "centrality/betweenness_centrality.cuh"
#include "common.h"
namespace gpu_easygraph {
using std::pair;
using std::string;
using std::vector;
static int decide_warp_size (
_IN_ int len_V,
_IN_ int len_E
)
{
vector<int> warp_size_cand{1, 2, 4, 8, 16, 32};
if (len_E / len_V < warp_size_cand.front()) {
return warp_size_cand.front();
}
for (int i = 0; i + 1 < warp_size_cand.size(); ++i) {
if (warp_size_cand[i] <= len_E / len_V
&& len_E / len_V < warp_size_cand[i + 1]) {
return warp_size_cand[i + 1];
}
}
return warp_size_cand.back();
}
int closeness_centrality(
_IN_ const std::vector<int>& V,
_IN_ const std::vector<int>& E,
_IN_ const std::vector<double>& W,
_IN_ const std::vector<int>& sources,
_OUT_ std::vector<double>& CC
) {
int len_V = V.size() - 1;
int len_E = E.size();
int warp_size = decide_warp_size(len_V, len_E);
CC = vector<double>(len_V);
int r = cuda_closeness_centrality(V.data(), E.data(), W.data(),
sources.data(), len_V, len_E, sources.size(),
warp_size, CC.data());
return r;
}
int betweenness_centrality(
_IN_ const std::vector<int>& V,
_IN_ const std::vector<int>& E,
_IN_ const std::vector<double>& W,
_IN_ const std::vector<int>& sources,
_IN_ bool is_directed,
_IN_ bool normalized,
_IN_ bool endpoints,
_OUT_ std::vector<double>& BC
) {
int len_V = V.size() - 1;
int len_E = E.size();
int warp_size = decide_warp_size(len_V, len_E);
BC = vector<double>(len_V);
int r = cuda_betweenness_centrality(V.data(), E.data(), W.data(),
sources.data(), len_V, len_E, sources.size(),
warp_size, is_directed, normalized, endpoints, BC.data());
return r;
}
} // namespace gpu_easygraph
@@ -0,0 +1,246 @@
#include <cuda.h>
#include <cuda_runtime.h>
#include <stdlib.h>
#include "common.h"
namespace gpu_easygraph {
static __device__ double atomicAddDouble (
_OUT_ double* address,
_IN_ double val
)
{
unsigned long long int* address_as_ull =
(unsigned long long int*)address;
unsigned long long int old = *address_as_ull, assumed;
do {
assumed = old;
old = atomicCAS(address_as_ull, assumed,
__double_as_longlong(val +
__longlong_as_double(assumed)));
} while (assumed != old);
return __longlong_as_double(old);
}
static __device__ double atomicMinDouble (
_OUT_ double *address,
_IN_ double val
)
{
unsigned long long ret = __double_as_longlong(*address);
while (val < __longlong_as_double(ret))
{
unsigned long long old = ret;
if ((ret = atomicCAS((unsigned long long *)address, old, __double_as_longlong(val))) == old)
break;
}
return __longlong_as_double(ret);
}
static __global__ void d_calc_min_edge (
_IN_ int* d_V,
_IN_ int* d_E,
_IN_ double* d_W,
_IN_ int len_V,
_IN_ int len_E,
_OUT_ double* d_min_edge
)
{
int tid = blockIdx.x * blockDim.x + threadIdx.x;
int tnum = blockDim.x * gridDim.x;
for (int u = tid; u < len_V; u += tnum) {
double curr_min = EG_DOUBLE_INF;
int edge_start = d_V[u];
int edge_end = d_V[u + 1];
for(int v = edge_start; v < edge_end; ++v) {
curr_min = min(curr_min, d_W[v]);
}
d_min_edge[u] = curr_min;
}
}
static __global__ void d_dijkstra_cc (
_IN_ int* d_V,
_IN_ int* d_E,
_IN_ double* d_W,
_IN_ double* d_min_edge,
_IN_ int* d_sources,
_BUFFER_ double* d_dist_2D,
_BUFFER_ int* d_U_2D,
_BUFFER_ int* d_F_2D,
_IN_ int len_V,
_IN_ int len_E,
_IN_ int len_sources,
_IN_ int warp_size,
_OUT_ double* d_CC
)
{
for (int s_idx = blockIdx.x; s_idx < len_sources; s_idx += gridDim.x) {
int s = d_sources[s_idx];
int* d_U = d_U_2D + blockIdx.x * len_V;
int* d_F = d_F_2D + blockIdx.x * len_V;
double* d_dist = d_dist_2D + blockIdx.x * len_V;
__shared__ int len_F;
__shared__ double delta;
__shared__ double dist_accum;
__shared__ int reachable_cnt;
for (int i = threadIdx.x; i < len_V; i += blockDim.x) {
d_U[i] = 1;
d_dist[i] = EG_DOUBLE_INF;
}
__syncthreads();
if (threadIdx.x == 0) {
d_dist[s] = 0.0;
d_F[0] = s;
len_F = 1;
delta = 0.0;
dist_accum = 0.0;
reachable_cnt = 0;
}
__syncthreads();
while (delta < EG_DOUBLE_INF) {
for (int j = threadIdx.x; j < len_F * warp_size; j += blockDim.x) {
int f = d_F[j / warp_size];
int edge_start = d_V[f];
int edge_end = d_V[f + 1];
double dist = d_dist[f];
for (int e = j % warp_size; e < edge_end - edge_start; e += warp_size) {
int adj = d_E[e + edge_start];
double relax_w = dist + d_W[e + edge_start];
atomicMinDouble(d_dist + adj, relax_w);
}
__threadfence_block();
}
__syncthreads();
if (threadIdx.x == 0) {
delta = EG_DOUBLE_INF;
}
__syncthreads();
for (int i = threadIdx.x; i < len_V; i += blockDim.x) {
double dist_i = d_dist[i];
if (d_U[i] == 1 && dist_i < EG_DOUBLE_INF) {
atomicMinDouble(&delta, dist_i + d_min_edge[i]);
}
}
__syncthreads();
if (threadIdx.x == 0) {
len_F = 0;
}
__syncthreads();
for (int i = threadIdx.x; i < len_V; i += blockDim.x) {
double dist_i = d_dist[i];
if (d_U[i] && dist_i <= delta && dist_i < EG_DOUBLE_INF) {
d_U[i] = 0;
int f_idx = atomicAdd(&len_F, 1);
d_F[f_idx] = i;
atomicAdd(&reachable_cnt, 1);
atomicAddDouble(&dist_accum, d_dist[i]);
}
}
__syncthreads();
}
if (threadIdx.x == 0) {
d_CC[s_idx] = dist_accum == 0.0 ? 0.0 :
(double)(reachable_cnt - 1) *
(double)(reachable_cnt - 1) /
((len_V - 1) * dist_accum);
}
__syncthreads();
}
}
// we here use CSR to represent a graph
int cuda_closeness_centrality (
_IN_ const int* V,
_IN_ const int* E,
_IN_ const double* W,
_IN_ const int* sources,
_IN_ int len_V,
_IN_ int len_E,
_IN_ int len_sources,
_IN_ int warp_size,
_OUT_ double* CC
)
{
int cuda_ret = cudaSuccess;
int EG_ret = EG_GPU_SUCC;
int min_edge_block_size;
int min_edge_grid_size;
int dijkstra_block_size;
int dijkstra_grid_size;
cudaOccupancyMaxPotentialBlockSize(&min_edge_grid_size, &min_edge_block_size, d_calc_min_edge, 0, 0);
cudaOccupancyMaxPotentialBlockSize(&dijkstra_grid_size, &dijkstra_block_size, d_dijkstra_cc, 0, 0);
int *d_V = NULL, *d_E = NULL, *d_sources= NULL;
int *d_U_2D = NULL, *d_F_2D = NULL;
double *d_W = NULL, *d_min_edge = NULL, *d_dist_2D = NULL, *d_CC = NULL;
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_V, sizeof(int) * (len_V + 1)));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_E, sizeof(int) * len_E));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_sources, sizeof(int) * len_sources));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_U_2D, sizeof(int) * dijkstra_grid_size * len_V));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_F_2D, sizeof(int) * dijkstra_grid_size * len_V));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_W, sizeof(double) * len_E));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_min_edge, sizeof(double) * len_V));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_dist_2D, sizeof(double) * dijkstra_grid_size * len_V));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_CC, sizeof(double) * len_V));
EXIT_IF_CUDA_FAILED(cudaMemcpy(d_V, V, sizeof(int) * (len_V + 1), cudaMemcpyHostToDevice));
EXIT_IF_CUDA_FAILED(cudaMemcpy(d_E, E, sizeof(int) * len_E, cudaMemcpyHostToDevice));
EXIT_IF_CUDA_FAILED(cudaMemcpy(d_sources, sources, sizeof(int) * len_sources, cudaMemcpyHostToDevice));
EXIT_IF_CUDA_FAILED(cudaMemcpy(d_W, W, sizeof(double) * len_E, cudaMemcpyHostToDevice));
d_calc_min_edge<<<dijkstra_grid_size, dijkstra_block_size>>>(d_V, d_E, d_W, len_V, len_E, d_min_edge);
d_dijkstra_cc<<<min_edge_grid_size, min_edge_block_size>>>(d_V, d_E, d_W, d_min_edge, d_sources,
d_dist_2D, d_U_2D, d_F_2D, len_V, len_E, len_sources, warp_size, d_CC);
EXIT_IF_CUDA_FAILED(cudaMemcpy(CC, d_CC, sizeof(double) * len_V, cudaMemcpyDeviceToHost));
exit:
cudaFree(d_V);
cudaFree(d_E);
cudaFree(d_sources);
cudaFree(d_U_2D);
cudaFree(d_F_2D);
cudaFree(d_W);
cudaFree(d_min_edge);
cudaFree(d_dist_2D);
cudaFree(d_CC);
if (cuda_ret != cudaSuccess) {
switch (cuda_ret) {
case cudaErrorMemoryAllocation:
EG_ret = EG_GPU_FAILED_TO_ALLOCATE_DEVICE_MEM;
break;
default:
EG_ret = EG_GPU_DEVICE_ERR;
break;
}
}
return EG_ret;
}
} // namespace gpu_easygraph
@@ -0,0 +1,53 @@
#pragma once
#include "common.h"
namespace gpu_easygraph {
/**
* description:
* use cuda to calculate closeness_centrality. the graph must be
* in CSR format.
*
* arguments:
* V -
* the vertices in CSR format
*
* E -
* the edges in CSR format
*
* W -
* the weight of edges in CSR format
*
* sources -
* an array of EG_GPU_NODE_STATUS. the according CC[i] will be
* calculated only if sources[i] == EG_GPU_NODE_ACTIVE
*
* len_V -
* len of V
*
* len_E -
* len of E
*
* warp_size -
* the number of threads assigned to a vertex
*
* CC -
* closeness centrality output
*
* return:
* EG_GPU_STATUS_CODE
*/
int cuda_closeness_centrality (
_IN_ const int* V,
_IN_ const int* E,
_IN_ const double* W,
_IN_ const int* sources,
_IN_ int len_V,
_IN_ int len_E,
_IN_ int len_sources,
_IN_ int warp_size,
_OUT_ double* CC
);
} // namespace gpu_easygraph
+25
View File
@@ -0,0 +1,25 @@
#include <string>
#include <vector>
#include "core/k_core.cuh"
#include "common.h"
namespace gpu_easygraph {
using std::vector;
int k_core(
_IN_ const std::vector<int>& V,
_IN_ const std::vector<int>& E,
_OUT_ std::vector<int>& KC
) {
int len_V = V.size() - 1;
int len_E = E.size();
KC = vector<int>(len_V, 0);
int r = cuda_k_core(V.data(), E.data(), len_V, len_E, KC.data());
return r;
}
} // namespace gpu_easygraph
+239
View File
@@ -0,0 +1,239 @@
#include <cuda.h>
#include <cuda_runtime.h>
#include <stdlib.h>
#include "common.h"
namespace gpu_easygraph {
static __global__ void d_calc_deg(
_IN_ int* d_V,
_IN_ int* d_E,
_IN_ int len_V,
_IN_ int len_E,
_OUT_ int* d_deg
)
{
int tid = blockIdx.x * blockDim.x + threadIdx.x;
int tnum = blockDim.x * gridDim.x;
for (int u = tid; u < len_V; u += tnum) {
d_deg[u] = d_V[u + 1] - d_V[u];
}
}
static __global__ void d_k_core_scan(
_IN_ int* d_deg,
_IN_ int len_V,
_IN_ int level,
_IN_ int* d_buf_2D,
_IN_ int* d_buf_tail_2D
)
{
int tid = blockIdx.x * blockDim.x + threadIdx.x;
int threads_num = blockDim.x * gridDim.x;
int* d_buf = d_buf_2D + blockIdx.x * len_V;
__shared__ int buf_tail;
if (threadIdx.x == 0) {
buf_tail = 0;
}
__syncthreads();
for (int base = 0; base < len_V; base += threads_num) {
int v = base + tid;
if (v >= len_V) {
continue;
}
if (d_deg[v] == level) {
int buf_idx = atomicAdd(&buf_tail, 1);
d_buf[buf_idx] = v;
}
}
__syncthreads();
if (threadIdx.x == 0) {
d_buf_tail_2D[blockIdx.x] = buf_tail;
}
}
static __global__ void d_k_core_loop(
_IN_ int* d_V,
_IN_ int* d_E,
_OUT_ int* d_deg,
_IN_ int len_V,
_IN_ int len_E,
_IN_ int level,
_IN_ int* d_buf_2D,
_IN_ int* d_buf_tail_2D,
_OUT_ int* d_count
)
{
int warp_size = 32;
int tid = threadIdx.x;
int* d_buf = d_buf_2D + blockIdx.x * len_V;
int warp_id = tid / warp_size;
int lane_id = tid % warp_size;
int reg_tail;
int i;
__shared__ int buf_tail;
__shared__ int base;
if (threadIdx.x == 0) {
base = 0;
buf_tail = d_buf_tail_2D[blockIdx.x];
}
__syncthreads();
while (1) {
__syncthreads();
if (base == buf_tail) {
break;
}
i = base + warp_id;
reg_tail = buf_tail;
__syncthreads();
if (i >= reg_tail) {
continue;
}
if (threadIdx.x == 0) {
base += blockDim.x / warp_size;
if (reg_tail < base) {
base = reg_tail;
}
}
int v = d_buf[i];
int edge_start = d_V[v];
int edge_end = d_V[v + 1];
while (1) {
__syncwarp();
if (edge_start >= edge_end) {
break;
}
int curr_e = edge_start + lane_id;
edge_start += warp_size;
if (curr_e >= edge_end) {
continue;
}
int nbr = d_E[curr_e];
if (d_deg[nbr] > level) {
int a = atomicSub(d_deg + nbr, 1);
if (a == level + 1) {
int loc = atomicAdd(&buf_tail, 1);
d_buf[loc] = nbr;
}
if (a <= level) {
atomicAdd(d_deg + nbr, 1);
}
}
}
}
if (threadIdx.x == 0 && buf_tail) {
atomicAdd(d_count, buf_tail);
}
}
int cuda_k_core (
_IN_ const int* V,
_IN_ const int* E,
_IN_ int len_V,
_IN_ int len_E,
_OUT_ int* k_core_res
)
{
int cuda_ret = cudaSuccess;
int EG_ret = EG_GPU_SUCC;
int calc_deg_block_size;
int calc_deg_grid_size;
int scan_block_size;
int scan_grid_size;
int loop_block_size;
int loop_grid_size;
cudaOccupancyMaxPotentialBlockSize(&calc_deg_grid_size, &calc_deg_block_size, d_calc_deg, 0, 0);
cudaOccupancyMaxPotentialBlockSize(&scan_grid_size, &scan_block_size, d_k_core_scan, 0, 0);
cudaOccupancyMaxPotentialBlockSize(&loop_grid_size, &loop_block_size, d_k_core_loop, 0, 0);
int k_core_grid_size = max(scan_grid_size, loop_grid_size);
int count = 0, level = 0;
int *d_V = NULL, *d_E = NULL, *d_deg = NULL, *d_k_core_res = NULL,
*d_buf_2D = NULL, *d_buf_tail_2D = NULL, *d_count = NULL;
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_V, sizeof(int) * (len_V + 1)));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_E, sizeof(int) * len_E));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_deg, sizeof(int) * len_V));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_k_core_res, sizeof(int) * len_V));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_buf_2D, sizeof(int) * k_core_grid_size * len_V));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_buf_tail_2D, sizeof(int) * k_core_grid_size));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_count, sizeof(int)));
EXIT_IF_CUDA_FAILED(cudaMemcpy(d_V, V, sizeof(int) * (len_V + 1), cudaMemcpyHostToDevice));
EXIT_IF_CUDA_FAILED(cudaMemcpy(d_E, E, sizeof(int) * len_E, cudaMemcpyHostToDevice));
EXIT_IF_CUDA_FAILED(cudaMemset(d_count, 0, sizeof(int)));
d_calc_deg<<<calc_deg_grid_size, calc_deg_block_size>>>(d_V, d_E, len_V, len_E, d_deg);
while (count < len_V) {
EXIT_IF_CUDA_FAILED(cudaMemset(d_buf_tail_2D, 0, sizeof(int) * k_core_grid_size));
d_k_core_scan<<<k_core_grid_size, scan_block_size>>>(d_deg, len_V, level, d_buf_2D, d_buf_tail_2D);
d_k_core_loop<<<k_core_grid_size, loop_block_size>>>(d_V, d_E, d_deg, len_V, len_E, level,
d_buf_2D, d_buf_tail_2D, d_count);
EXIT_IF_CUDA_FAILED(cudaMemcpy(&count, d_count, sizeof(int), cudaMemcpyDeviceToHost));
++level;
}
EXIT_IF_CUDA_FAILED(cudaMemcpy(k_core_res, d_deg, sizeof(int) * len_V, cudaMemcpyDeviceToHost));
exit:
cudaFree(d_V);
cudaFree(d_E);
cudaFree(d_deg);
cudaFree(d_k_core_res);
cudaFree(d_buf_2D);
cudaFree(d_buf_tail_2D);
cudaFree(d_count);
if (cuda_ret != cudaSuccess) {
switch (cuda_ret) {
case cudaErrorMemoryAllocation:
EG_ret = EG_GPU_FAILED_TO_ALLOCATE_DEVICE_MEM;
break;
default:
EG_ret = EG_GPU_DEVICE_ERR;
break;
}
}
return EG_ret;
}
} // namespace gpu_easygraph
+39
View File
@@ -0,0 +1,39 @@
# pragma once
#include "common.h"
namespace gpu_easygraph {
/**
* description:
* use cuda to calculate k core. the graph must be
* in CSR format.
*
* arguments:
* V -
* the vertices in CSR format
*
* E -
* the edges in CSR format
*
* len_V -
* len of V
*
* len_E -
* len of E
*
* k_core_res -
* result of k_core
*
* return:
* EG_GPU_STATUS_CODE
*/
int cuda_k_core (
_IN_ const int* V,
_IN_ const int* E,
_IN_ int len_V,
_IN_ int len_E,
_OUT_ int* k_core_res
);
} // namespace gpu_easygraph
+63
View File
@@ -0,0 +1,63 @@
#include <limits>
#include <vector>
#include "path/sssp_dijkstra.cuh"
#include "common.h"
namespace gpu_easygraph {
using std::vector;
static int decide_warp_size(
_IN_ int len_V,
_IN_ int len_E
)
{
vector<int> warp_size_cand{1, 2, 4, 8, 16, 32};
if (len_E / len_V < warp_size_cand.front()) {
return warp_size_cand.front();
}
for (int i = 0; i + 1 < warp_size_cand.size(); ++i) {
if (warp_size_cand[i] <= len_E / len_V
&& len_E / len_V < warp_size_cand[i + 1]) {
return warp_size_cand[i + 1];
}
}
return warp_size_cand.back();
}
int sssp_dijkstra(
_IN_ const vector<int>& V,
_IN_ const vector<int>& E,
_IN_ const vector<double>& W,
_IN_ const vector<int>& sources,
_IN_ int target,
_OUT_ vector<double>& res
)
{
int len_V = V.size() - 1;
int len_E = E.size();
int warp_size = decide_warp_size(len_V, len_E);
res = vector<double>(sources.size() * V.size());
int r = cuda_sssp_dijkstra(V.data(), E.data(), W.data(),
sources.data(), len_V, len_E, sources.size(),
target, warp_size, res.data());
double double_inf = std::numeric_limits<double>::infinity();
for (int i = 0; i < res.size(); ++i) {
if (res[i] >= EG_DOUBLE_INF) {
res[i] = double_inf;
}
}
return r;
}
} // namespace gpu_easygraph
@@ -0,0 +1,233 @@
#include <cuda.h>
#include <cuda_runtime.h>
#include <stdlib.h>
#include "common.h"
namespace gpu_easygraph {
static __device__ double atomicMinDouble (
_OUT_ double *address,
_IN_ double val
)
{
unsigned long long ret = __double_as_longlong(*address);
while (val < __longlong_as_double(ret))
{
unsigned long long old = ret;
if ((ret = atomicCAS((unsigned long long *)address, old, __double_as_longlong(val))) == old)
break;
}
return __longlong_as_double(ret);
}
static __global__ void d_calc_min_edge (
_IN_ int* d_V,
_IN_ int* d_E,
_IN_ double* d_W,
_IN_ int len_V,
_IN_ int len_E,
_OUT_ double* d_min_edge
)
{
int tid = blockIdx.x * blockDim.x + threadIdx.x;
int tnum = blockDim.x * gridDim.x;
for (int u = tid; u < len_V; u += tnum) {
double curr_min = EG_DOUBLE_INF;
int edge_start = d_V[u];
int edge_end = d_V[u + 1];
for(int v = edge_start; v < edge_end; ++v) {
curr_min = min(curr_min, d_W[v]);
}
d_min_edge[u] = curr_min;
}
}
static __global__ void d_sssp_dijkstra (
_IN_ int* d_curr_node,
_IN_ int* d_V,
_IN_ int* d_E,
_IN_ double* d_W,
_IN_ double* d_min_edge,
_IN_ int* d_sources,
_OUT_ double* d_dist_2D,
_BUFFER_ int* d_U_2D,
_BUFFER_ int* d_F_2D,
_IN_ int len_V,
_IN_ int len_E,
_IN_ int len_sources,
_IN_ int target,
_IN_ int warp_size
)
{
while (1) {
__shared__ int curr_node;
if (threadIdx.x == 0) {
curr_node = atomicAdd(d_curr_node, 1);
}
__syncthreads();
if (curr_node >= len_sources) {
break;
}
int s = d_sources[curr_node];
double* d_dist = d_dist_2D + curr_node * len_V;
int* d_U = d_U_2D + blockIdx.x * len_V;
int* d_F = d_F_2D + blockIdx.x * len_V;
__shared__ int len_F;
__shared__ double delta;
__shared__ int target_cnt;
for (int i = threadIdx.x; i < len_V; i += blockDim.x) {
d_U[i] = 1;
d_dist[i] = EG_DOUBLE_INF;
}
__syncthreads();
if (threadIdx.x == 0) {
d_dist[s] = 0.0;
d_F[0] = s;
len_F = 1;
delta = 0.0;
target_cnt = 0;
}
__syncthreads();
while (delta < EG_DOUBLE_INF && target_cnt == 0) {
for (int j = threadIdx.x; j < len_F * warp_size; j += blockDim.x) {
int f = d_F[j / warp_size];
int edge_start = d_V[f];
int edge_end = d_V[f + 1];
double dist = d_dist[f];
for (int e = j % warp_size; e < edge_end - edge_start; e += warp_size) {
int adj = d_E[e + edge_start];
double relax_w = dist + d_W[e + edge_start];
atomicMinDouble(d_dist + adj, relax_w);
}
__threadfence_block();
}
__syncthreads();
if (threadIdx.x == 0) {
delta = EG_DOUBLE_INF;
}
__syncthreads();
for (int i = threadIdx.x; i < len_V; i += blockDim.x) {
double dist_i = d_dist[i];
if (d_U[i] == 1 && dist_i < EG_DOUBLE_INF) {
atomicMinDouble(&delta, dist_i + d_min_edge[i]);
}
}
__syncthreads();
if (threadIdx.x == 0) {
len_F = 0;
}
__syncthreads();
for (int i = threadIdx.x; i < len_V; i += blockDim.x) {
double dist_i = d_dist[i];
if (d_U[i] && dist_i <= delta && dist_i < EG_DOUBLE_INF) {
d_U[i] = 0;
int f_idx = atomicAdd(&len_F, 1);
d_F[f_idx] = i;
target_cnt += i == target;
}
}
__syncthreads();
}
__syncthreads();
}
}
// we here use CSR to represent a graph
int cuda_sssp_dijkstra(
_IN_ const int* V,
_IN_ const int* E,
_IN_ const double* W,
_IN_ const int* sources,
_IN_ int len_V,
_IN_ int len_E,
_IN_ int len_sources,
_IN_ int target,
_IN_ int warp_size,
_OUT_ double* res
)
{
int cuda_ret = cudaSuccess;
int EG_ret = EG_GPU_SUCC;
int min_edge_block_size;
int min_edge_grid_size;
int dijkstra_block_size;
int dijkstra_grid_size;
cudaOccupancyMaxPotentialBlockSize(&min_edge_grid_size, &min_edge_block_size, d_calc_min_edge, 0, 0);
cudaOccupancyMaxPotentialBlockSize(&dijkstra_grid_size, &dijkstra_block_size, d_sssp_dijkstra, 0, 0);
int *d_curr_node = NULL;
int *d_V = NULL, *d_E = NULL, *d_sources= NULL;
int *d_U_2D = NULL, *d_F_2D = NULL;
double *d_W = NULL, *d_min_edge = NULL, *d_dist_2D = NULL;
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_curr_node, sizeof(int)));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_V, sizeof(int) * (len_V + 1)));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_E, sizeof(int) * len_E));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_sources, sizeof(int) * len_sources));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_U_2D, sizeof(int) * dijkstra_grid_size * len_V));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_F_2D, sizeof(int) * dijkstra_grid_size * len_V));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_W, sizeof(double) * len_E));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_min_edge, sizeof(double) * len_V));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_dist_2D, sizeof(double) * len_sources * len_V));
EXIT_IF_CUDA_FAILED(cudaMemset(d_curr_node, 0, sizeof(int)));
EXIT_IF_CUDA_FAILED(cudaMemcpy(d_V, V, sizeof(int) * (len_V + 1), cudaMemcpyHostToDevice));
EXIT_IF_CUDA_FAILED(cudaMemcpy(d_E, E, sizeof(int) * len_E, cudaMemcpyHostToDevice));
EXIT_IF_CUDA_FAILED(cudaMemcpy(d_sources, sources, sizeof(int) * len_sources, cudaMemcpyHostToDevice));
EXIT_IF_CUDA_FAILED(cudaMemcpy(d_W, W, sizeof(double) * len_E, cudaMemcpyHostToDevice));
d_calc_min_edge<<<dijkstra_grid_size, dijkstra_block_size>>>(d_V, d_E, d_W, len_V, len_E, d_min_edge);
d_sssp_dijkstra<<<min_edge_grid_size, min_edge_block_size>>>(d_curr_node ,d_V, d_E, d_W, d_min_edge, d_sources,
d_dist_2D, d_U_2D, d_F_2D, len_V, len_E, len_sources, target, warp_size);
EXIT_IF_CUDA_FAILED(cudaMemcpy(res, d_dist_2D, sizeof(double) * len_sources * len_V, cudaMemcpyDeviceToHost));
exit:
cudaFree(d_curr_node);
cudaFree(d_V);
cudaFree(d_E);
cudaFree(d_sources);
cudaFree(d_U_2D);
cudaFree(d_F_2D);
cudaFree(d_W);
cudaFree(d_min_edge);
cudaFree(d_dist_2D);
if (cuda_ret != cudaSuccess) {
switch (cuda_ret) {
case cudaErrorMemoryAllocation:
EG_ret = EG_GPU_FAILED_TO_ALLOCATE_DEVICE_MEM;
break;
default:
EG_ret = EG_GPU_DEVICE_ERR;
break;
}
}
return EG_ret;
}
} // namespace gpu_easygraph
@@ -0,0 +1,20 @@
#pragma once
#include "common.h"
namespace gpu_easygraph {
int cuda_sssp_dijkstra(
_IN_ const int* V,
_IN_ const int* E,
_IN_ const double* W,
_IN_ const int* sources,
_IN_ int len_V,
_IN_ int len_E,
_IN_ int len_sources,
_IN_ int target,
_IN_ int warp_size,
_OUT_ double* res
);
} // namespace gpu_easygraph
@@ -0,0 +1,31 @@
#include <string>
#include <vector>
#include <memory>
#include "structural_holes/constraint.cuh"
#include "common.h"
namespace gpu_easygraph {
using std::vector;
int constraint(
_IN_ const vector<int>& V,
_IN_ const vector<int>& E,
_IN_ const vector<int>& row,
_IN_ const vector<int>& col,
_IN_ int num_nodes,
_IN_ const vector<double>& W,
_IN_ bool is_directed,
_IN_ vector<int>& node_mask,
_OUT_ vector<double>& constraint
) {
int num_edges = row.size();
constraint = vector<double>(num_nodes);
int r = cuda_constraint(V.data(), E.data(), row.data(), col.data(), W.data(), num_nodes, num_edges, is_directed, node_mask.data(), constraint.data());
return r;
}
} // namespace gpu_easygraph
@@ -0,0 +1,341 @@
#include <cuda.h>
#include <cuda_runtime.h>
#include <stdlib.h>
#include "common.h"
#define NODES_PER_BLOCK 1
namespace gpu_easygraph {
enum norm_t { SUM = 0, MAX = 1 };
static __device__ double mutual_weight(
const int* V,
const int* E,
const double* W,
int u,
int v
) {
double a_uv = 0.0;
for (int i = V[u]; i < V[u+1]; i++) {
if (E[i] == v) {
a_uv = W[i];
break;
}
}
return a_uv;
}
static __device__ double normalized_mutual_weight(
const int* V,
const int* E,
const double* W,
int u,
int v,
norm_t norm
) {
double weight_uv = mutual_weight(V, E, W, u, v);
double scale = 0.0;
if (norm == SUM) {
for (int i = V[u]; i < V[u+1]; i++) {
int neighbor = E[i];
double weight_uw = mutual_weight(V, E, W, u, neighbor);
scale += weight_uw;
}
} else if (norm == MAX) {
for (int i = V[u]; i < V[u+1]; i++) {
int neighbor = E[i];
double weight_uw = mutual_weight(V, E, W, u, neighbor);
scale = fmax(scale,weight_uw);
}
}
return (scale==0.0) ? 0.0 : (weight_uv / scale);
}
static __device__ double local_constraint(
const int* V,
const int* E,
const double* W,
int u,
int v
) {
double direct = normalized_mutual_weight(V,E,W,u,v,SUM);
double indirect = 0.0;
for (int i = V[u]; i < V[u+1]; i++) {
int neighbor = E[i];
double norm_uw = normalized_mutual_weight(V, E, W, u, neighbor,SUM);
double norm_wv = normalized_mutual_weight(V, E, W, neighbor, v,SUM);
indirect += norm_uw * norm_wv;
}
double local_constraint_of_uv = (direct + indirect) * (direct + indirect);
return local_constraint_of_uv;
}
__global__ void calculate_constraints(
const int* __restrict__ V,
const int* __restrict__ E,
const double* __restrict__ W,
const int num_nodes,
const int* __restrict__ node_mask,
double* __restrict__ constraint_results
) {
int start_node = blockIdx.x * NODES_PER_BLOCK;
int end_node = min(start_node + NODES_PER_BLOCK, num_nodes);
for (int v = start_node; v < end_node; ++v) {
if (!node_mask[v]) continue;
double constraint_of_v = 0.0;
bool is_nan = true;
__shared__ double shared_constraint[256];
double local_sum = 0.0;
for (int i = V[v] + threadIdx.x; i < V[v + 1]; i += blockDim.x) {
is_nan = false;
int neighbor = E[i];
local_sum += local_constraint(V, E, W, v, neighbor);
}
shared_constraint[threadIdx.x] = local_sum;
__syncthreads();
for (int offset = blockDim.x / 2; offset > 0; offset /= 2) {
if (threadIdx.x < offset) {
shared_constraint[threadIdx.x] += shared_constraint[threadIdx.x + offset];
}
__syncthreads();
}
if (threadIdx.x == 0) {
constraint_results[v] = (is_nan) ? NAN : shared_constraint[0];
}
}
}
static __device__ double directed_mutual_weight(
const int* V,
const int* E,
const double* W,
int u,
int v
) {
double a_uv = 0.0, a_vu = 0.0;
for (int i = V[u]; i < V[u+1]; i++) {
if (E[i] == v) {
a_uv = W[i];
break;
}
}
for (int i = V[v]; i < V[v+1]; i++) {
if (E[i] == u) {
a_vu = W[i];
break;
}
}
return a_uv + a_vu;
}
static __device__ double directed_normalized_mutual_weight(
const int* V,
const int* E,
const int* row,
const int* col,
const double* W,
int num_edges,
int u,
int v,
norm_t norm
) {
double weight_uv = directed_mutual_weight(V, E, W, u, v);
double scale = 0.0;
if(norm==SUM){
for (int i = V[u]; i < V[u+1]; i++) {
int neighbor = E[i];
double weight_uw = directed_mutual_weight(V, E, W, u, neighbor);
scale += weight_uw;
}
for (int i = 0; i < num_edges; i++) {
if (col[i] == u) {
int neighbor = row[i];
double weight_wu = directed_mutual_weight(V, E, W, u, neighbor);
scale += weight_wu;
}
}
}else if(norm==MAX){
for (int i = V[u]; i < V[u+1]; i++) {
int neighbor = E[i];
double weight_uw = directed_mutual_weight(V, E, W, u, neighbor);
scale = fmax(scale,weight_uw);
}
for (int i = 0; i < num_edges; i++) {
if (col[i] == u) {
int neighbor = row[i];
double weight_wu = directed_mutual_weight(V, E, W, u, neighbor);
scale = fmax(scale,weight_wu);
}
}
}
return (scale==0.0) ? 0.0 : (weight_uv / scale);
}
static __device__ double directed_local_constraint(
const int* V,
const int* E,
const int* row,
const int* col,
const double* W,
int num_edges,
int u,
int v
) {
double direct = directed_normalized_mutual_weight(V,E,row,col,W,num_edges,u,v,SUM);
double indirect = 0.0;
for (int i = V[u]; i < V[u+1]; i++) {
int neighbor = E[i];
double norm_uw = directed_normalized_mutual_weight(V, E, row, col, W, num_edges, u, neighbor,SUM);
double norm_wv = directed_normalized_mutual_weight(V, E, row, col, W, num_edges, neighbor, v,SUM);
indirect += norm_uw * norm_wv;
}
for (int i = 0; i < num_edges; i++) {
if (col[i] == u) {
int neighbor = row[i];
double norm_uw = directed_normalized_mutual_weight(V, E, row, col, W, num_edges, u, neighbor,SUM);
double norm_wv = directed_normalized_mutual_weight(V, E, row, col, W, num_edges, neighbor, v,SUM);
indirect += norm_uw * norm_wv;
}
}
double local_constraint_of_uv = (direct + indirect) * (direct + indirect);
return local_constraint_of_uv;
}
__global__ void directed_calculate_constraints(
const int* V,
const int* E,
const int* row,
const int* col,
const double* W,
int num_nodes,
int num_edges,
int* node_mask,
double* constraint_results
) {
int start_node = blockIdx.x * NODES_PER_BLOCK;
int end_node = min(start_node + NODES_PER_BLOCK, num_nodes);
for (int v = start_node; v < end_node; ++v) {
if (!node_mask[v]) continue;
double constraint_of_v = 0.0;
bool is_nan = true;
__shared__ double shared_constraint[256];
double local_sum = 0.0;
for (int i = V[v] + threadIdx.x; i < V[v + 1]; i += blockDim.x) {
is_nan = false;
int neighbor = E[i];
local_sum += directed_local_constraint(V, E, row, col, W, num_edges, v, neighbor);
}
for (int i = threadIdx.x; i < num_edges; i += blockDim.x) {
if (col[i] == v) {
// is_nan = false;
int neighbor = row[i];
local_sum += directed_local_constraint(V, E, row, col, W, num_edges, v, neighbor);
}
}
shared_constraint[threadIdx.x] = local_sum;
__syncthreads();
for (int offset = blockDim.x / 2; offset > 0; offset /= 2) {
if (threadIdx.x < offset) {
shared_constraint[threadIdx.x] += shared_constraint[threadIdx.x + offset];
}
__syncthreads();
}
if (threadIdx.x == 0) {
constraint_results[v] = (is_nan) ? NAN : shared_constraint[0];
}
}
}
int cuda_constraint(
_IN_ const int* V,
_IN_ const int* E,
_IN_ const int* row,
_IN_ const int* col,
_IN_ const double* W,
_IN_ int num_nodes,
_IN_ int num_edges,
_IN_ bool is_directed,
_IN_ int* node_mask,
_OUT_ double* constraint_results
) {
int cuda_ret = cudaSuccess;
int EG_ret = EG_GPU_SUCC;
int* d_V;
int* d_E;
int* d_row;
int* d_col;
double* d_W;
int* d_node_mask;
double* d_constraint_results;
int block_size = 256;
int grid_size = (num_nodes + NODES_PER_BLOCK - 1) / NODES_PER_BLOCK;
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_V, (num_nodes+1) * sizeof(int)));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_E, num_edges * sizeof(int)));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_row, num_edges * sizeof(int)));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_col, num_edges * sizeof(int)));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_W, num_edges * sizeof(double)));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_node_mask, num_nodes * sizeof(int)));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_constraint_results, num_nodes * sizeof(double)));
EXIT_IF_CUDA_FAILED(cudaMemcpy(d_V, V, (num_nodes+1) * sizeof(int), cudaMemcpyHostToDevice));
EXIT_IF_CUDA_FAILED(cudaMemcpy(d_E, E, num_edges * sizeof(int), cudaMemcpyHostToDevice));
EXIT_IF_CUDA_FAILED(cudaMemcpy(d_row, row, num_edges * sizeof(int), cudaMemcpyHostToDevice));
EXIT_IF_CUDA_FAILED(cudaMemcpy(d_col, col, num_edges * sizeof(int), cudaMemcpyHostToDevice));
EXIT_IF_CUDA_FAILED(cudaMemcpy(d_node_mask, node_mask, num_nodes * sizeof(int), cudaMemcpyHostToDevice));
EXIT_IF_CUDA_FAILED(cudaMemcpy(d_W, W, num_edges * sizeof(double), cudaMemcpyHostToDevice));
if(is_directed){
directed_calculate_constraints<<<grid_size, block_size>>>(d_V, d_E, d_row, d_col, d_W, num_nodes, num_edges, d_node_mask, d_constraint_results);
}else{
calculate_constraints<<<grid_size, block_size>>>(d_V, d_E, d_W, num_nodes, d_node_mask, d_constraint_results);
}
EXIT_IF_CUDA_FAILED(cudaMemcpy(constraint_results, d_constraint_results, num_nodes * sizeof(double), cudaMemcpyDeviceToHost));
exit:
cudaFree(d_V);
cudaFree(d_E);
cudaFree(d_row);
cudaFree(d_col);
cudaFree(d_W);
cudaFree(d_node_mask);
cudaFree(d_constraint_results);
if (cuda_ret != cudaSuccess) {
switch (cuda_ret) {
case cudaErrorMemoryAllocation:
EG_ret = EG_GPU_FAILED_TO_ALLOCATE_DEVICE_MEM;
break;
default:
EG_ret = EG_GPU_DEVICE_ERR;
break;
}
}
return EG_ret;
}
} // namespace gpu_easygraph
@@ -0,0 +1,20 @@
#pragma once
#include "common.h"
namespace gpu_easygraph {
int cuda_constraint(
_IN_ const int* V,
_IN_ const int* E,
_IN_ const int* row,
_IN_ const int* col,
_IN_ const double* W,
_IN_ int num_nodes,
_IN_ int num_edges,
_IN_ bool is_directed,
_IN_ int* node_mask,
_OUT_ double* constraint_results
);
} // namespace gpu_easygraph
@@ -0,0 +1,31 @@
#include <string>
#include <vector>
#include <memory>
#include "structural_holes/effective_size.cuh"
#include "common.h"
namespace gpu_easygraph {
using std::vector;
int effective_size(
_IN_ const vector<int>& V,
_IN_ const vector<int>& E,
_IN_ const vector<int>& row,
_IN_ const vector<int>& col,
_IN_ int num_nodes,
_IN_ const vector<double>& W,
_IN_ bool is_directed,
_IN_ vector<int>& node_mask,
_OUT_ vector<double>& effective_size
) {
int num_edges = row.size();
effective_size = vector<double>(num_nodes);
int r = cuda_effective_size(V.data(), E.data(), row.data(), col.data(), W.data(), num_nodes, num_edges, is_directed, node_mask.data(), effective_size.data());
return r;
}
} // namespace gpu_easygraph
@@ -0,0 +1,346 @@
#include <cuda.h>
#include <cuda_runtime.h>
#include <stdlib.h>
#include "common.h"
#define NODES_PER_BLOCK 1
namespace gpu_easygraph {
enum norm_t { SUM = 0, MAX = 1 };
static __device__ double mutual_weight(
const int* V,
const int* E,
const double* W,
int u,
int v
) {
double a_uv = 0.0;
for (int i = V[u]; i < V[u+1]; i++) {
if (E[i] == v) {
a_uv = W[i];
break;
}
}
return a_uv;
}
static __device__ double normalized_mutual_weight(
const int* V,
const int* E,
const double* W,
int u,
int v,
norm_t norm
) {
double weight_uv = mutual_weight(V, E, W, u, v);
double scale = 0.0;
if (norm == SUM) {
for (int i = V[u]; i < V[u+1]; i++) {
int neighbor = E[i];
double weight_uw = mutual_weight(V, E, W, u, neighbor);
scale += weight_uw;
}
} else if (norm == MAX) {
for (int i = V[u]; i < V[u+1]; i++) {
int neighbor = E[i];
double weight_uw = mutual_weight(V, E, W, u, neighbor);
scale = fmax(scale,weight_uw);
}
}
return (scale==0.0) ? 0.0 : (weight_uv / scale);
}
static __device__ double directed_mutual_weight(
const int* V,
const int* E,
const double* W,
int u,
int v
) {
double a_uv = 0.0, a_vu = 0.0;
for (int i = V[u]; i < V[u+1]; i++) {
if (E[i] == v) {
a_uv = W[i];
break;
}
}
for (int i = V[v]; i < V[v+1]; i++) {
if (E[i] == u) {
a_vu = W[i];
break;
}
}
return a_uv + a_vu;
}
static __device__ double directed_normalized_mutual_weight(
const int* V,
const int* E,
const int* row,
const int* col,
const double* W,
int num_edges,
int u,
int v,
norm_t norm
) {
double weight_uv = directed_mutual_weight(V, E, W, u, v);
double scale = 0.0;
if(norm==SUM){
for (int i = V[u]; i < V[u+1]; i++) {
int neighbor = E[i];
double weight_uw = directed_mutual_weight(V, E, W, u, neighbor);
scale += weight_uw;
}
for (int i = 0; i < num_edges; i++) {
if (col[i] == u) {
int neighbor = row[i];
double weight_wu = directed_mutual_weight(V, E, W, u, neighbor);
scale += weight_wu;
}
}
}else if(norm==MAX){
for (int i = V[u]; i < V[u+1]; i++) {
int neighbor = E[i];
double weight_uw = directed_mutual_weight(V, E, W, u, neighbor);
scale = fmax(scale,weight_uw);
}
for (int i = 0; i < num_edges; i++) {
if (col[i] == u) {
int neighbor = row[i];
double weight_wu = directed_mutual_weight(V, E, W, u, neighbor);
scale = fmax(scale,weight_wu);
}
}
}
return (scale==0.0) ? 0.0 : (weight_uv / scale);
}
static __device__ double redundancy(
const int* V,
const int* E,
const double* W,
const int num_nodes,
int u,
int v
) {
double r = 0.0;
for (int i = V[v]; i < V[v + 1]; i++) {
int w = E[i];
r += normalized_mutual_weight(V, E, W, u, w, SUM) * normalized_mutual_weight(V, E, W, v, w, MAX);
}
return 1-r;
}
__inline__ __device__ double warp_reduce_sum(double val)
{
for (int offset = warpSize / 2; offset > 0; offset /= 2)
{
val += __shfl_down_sync(0xffffffff, val, offset);
}
return val;
}
__inline__ __device__ double block_reduce_sum(double val)
{
val = warp_reduce_sum(val);
__shared__ double shared[32];
int warp_id = threadIdx.x / warpSize;
if (threadIdx.x % warpSize == 0)
{
shared[warp_id] = val;
}
__syncthreads();
if (warp_id == 0)
{
val = (threadIdx.x < (blockDim.x / warpSize)) ? shared[threadIdx.x] : 0.0;
val = warp_reduce_sum(val);
}
return val;
}
__global__ void calculate_effective_size(
const int* __restrict__ V,
const int* __restrict__ E,
const double* __restrict__ W,
const int num_nodes,
const int* __restrict__ node_mask,
double* __restrict__ effective_size_results
) {
int u = blockIdx.x;
if (u >= num_nodes || !node_mask[u]) return;
int neighbor_start = V[u];
int neighbor_end = V[u + 1];
int degree = neighbor_end - neighbor_start;
int threads_per_block = blockDim.x;
double redundancy_sum = 0.0;
for (int idx = threadIdx.x; idx < degree; idx += threads_per_block) {
int i = neighbor_start + idx;
int v = E[i];
if (v != u) {
double r = 0.0;
for (int j = V[v]; j < V[v + 1]; j++) {
int w = E[j];
r += normalized_mutual_weight(V, E, W, u, w, SUM) *
normalized_mutual_weight(V, E, W, v, w, MAX);
}
redundancy_sum += 1 - r;
}
}
redundancy_sum = block_reduce_sum(redundancy_sum);
if (threadIdx.x == 0) {
effective_size_results[u] = redundancy_sum;
}
}
static __device__ double directed_redundancy(
const int* V,
const int* E,
const int* row,
const int* col,
const double* W,
const int num_nodes,
const int num_edges,
int u,
int v
) {
double r = 0.0;
for (int i = V[v]; i < V[v + 1]; i++) {
int w = E[i];
r += directed_normalized_mutual_weight(V, E, row,col,W,num_edges, u, w,SUM) * directed_normalized_mutual_weight(V, E, row,col,W, num_edges, v,w,MAX);
}
for (int i = 0; i < num_edges; i++) {
if (col[i] == v) {
int w = row[i];
r += directed_normalized_mutual_weight(V, E, row,col,W,num_edges, u, w,SUM) * directed_normalized_mutual_weight(V, E, row,col,W, num_edges, v,w,MAX);
}
}
return 1-r;
}
__global__ void directed_calculate_effective_size(
const int* V,
const int* E,
const int* row,
const int* col,
const double* W,
const int num_nodes,
const int num_edges,
const int* node_mask,
double* effective_size_results
) {
int u = blockIdx.x * blockDim.x + threadIdx.x;
if (u >= num_nodes || !node_mask[u]) return;
double redundancy_sum = 0.0;
bool is_nan = true;
for (int i = V[u]; i < V[u + 1]; i++) {
int v = E[i];
if (v == u) continue;
is_nan = false;
redundancy_sum += directed_redundancy(V,E,row,col,W,num_nodes,num_edges,u,v);
}
for (int i = 0; i < num_edges; i++) {
if (col[i] == u) {
int v = row[i];
redundancy_sum += directed_redundancy(V,E,row,col,W,num_nodes,num_edges,u,v);
}
}
effective_size_results[u] = is_nan ? NAN : redundancy_sum;
}
int cuda_effective_size(
_IN_ const int* V,
_IN_ const int* E,
_IN_ const int* row,
_IN_ const int* col,
_IN_ const double* W,
_IN_ int num_nodes,
_IN_ int num_edges,
_IN_ bool is_directed,
_IN_ int* node_mask,
_OUT_ double* effective_size_results
) {
int cuda_ret = cudaSuccess;
int EG_ret = EG_GPU_SUCC;
int min_grid_size = 0;
int block_size = 0;
cudaOccupancyMaxPotentialBlockSize(&min_grid_size, &block_size, calculate_effective_size, 0, 0);
int grid_size = (num_nodes + block_size * NODES_PER_BLOCK - 1) / (block_size * NODES_PER_BLOCK);
int* d_V;
int* d_E;
int* d_row;
int* d_col;
double* d_W;
int* d_node_mask;
double* d_effective_size_results;
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_V, (num_nodes+1) * sizeof(int)));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_E, num_edges * sizeof(int)));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_row, num_edges * sizeof(int)));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_col, num_edges * sizeof(int)));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_W, num_edges * sizeof(double)));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_node_mask, num_nodes * sizeof(int)));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_effective_size_results, num_nodes * sizeof(double)));
EXIT_IF_CUDA_FAILED(cudaMemcpy(d_V, V, (num_nodes+1) * sizeof(int), cudaMemcpyHostToDevice));
EXIT_IF_CUDA_FAILED(cudaMemcpy(d_E, E, num_edges * sizeof(int), cudaMemcpyHostToDevice));
EXIT_IF_CUDA_FAILED(cudaMemcpy(d_row, row, num_edges * sizeof(int), cudaMemcpyHostToDevice));
EXIT_IF_CUDA_FAILED(cudaMemcpy(d_col, col, num_edges * sizeof(int), cudaMemcpyHostToDevice));
EXIT_IF_CUDA_FAILED(cudaMemcpy(d_node_mask, node_mask, num_nodes * sizeof(int), cudaMemcpyHostToDevice));
EXIT_IF_CUDA_FAILED(cudaMemcpy(d_W, W, num_edges * sizeof(double), cudaMemcpyHostToDevice));
if(is_directed){
directed_calculate_effective_size<<<grid_size, block_size>>>(d_V, d_E, d_row, d_col, d_W, num_nodes, num_edges, d_node_mask, d_effective_size_results);
}else{
int block_size = 256;
int grid_size = (num_nodes + NODES_PER_BLOCK - 1) / NODES_PER_BLOCK;
calculate_effective_size<<<grid_size, block_size>>>(d_V, d_E, d_W, num_nodes, d_node_mask, d_effective_size_results);
}
EXIT_IF_CUDA_FAILED(cudaMemcpy(effective_size_results, d_effective_size_results, num_nodes * sizeof(double), cudaMemcpyDeviceToHost));
exit:
cudaFree(d_V);
cudaFree(d_E);
cudaFree(d_row);
cudaFree(d_col);
cudaFree(d_W);
cudaFree(d_node_mask);
cudaFree(d_effective_size_results);
if (cuda_ret != cudaSuccess) {
switch (cuda_ret) {
case cudaErrorMemoryAllocation:
EG_ret = EG_GPU_FAILED_TO_ALLOCATE_DEVICE_MEM;
break;
default:
EG_ret = EG_GPU_DEVICE_ERR;
break;
}
}
return EG_ret;
}
} // namespace gpu_easygraph
@@ -0,0 +1,20 @@
#pragma once
#include "common.h"
namespace gpu_easygraph {
int cuda_effective_size(
_IN_ const int* V,
_IN_ const int* E,
_IN_ const int* row,
_IN_ const int* col,
_IN_ const double* W,
_IN_ int num_nodes,
_IN_ int num_edges,
_IN_ bool is_directed,
_IN_ int* node_mask,
_OUT_ double* effective_size_results
);
} // namespace gpu_easygraph
@@ -0,0 +1,31 @@
#include <string>
#include <vector>
#include <memory>
#include "structural_holes/hierarchy.cuh"
#include "common.h"
namespace gpu_easygraph {
using std::vector;
int hierarchy(
_IN_ const vector<int>& V,
_IN_ const vector<int>& E,
_IN_ const vector<int>& row,
_IN_ const vector<int>& col,
_IN_ int num_nodes,
_IN_ const vector<double>& W,
_IN_ bool is_directed,
_IN_ vector<int>& node_mask,
_OUT_ vector<double>& hierarchy
) {
int num_edges = row.size();
hierarchy = vector<double>(num_nodes);
int r = cuda_hierarchy(V.data(), E.data(), row.data(), col.data(), W.data(), num_nodes, num_edges, is_directed, node_mask.data(), hierarchy.data());
return r;
}
} // namespace gpu_easygraph
@@ -0,0 +1,369 @@
#include <cuda.h>
#include <cuda_runtime.h>
#include <stdlib.h>
#include "common.h"
#define NODES_PER_BLOCK 1
namespace gpu_easygraph {
enum norm_t { SUM = 0, MAX = 1 };
static __device__ double mutual_weight(
const int* V,
const int* E,
const double* W,
int u,
int v
) {
double a_uv = 0.0;
for (int i = V[u]; i < V[u+1]; i++) {
if (E[i] == v) {
a_uv = W[i];
break;
}
}
return a_uv;
}
static __device__ double normalized_mutual_weight(
const int* V,
const int* E,
const double* W,
int u,
int v,
norm_t norm
) {
double weight_uv = mutual_weight(V, E, W, u, v);
double scale = 0.0;
if (norm == SUM) {
for (int i = V[u]; i < V[u+1]; i++) {
int neighbor = E[i];
double weight_uw = mutual_weight(V, E, W, u, neighbor);
scale += weight_uw;
}
} else if (norm == MAX) {
for (int i = V[u]; i < V[u+1]; i++) {
int neighbor = E[i];
double weight_uw = mutual_weight(V, E, W, u, neighbor);
scale = fmax(scale,weight_uw);
}
}
return (scale==0.0) ? 0.0 : (weight_uv / scale);
}
static __device__ double directed_mutual_weight(
const int* V,
const int* E,
const double* W,
int u,
int v
) {
double a_uv = 0.0, a_vu = 0.0;
for (int i = V[u]; i < V[u+1]; i++) {
if (E[i] == v) {
a_uv = W[i];
break;
}
}
for (int i = V[v]; i < V[v+1]; i++) {
if (E[i] == u) {
a_vu = W[i];
break;
}
}
return a_uv + a_vu;
}
static __device__ double directed_normalized_mutual_weight(
const int* V,
const int* E,
const int* row,
const int* col,
const double* W,
int num_edges,
int u,
int v,
norm_t norm
) {
double weight_uv = directed_mutual_weight(V, E, W, u, v);
double scale = 0.0;
if(norm==SUM){
for (int i = V[u]; i < V[u+1]; i++) {
int neighbor = E[i];
double weight_uw = directed_mutual_weight(V, E, W, u, neighbor);
scale += weight_uw;
}
for (int i = 0; i < num_edges; i++) {
if (col[i] == u) {
int neighbor = row[i];
double weight_wu = directed_mutual_weight(V, E, W, u, neighbor);
scale += weight_wu;
}
}
}else if(norm==MAX){
for (int i = V[u]; i < V[u+1]; i++) {
int neighbor = E[i];
double weight_uw = directed_mutual_weight(V, E, W, u, neighbor);
scale = fmax(scale,weight_uw);
}
for (int i = 0; i < num_edges; i++) {
if (col[i] == u) {
int neighbor = row[i];
double weight_wu = directed_mutual_weight(V, E, W, u, neighbor);
scale = fmax(scale,weight_wu);
}
}
}
return (scale==0.0) ? 0.0 : (weight_uv / scale);
}
static __device__ double atomicAdd (
_OUT_ double* address,
_IN_ double val
)
{
unsigned long long int* address_as_ull =
(unsigned long long int*)address;
unsigned long long int old = *address_as_ull, assumed;
do {
assumed = old;
old = atomicCAS(address_as_ull, assumed,
__double_as_longlong(val +
__longlong_as_double(assumed)));
} while (assumed != old);
return __longlong_as_double(old);
}
static __device__ double local_constraint(
const int* V,
const int* E,
const double* W,
int u,
int v
) {
double direct = normalized_mutual_weight(V,E,W,u,v,SUM);
double indirect = 0.0;
for (int i = V[u]; i < V[u+1]; i++) {
int neighbor = E[i];
double norm_uw = normalized_mutual_weight(V, E, W, u, neighbor,SUM);
double norm_wv = normalized_mutual_weight(V, E, W, neighbor, v,SUM);
indirect += norm_uw * norm_wv;
}
double local_constraint_of_uv = (direct + indirect) * (direct + indirect);
return local_constraint_of_uv;
}
__global__ void calculate_hierarchy(
const int* V,
const int* E,
const double* W,
int num_nodes,
const int* node_mask,
double* hierarchy_results
) {
int start_node = blockIdx.x * NODES_PER_BLOCK;
int end_node = min(start_node + NODES_PER_BLOCK, num_nodes);
extern __shared__ double shared_mem[];
double* shared_c = shared_mem;
double* shared_C = &shared_mem[blockDim.x];
for (int v = start_node; v < end_node; ++v) {
if (!node_mask[v]) continue;
int n = V[v + 1] - V[v];
if (n <= 1) {
hierarchy_results[v] = 0.0;
continue;
}
if (threadIdx.x == 0) shared_C[0] = 0.0;
__syncthreads();
double local_C = 0.0;
for (int i = V[v] + threadIdx.x; i < V[v + 1]; i += blockDim.x) {
int w = E[i];
double constraint = local_constraint(V, E, W, v, w);
shared_c[threadIdx.x] = constraint;
local_C += constraint;
}
atomicAdd(&shared_C[0], local_C);
__syncthreads();
if (threadIdx.x == 0) {
double C = shared_C[0];
double hierarchy_sum = 0.0;
for (int i = 0; i < n; i++) {
double normalized_c = shared_c[i] / C;
hierarchy_sum += normalized_c * n * logf(normalized_c * n) / (n * logf(n));
}
hierarchy_results[v] = hierarchy_sum;
}
__syncthreads();
}
}
static __device__ double directed_local_constraint(
const int* V,
const int* E,
const int* row,
const int* col,
const double* W,
int num_edges,
int u,
int v
) {
double direct = directed_normalized_mutual_weight(V,E,row,col,W,num_edges,u,v,SUM);
double indirect = 0.0;
for (int i = V[u]; i < V[u+1]; i++) {
int neighbor = E[i];
double norm_uw = directed_normalized_mutual_weight(V, E, row, col, W, num_edges, u, neighbor,SUM);
double norm_wv = directed_normalized_mutual_weight(V, E, row, col, W, num_edges, neighbor, v,SUM);
indirect += norm_uw * norm_wv;
}
for (int i = 0; i < num_edges; i++) {
if (col[i] == u) {
int neighbor = row[i];
double norm_uw = directed_normalized_mutual_weight(V, E, row, col, W, num_edges, u, neighbor,SUM);
double norm_wv = directed_normalized_mutual_weight(V, E, row, col, W, num_edges, neighbor, v,SUM);
indirect += norm_uw * norm_wv;
}
}
double local_constraint_of_uv = (direct + indirect) * (direct + indirect);
return local_constraint_of_uv;
}
__global__ void directed_calculate_hierarchy(
const int* V,
const int* E,
const int* row,
const int* col,
const double* W,
const int num_nodes,
const int num_edges,
const int* node_mask,
double* hierarchy_results
) {
int v = blockIdx.x * blockDim.x + threadIdx.x;
if (v >= num_nodes || !node_mask[v]) return;
int in_neighbor = V[v + 1] - V[v];
int out_neighbor = 0;
double C = 0.0;
double hierarchy_sum = 0.0;
int neighbor = 0;
for (int i = 0; i < num_edges; i++) {
if (col[i] == v) {
out_neighbor++;
}
}
double *c = new double[in_neighbor+out_neighbor];
for (int i = V[v]; i < V[v + 1]; i++) {
int w = E[i];
c[neighbor] = directed_local_constraint(V, E, row, col, W, num_edges, v, w);
C += c[neighbor];
neighbor++;
}
for (int i = 0; i < num_edges; i++) {
if (col[i] == v) {
int w = row[i];
c[neighbor] = directed_local_constraint(V, E, row, col, W, num_edges, v, w);
C += c[neighbor];
neighbor++;
}
}
__syncthreads();
if (neighbor > 1) {
for (int i = 0; i < neighbor; i++) {
hierarchy_sum += (c[i] / C) * neighbor * logf((c[i] / C) * neighbor) / (neighbor * logf(neighbor));
}
hierarchy_results[v] = hierarchy_sum;
}else{
hierarchy_results[v] = 0;
}
delete[] c;
}
int cuda_hierarchy(
_IN_ const int* V,
_IN_ const int* E,
_IN_ const int* row,
_IN_ const int* col,
_IN_ const double* W,
_IN_ int num_nodes,
_IN_ int num_edges,
_IN_ bool is_directed,
_IN_ int* node_mask,
_OUT_ double* hierarchy_results
){
int cuda_ret = cudaSuccess;
int EG_ret = EG_GPU_SUCC;
int min_grid_size = 0;
int block_size = 0;
cudaOccupancyMaxPotentialBlockSize(&min_grid_size, &block_size, calculate_hierarchy, 0, 0);
int grid_size = (num_nodes + block_size - 1) / block_size;
int* d_V;
int* d_E;
int* d_row;
int* d_col;
double* d_W;
int* d_node_mask;
double* d_hierarchy_results;
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_V, (num_nodes+1) * sizeof(int)));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_E, num_edges * sizeof(int)));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_row, num_edges * sizeof(int)));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_col, num_edges * sizeof(int)));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_W, num_edges * sizeof(double)));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_node_mask, num_nodes * sizeof(int)));
EXIT_IF_CUDA_FAILED(cudaMalloc((void**)&d_hierarchy_results, num_nodes * sizeof(double)));
EXIT_IF_CUDA_FAILED(cudaMemcpy(d_V, V, (num_nodes+1) * sizeof(int), cudaMemcpyHostToDevice));
EXIT_IF_CUDA_FAILED(cudaMemcpy(d_E, E, num_edges * sizeof(int), cudaMemcpyHostToDevice));
EXIT_IF_CUDA_FAILED(cudaMemcpy(d_row, row, num_edges * sizeof(int), cudaMemcpyHostToDevice));
EXIT_IF_CUDA_FAILED(cudaMemcpy(d_col, col, num_edges * sizeof(int), cudaMemcpyHostToDevice));
EXIT_IF_CUDA_FAILED(cudaMemcpy(d_node_mask, node_mask, num_nodes * sizeof(int), cudaMemcpyHostToDevice));
EXIT_IF_CUDA_FAILED(cudaMemcpy(d_W, W, num_edges * sizeof(double), cudaMemcpyHostToDevice));
if(is_directed){
directed_calculate_hierarchy<<<grid_size, block_size>>>(d_V, d_E, d_row, d_col, d_W, num_nodes, num_edges, d_node_mask, d_hierarchy_results);
}else{
int block_size = 256;
int grid_size = (num_nodes + NODES_PER_BLOCK - 1) / NODES_PER_BLOCK;
int shared_memory_size = 2 * sizeof(double) * block_size;
calculate_hierarchy<<<grid_size, block_size, shared_memory_size>>>(d_V, d_E, d_W, num_nodes, d_node_mask, d_hierarchy_results);
}
EXIT_IF_CUDA_FAILED(cudaMemcpy(hierarchy_results, d_hierarchy_results, num_nodes * sizeof(double), cudaMemcpyDeviceToHost));
exit:
cudaFree(d_V);
cudaFree(d_E);
cudaFree(d_row);
cudaFree(d_col);
cudaFree(d_W);
cudaFree(d_node_mask);
cudaFree(d_hierarchy_results);
if (cuda_ret != cudaSuccess) {
switch (cuda_ret) {
case cudaErrorMemoryAllocation:
EG_ret = EG_GPU_FAILED_TO_ALLOCATE_DEVICE_MEM;
break;
default:
EG_ret = EG_GPU_DEVICE_ERR;
break;
}
}
return EG_ret;
}
}
@@ -0,0 +1,19 @@
#pragma once
#include "common.h"
namespace gpu_easygraph {
int cuda_hierarchy(
_IN_ const int* V,
_IN_ const int* E,
_IN_ const int* row,
_IN_ const int* col,
_IN_ const double* W,
_IN_ int num_nodes,
_IN_ int num_edges,
_IN_ bool is_directed,
_IN_ int* node_mask,
_OUT_ double* hierarchy_results
);
} // namespace gpu_easygraph
+100
View File
@@ -0,0 +1,100 @@
#include <vector>
#include "./common/err.h"
namespace gpu_easygraph {
int closeness_centrality(
const std::vector<int>& V,
const std::vector<int>& E,
const std::vector<double>& W,
const std::vector<int>& sources,
std::vector<double>& CC
);
int betweenness_centrality(
const std::vector<int>& V,
const std::vector<int>& E,
const std::vector<double>& W,
const std::vector<int>& sources,
bool is_directed,
bool normalized,
bool endpoints,
std::vector<double>& BC
);
int k_core(
const std::vector<int>& V,
const std::vector<int>& E,
std::vector<int>& KC
);
int sssp_dijkstra(
const std::vector<int>& V,
const std::vector<int>& E,
const std::vector<double>& W,
const std::vector<int>& sources,
int target,
std::vector<double>& res
);
int pagerank(
const std::vector<int>& V,
const std::vector<int>& E,
double alpha,
int max_iter_num,
double threshold,
std::vector<double>& PR
);
int constraint(
const std::vector<int>& V,
const std::vector<int>& E,
const std::vector<int>& row,
const std::vector<int>& col,
int num_nodes,
const std::vector<double>& W,
bool is_directed,
std::vector<int>& node_mask,
std::vector<double>& constraint
);
int hierarchy(
const std::vector<int>& V,
const std::vector<int>& E,
const std::vector<int>& row,
const std::vector<int>& col,
int num_nodes,
const std::vector<double>& W,
bool is_directed,
std::vector<int>& node_mask,
std::vector<double>& hierarchy
);
int effective_size(
const std::vector<int>& V,
const std::vector<int>& E,
const std::vector<int>& row,
const std::vector<int>& col,
int num_nodes,
const std::vector<double>& W,
bool is_directed,
std::vector<int>& node_mask,
std::vector<double>& effective_size
);
} // namespace gpu_easygraph