CellularAutomata
Loading...
Searching...
No Matches
cuda.inl
1#pragma once
2
3#include "cuda.cuh"
4#include "../core/common.hpp"
5#include "../core/exceptions.hpp"
6
7#include <iostream>
8
9#include <cuda_runtime.h>
10#include <device_launch_parameters.h>
11
12#define cudaCheck(fn) do {cudaError_t e = fn; if (e != cudaSuccess) throw CellularAutomata::exception::CudaRuntime(cudaGetErrorString(e)); } while(false)
13
14#define TILE_SIZE 32
15
24__device__ __forceinline__ unsigned int modP(const int a, const int n)
25{
26 int r = a % n;
27 if (r < 0)
28 r += n;
29 return r;
30}
31
32template<typename T>
33__device__ void loadShared_recursive(const T* __restrict__ input, T* shared, const uint2 inputShape, const uint2 sharedShape, const unsigned int kernelSize, const uint4 valid)
34{
35 const int halo = kernelSize / 2; // extra space needed because convolution goes beyond borders
36 const int tid = threadIdx.y * blockDim.x + threadIdx.x; // local thread ID
37 const int memSize = sharedShape.y * sharedShape.x; // shared memory size
38 const int threadsPerBlock = blockDim.y * blockDim.x; // total number of threads per block (should be TILE_SIZE * TILE_SIZE)
39
40 // Because of halo / padding, each thread has to load multiple elements
41 for (int i = tid; i < memSize; i += threadsPerBlock)
42 {
43 // convert i back to local x,y coordinates
44 int y = i / sharedShape.x;
45 int x = i % sharedShape.x;
46
47 // check if x,y are inside the valid area
48 if (y < (valid.w + kernelSize - 1) && x < (valid.z + kernelSize - 1))
49 {
50 // 1. convert x,y to global x,y
51 // 2. loop x,y if outside of input array range
52 y = modP(y + valid.y - halo, inputShape.y);
53 x = modP(x + valid.x - halo, inputShape.x);
54
55 // copy
56 shared[i] = input[y * inputShape.x + x];
57 }
58 }
59}
60
61template<typename T>
62__device__ void loadShared_simple(const T* __restrict__ input, T* shared, const int inputWidth, const int sharedWidth, const unsigned int kernelSize, const uint4 valid)
63{
64 const int halo = kernelSize / 2; // extra space needed because convolution goes beyond borders
65
66 if (threadIdx.y < valid.w && threadIdx.x < valid.z)
67 {
68 // each thread loads their element into shared offset by halo
69 shared[(threadIdx.y + halo) * sharedWidth + (threadIdx.x + halo)] = input[threadIdx.y * inputWidth + threadIdx.x];
70 }
71}
72
73template<typename T, typename Activation>
74__global__ void convKernel(
75 const T* __restrict__ input,
76 const T* __restrict__ kernel,
77 T* output,
78 Activation fn,
79 const uint2 inputShape,
80 const unsigned int kernelSize,
81 const bool recursive)
82{
83 extern __shared__ T shared[];
84
85#pragma region Variables
86
87 // Global position (aka output element which the thread handles): x = col, y = row
88 const uint2 pos{ blockIdx.x * blockDim.x + threadIdx.x, blockIdx.y * blockDim.y + threadIdx.y };
89
90 // shared memory shape
91 const uint2 sharedShape{ blockDim.x + kernelSize - 1, blockDim.y + kernelSize - 1 };
92
93 // rect in which the convolution should be done
94 const uint4 valid
95 {
96 blockIdx.x * blockDim.x, // x
97 blockIdx.y * blockDim.y, // y
98 min(inputShape.x - (blockIdx.x * blockDim.x), blockDim.x), // z
99 min(inputShape.y - (blockIdx.y * blockDim.y), blockDim.y) // w
100 };
101
102#pragma endregion Variables
103
104
105#pragma region Loading
106
107 if (recursive)
108 loadShared_recursive(input, shared, inputShape, sharedShape, kernelSize, valid);
109 else
110 loadShared_simple(input, shared, inputShape.x, sharedShape.x, kernelSize, valid);
111
112#pragma endregion Loading
113
114 __syncthreads();
115
116#pragma region Matrix mul
117
118 // Ignore empty / padded elements (input size is not perfect -> some tiles are not full)
119 if (threadIdx.x < valid.z && threadIdx.y < valid.w)
120 {
121 T temp = 0;
122
123 // Iterate over kernel
124 for (int y = 0; y < kernelSize; y++)
125 {
126 for (int x = 0; x < kernelSize; x++)
127 {
128 int _shared = (threadIdx.y + y) * sharedShape.x + (threadIdx.x + x);
129
130 temp += shared[_shared] * kernel[y * kernelSize + x];
131 }
132 }
133
134 output[pos.y * inputShape.x + pos.x] = fn(temp);
135 }
136
137#pragma endregion
138
139}
140
141namespace CellularAutomata
142{
143 namespace cuda
144 {
145 void allocateCUDA(void** arrayPtr, size_t bytes)
146 {
147 cudaCheck(cudaMalloc(arrayPtr, bytes));
148 }
149
150 void allocateHost(void** arrayPtr, size_t bytes)
151 {
152 cudaCheck(cudaMallocHost(arrayPtr, bytes));
153 }
154
155 void freeCUDA(void* array)
156 {
157 cudaCheck(cudaFree(array));
158 }
159
160 void freeHost(void* array)
161 {
162 cudaCheck(cudaFreeHost(array));
163 }
164
165 void copyCUDA(void* src, Device from, void* dst, Device to, size_t bytes)
166 {
167 cudaCheck(cudaMemcpy(dst, src, bytes, cudaMemcpyKind::cudaMemcpyDefault));
168 }
169
170 template <typename T, typename Activation>
171 void epoch(
172 T* input, T* kernel, T* output, Activation fn,
173 const unsigned int h, const unsigned int w, const unsigned int s, const bool r)
174 {
175 dim3 grid(((w - 1) / TILE_SIZE) + 1, ((h - 1) / TILE_SIZE) + 1);
176 dim3 block(TILE_SIZE, TILE_SIZE);
177 size_t memSize = (TILE_SIZE + s - 1) * (TILE_SIZE + s - 1) * sizeof(T);
178
179 // printDim3(grid);
180 // printDim3(block);
181 // std::cout << memSize << std::endl;
182
183 convKernel<<<grid, block, memSize>>> (
184 input, kernel, output, fn,
185 uint2{ h, w }, s, r);
186
187 // cudaError_t e = cudaDeviceSynchronize();
188
189 // if (e != cudaSuccess)
190 // printf("%s: %s\n", cudaGetErrorName(e), cudaGetErrorString(e));
191
192 // throw CellularAutomata::exception::CudaRuntime(cudaGetErrorString(e));
193
194 cudaCheck(cudaDeviceSynchronize());
195 }
196 }
197}
198
199#undef TILE_SIZE
200#undef cudaCheck
void copyCUDA(void *src, Device from, void *dst, Device to, size_t bytes)
Memcopy between devices and host.
Definition cuda.inl:165
void allocateHost(void **arrayPtr, size_t bytes)
Allocated memory on the host device (CPU)
Definition cuda.inl:150
void allocateCUDA(void **arrayPtr, size_t bytes)
Allocated memory on a CUDA device.
Definition cuda.inl:145
void freeCUDA(void *array)
Frees memory on a CUDA device.
Definition cuda.inl:155
void freeHost(void *array)
Frees memory on the host device (CPU)
Definition cuda.inl:160
Main namespace.
Definition common.hpp:33
Device
Computational device.
Definition common.hpp:39