#include "convolution.h" #include "util.h" #include #include #include #define CUDA_CALL(f) \ { \ cudaError_t err = (f); \ if (err != cudaSuccess) { \ fprintf(stderr, "CUDA error at [%s:%d] %d %s\n", __FILE__, __LINE__, \ err, cudaGetErrorString(err)); \ exit(1); \ } \ } #define MAX_NUM_GPU 8 #define TS 32 int num_devices = 0; static int mpi_world_size, mpi_rank; static float *input, *output, *filter; //static int OH, OW; __global__ void sgemm(float *input, float *output, float *filter, int N, int C, int H, int W, int K, int R, int S, int pad, int dilation, int stride) { int n = blockIdx.x; int k = blockIdx.y; int oh = blockIdx.z; int ow = threadIdx.x; const int OH = (H + 2 * pad - dilation * (R - 1) - 1) / stride + 1; const int OW = (W + 2 * pad - dilation * (S - 1) - 1) / stride + 1; float o = 0.f; for(int c = 0; c < C; ++c){ for (int r = 0; r < R; ++r) { for (int s = 0; s < S; ++s) { int h = oh * stride - pad + r * dilation; int w = ow * stride - pad + s * dilation; if (h < 0 || h >= H || w < 0 || w >= W) continue; float i = input[n*C*H*W + c*H*W + h*W + w]; float f = filter[k*C*R*S + c*R*S + r*S + s]; o += i*f; } } } output[n*K*OH*OW + k*OH*OW + oh*OW + ow] = o; } // Array of device (GPU) pointers static float *input_d[MAX_NUM_GPU]; static float *filter_d[MAX_NUM_GPU]; static float *output_d[MAX_NUM_GPU]; static int N, C, H, W, K, R, S, pad, dilation, stride, OH, OW; static int Nbegin[MAX_NUM_GPU], Nend[MAX_NUM_GPU]; static int is[2], ie[2]; void convolution(float *_input, float *_output, float *_filter, int _N, int _C, int _H, int _W, int _K, int _R, int _S, int _pad, int _dilation, int _stride) { input = _input; output = _output; filter = _filter; if(mpi_world_size > 1) { for (int i = 0; i < mpi_world_size; i++) { is[i] = N / mpi_world_size * i; ie[i] = N / mpi_world_size * (i + 1); } ie[mpi_world_size - 1] = N; if (mpi_rank != 0) { alloc_tensor(&input, N, C, H, W); alloc_tensor(&output, N, K, OH, OW); alloc_tensor(&filter, K, C, R, S); } if (mpi_rank == 0) { for (int i = 1; i < mpi_world_size; i++) { MPI_Send(input+is[i]*C*H*W, (ie[i]-is[i])*C*H*W, MPI_FLOAT, i, 0, MPI_COMM_WORLD); } } else { MPI_Recv(input+is[mpi_rank]*C*H*W, (ie[mpi_rank]-is[mpi_rank])*C*H*W, MPI_FLOAT, 0, 0, MPI_COMM_WORLD, nullptr); } // Broadcast B MPI_Bcast(filter, K*C*R*S, MPI_FLOAT, 0, MPI_COMM_WORLD); } //if(mpi_rank == 0){ /* CUDA_CALL( cudaGetDeviceCount(&num_devices) ); printf("Using %d devices\n", num_devices); for (int i = 0; i < num_devices; i++) { cudaDeviceProp prop; CUDA_CALL( cudaGetDeviceProperties(&prop, i) ); // Try printing more detailed information here printf("[GPU %d] %s\n", i, prop.name); } if (num_devices <= 0) { printf("No CUDA device found. Aborting\n"); exit(1); } if(mpi_world_size > 1){ // Setup problem size for each GPU if(mpi_rank == 0){ for (int i = 0; i < num_devices; i++) { Nbegin[i] = (N/mpi_world_size) / num_devices * i; Nend[i] = (N/mpi_world_size) / num_devices * (i + 1); } Nend[num_devices - 1] = N/mpi_world_size; } else { for (int i = 0; i < num_devices; i++) { Nbegin[i] = N/mpi_world_size + (N/mpi_world_size + N%mpi_world_size) / num_devices * i; Nend[i] = N/mpi_world_size + (N/mpi_world_size + N%mpi_world_size) / num_devices * (i+1); } Nend[num_devices - 1] = N; } } else { for (int i = 0; i < num_devices; i++) { Nbegin[i] = N / num_devices * i; Nend[i] = N / num_devices * (i + 1); } Nend[num_devices - 1] = N; } // Allocate device memory for each GPU for (int i = 0; i < num_devices; i++) { CUDA_CALL( cudaSetDevice(i) ); CUDA_CALL( cudaMalloc(&input_d[i], (Nend[i] - Nbegin[i])*C*H*W*sizeof(float)) ); CUDA_CALL( cudaMalloc(&filter_d[i], K*C*R*S*sizeof(float)) ); CUDA_CALL( cudaMalloc(&output_d[i], (Nend[i] - Nbegin[i])*K*OH*OW*sizeof(float)) ); }*/ // Upload A and B matrix to every GPU for (int i = 0; i < num_devices; i++) { CUDA_CALL( cudaMemcpy(input_d[i], input + Nbegin[i]*C*H*W, (Nend[i] - Nbegin[i])*C*H*W*sizeof(float), cudaMemcpyHostToDevice) ); CUDA_CALL( cudaMemcpy(filter_d[i], filter, K*C*R*S*sizeof(float), cudaMemcpyHostToDevice) ); } // DO NOT REMOVE; NEEDED FOR TIME MEASURE for (int i = 0; i < num_devices; i++) { CUDA_CALL( cudaDeviceSynchronize() ); } // Launch kernel on every GPU for (int i = 0; i < num_devices; i++) { dim3 blockDim(OW, 1, 1); dim3 gridDim(Nend[i]-Nbegin[i], K, OH); CUDA_CALL( cudaSetDevice(i) ); sgemm<<>>(input_d[i], output_d[i], filter_d[i], Nend[i]-Nbegin[i],C,H,W,K,R,S,pad,dilation,stride); } // DO NOT REMOVE; NEEDED FOR TIME MEASURE for (int i = 0; i < num_devices; i++) { CUDA_CALL( cudaDeviceSynchronize() ); } //} /* // Gather C if (mpi_rank == 0) { for (int i = 1; i < mpi_world_size; i++) { MPI_Recv(output+is[i]*K*OH*OW, (ie[i]-is[i])*K*OH*OW, MPI_FLOAT, i, 0, MPI_COMM_WORLD, nullptr); } } else { MPI_Send(output+is[mpi_rank]*K*OH*OW, (ie[mpi_rank]-is[mpi_rank])*K*OH*OW, MPI_FLOAT, 0, 0, MPI_COMM_WORLD); }*/ // } for (int i = 0; i < num_devices; i++) { CUDA_CALL( cudaMemcpy(output + Nbegin[i]*K*OH*OW, output_d[i], (Nend[i] - Nbegin[i])*K*OH*OW*sizeof(float), cudaMemcpyDeviceToHost) ); } // DO NOT REMOVE; NEEDED FOR TIME MEASURE for (int i = 0; i < num_devices; i++) { CUDA_CALL( cudaDeviceSynchronize() ); } if(mpi_world_size > 1){ // Gather C if (mpi_rank == 0) { for (int i = 1; i < mpi_world_size; i++) { MPI_Recv(output+is[i]*K*OH*OW, (ie[i]-is[i])*K*OH*OW, MPI_FLOAT, i, 0, MPI_COMM_WORLD, nullptr); } } else { MPI_Send(output+is[mpi_rank]*K*OH*OW, (ie[mpi_rank]-is[mpi_rank])*K*OH*OW, MPI_FLOAT, 0, 0, MPI_COMM_WORLD); } } } void convolution_init(int _N, int _C, int _H, int _W, int _K, int _R, int _S, int _pad, int _dilation, int _stride) { N = _N, C = _C, H = _H, W = _W, K = _K, R = _R, S = _S, pad = _pad, dilation = _dilation, stride = _stride; MPI_Comm_rank(MPI_COMM_WORLD, &mpi_rank); MPI_Comm_size(MPI_COMM_WORLD, &mpi_world_size); CUDA_CALL( cudaGetDeviceCount(&num_devices) ); printf("Using %d devices\n", num_devices); for (int i = 0; i < num_devices; i++) { cudaDeviceProp prop; CUDA_CALL( cudaGetDeviceProperties(&prop, i) ); // Try printing more detailed information here printf("[GPU %d] %s\n", i, prop.name); } if (num_devices <= 0) { printf("No CUDA device found. Aborting\n"); exit(1); } if(mpi_world_size > 1){ // Setup problem size for each GPU if(mpi_rank == 0){ for (int i = 0; i < num_devices; i++) { Nbegin[i] = (N/mpi_world_size) / num_devices * i; Nend[i] = (N/mpi_world_size) / num_devices * (i + 1); } Nend[num_devices - 1] = N/mpi_world_size; } else { for (int i = 0; i < num_devices; i++) { Nbegin[i] = N/mpi_world_size + (N/mpi_world_size + N%mpi_world_size) / num_devices * i; Nend[i] = N/mpi_world_size + (N/mpi_world_size + N%mpi_world_size) / num_devices * (i+1); } Nend[num_devices - 1] = N; } } else { for (int i = 0; i < num_devices; i++) { Nbegin[i] = N / num_devices * i; Nend[i] = N / num_devices * (i + 1); } Nend[num_devices - 1] = N; } OH = (H + 2 * pad - dilation * (R - 1) -1) / stride + 1; OW = (W + 2 * pad - dilation * (S - 1) -1) / stride + 1; // Allocate device memory for each GPU for (int i = 0; i < num_devices; i++) { CUDA_CALL( cudaSetDevice(i) ); CUDA_CALL( cudaMalloc(&input_d[i], (Nend[i] - Nbegin[i])*C*H*W*sizeof(float)) ); CUDA_CALL( cudaMalloc(&filter_d[i], K*C*R*S*sizeof(float)) ); CUDA_CALL( cudaMalloc(&output_d[i], (Nend[i] - Nbegin[i])*K*OH*OW*sizeof(float)) ); } } void convolution_final(int _N, int _C, int _H, int _W, int _K, int _R, int _S, int _pad, int _dilation, int _stride) { // Do any post-matmul cleanup work here. //int OH = (H + 2 * pad - dilation * (R - 1) - 1) / stride + 1; //int OW = (W + 2 * pad - dilation * (S - 1) - 1) / stride + 1; //float *input, *output, *filter; //if(mpi_rank == 0) { // Download C matrix from GPUs //if(mpi_rank == 0){ /* for (int i = 0; i < num_devices; i++) { CUDA_CALL( cudaMemcpy(output + Nbegin[i]*K*OH*OW, output_d[i], (Nend[i] - Nbegin[i])*K*OH*OW*sizeof(float), cudaMemcpyDeviceToHost) ); } // DO NOT REMOVE; NEEDED FOR TIME MEASURE for (int i = 0; i < num_devices; i++) { CUDA_CALL( cudaDeviceSynchronize() ); } if(mpi_world_size > 1){ // Gather C if (mpi_rank == 0) { for (int i = 1; i < mpi_world_size; i++) { MPI_Recv(output+is[i]*K*OH*OW, (ie[i]-is[i])*K*OH*OW, MPI_FLOAT, i, 0, MPI_COMM_WORLD, nullptr); } } else { MPI_Send(output+is[mpi_rank]*K*OH*OW, (ie[mpi_rank]-is[mpi_rank])*K*OH*OW, MPI_FLOAT, 0, 0, MPI_COMM_WORLD); } } */ //} }