Skip to content

Commit e63e6f6

Browse files
authored
Merge pull request #252 from L30nardoSV/tensorcores
Speeding up sum reductions in ADADELTA by using Tensor Cores
2 parents a0c8c95 + 0dde87b commit e63e6f6

8 files changed

Lines changed: 171 additions & 9 deletions

File tree

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "wmma_extension"]
2+
path = wmma_extension
3+
url = https://github.com/wmmae/wmma_extension

Makefile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,11 @@
2727
OVERLAP = ON
2828

2929
ifeq ($(DEVICE), $(filter $(DEVICE),GPU CUDA))
30-
TARGETS_SUPPORTED := $(shell ./test_cuda.sh nvcc "$(GPU_INCLUDE_PATH)" "$(GPU_LIBRARY_PATH)" "$(TARGETS)" "$(DEVICE)")
30+
MIN_COMPUTE:=50
31+
ifeq ($(TENSOR), ON)
32+
MIN_COMPUTE:=80
33+
endif
34+
TARGETS_SUPPORTED := $(shell ./test_cuda.sh nvcc "$(GPU_INCLUDE_PATH)" "$(GPU_LIBRARY_PATH)" "$(TARGETS)" "$(MIN_COMPUTE)")
3135
# if user specifies DEVICE=GPU the test result determines wether CUDA will be used or not
3236
ifeq ($(TARGETS_SUPPORTED),)
3337
ifeq ($(DEVICE),CUDA)

Makefile.Cuda

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@ CPP = g++
1616
UNAME := $(shell uname)
1717

1818
TARGETS = 52 60 61 70 80
19+
20+
ifeq ($(TENSOR), ON)
21+
NVTENSOR=-DUSE_NVTENSOR
22+
NVTENSOR+=-I./wmma_extension/include
23+
TARGETS = 80
24+
endif
25+
1926
CUDA_TARGETS=$(foreach target,$(TARGETS),-gencode arch=compute_$(target),code=sm_$(target))
2027

2128
ifeq ($(DEVICE), CPU)
@@ -54,7 +61,6 @@ ifeq ($(OVERLAP), ON)
5461
PIPELINE=-DUSE_PIPELINE -fopenmp
5562
endif
5663

57-
5864
BIN := $(wildcard $(TARGET)*)
5965

6066
# ------------------------------------------------------
@@ -63,6 +69,9 @@ BIN := $(wildcard $(TARGET)*)
6369
NUMWI=
6470

6571
ifeq ($(NUMWI), 32)
72+
ifeq ($(TENSOR), ON)
73+
$(error NUMWI needs to be at least 64 with TENSOR=ON)
74+
endif
6675
NWI=-DN32WI
6776
TARGET:=$(TARGET)_32wi
6877
else ifeq ($(NUMWI), 64)
@@ -177,15 +186,15 @@ TOOL_CFLAGS+=-DVERSION=\"$(GIT_VERSION)\"
177186
# ------------------------------------------------------
178187

179188
kernels: $(KERNEL_SRC)
180-
$(NVCC) $(NWI) $(REP) $(CUDA_FLAGS) $(IFLAGS) $(CUDA_INCLUDES) -c $(KRNL_DIR)/kernels.cu
189+
$(NVCC) $(NWI) $(REP) $(CUDA_FLAGS) $(IFLAGS) $(CUDA_INCLUDES) $(NVTENSOR) -c $(KRNL_DIR)/kernels.cu
181190

182191
otool:
183192
@echo "Building" $(TOOL_TARGET) "..."
184193
$(CPP) \
185194
$(shell ls $(HOST_SRC_DIR)/*.cpp) \
186195
$(TOOL_CFLAGS) \
187196
-o$(BIN_DIR)/$(TOOL_TARGET) \
188-
$(PIPELINE) $(OPT) -DTOOLMODE $(REP)
197+
$(PIPELINE) $(NVTENSOR) $(OPT) -DTOOLMODE $(REP)
189198

190199
odock: check-env-all kernels
191200
@echo "Building" $(TARGET) "..."
@@ -194,7 +203,7 @@ odock: check-env-all kernels
194203
$(CFLAGS) \
195204
$(LIB_CUDA) \
196205
-o$(BIN_DIR)/$(TARGET) \
197-
$(DEV) $(NWI) $(PIPELINE) $(OPT) $(DD) $(REP) $(KFLAGS)
206+
$(DEV) $(NWI) $(PIPELINE) $(NVTENSOR) $(OPT) $(DD) $(REP) $(KFLAGS)
198207

199208
# Example
200209
# 1ac8: for testing gradients of translation and rotation genes

cuda/calcMergeEneGra.cu

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -642,23 +642,77 @@ __device__ void gpu_calc_energrad(
642642
torque_rot.z += tr.z;
643643
}
644644

645-
// Do a reduction over the total gradient containing prepared "gradient_intra_*" values
645+
#ifdef USE_NVTENSOR
646+
/* Begin: Reduction using tensor units */
647+
648+
// Implementation based on M.Sc. thesis by Gabin Schieffer at KTH:
649+
// "Accelerating a Molecular Docking Application by Leveraging Modern Heterogeneous Computing Systemx"
650+
// https://www.diva-portal.org/smash/get/diva2:1786161/FULLTEXT01.pdf
651+
652+
// 1. Convert data-to-be-reduced from float to half
653+
// and place it in a shared memory array
654+
__shared__ __align__(256) float data_to_be_reduced[4*NUM_OF_THREADS_PER_BLOCK];
655+
data_to_be_reduced[4*threadIdx.x] = torque_rot.x;
656+
data_to_be_reduced[4*threadIdx.x + 1] = torque_rot.y;
657+
data_to_be_reduced[4*threadIdx.x + 2] = torque_rot.z;
658+
data_to_be_reduced[4*threadIdx.x + 3] = energy;
659+
660+
// 2. Perform reduction via tensor units
661+
reduce_via_tensor_units(data_to_be_reduced);
662+
663+
// 3. Retrieve results from shared memory
664+
torque_rot.x = data_to_be_reduced[0];
665+
torque_rot.y = data_to_be_reduced[1];
666+
torque_rot.z = data_to_be_reduced[2];
667+
energy = data_to_be_reduced[3];
668+
669+
/* End: Reduction using tensor units */
670+
#else
671+
// Reduction over the total gradient containing prepared "gradient_intra_*" values
646672
REDUCEFLOATSUM(torque_rot.x, pFloatAccumulator);
647673
REDUCEFLOATSUM(torque_rot.y, pFloatAccumulator);
648674
REDUCEFLOATSUM(torque_rot.z, pFloatAccumulator);
649675

676+
// Reduction over partial energies and prepared "gradient_intra_*" values
677+
REDUCEFLOATSUM(energy, pFloatAccumulator);
678+
#endif
679+
650680
// TODO
651681
// -------------------------------------------------------
652682
// Obtaining energy and translation-related gradients
653683
// -------------------------------------------------------
654-
// reduction over partial energies and prepared "gradient_intra_*" values
655-
REDUCEFLOATSUM(energy, pFloatAccumulator);
684+
656685
#if defined (DEBUG_ENERGY_KERNEL)
657686
REDUCEFLOATSUM(intraE, pFloatAccumulator);
658687
#endif
688+
689+
#ifdef USE_NVTENSOR
690+
/* Begin: Reduction using tensor units */
691+
692+
// Implementation based on M.Sc. thesis by Gabin Schieffer at KTH:
693+
// "Accelerating a Molecular Docking Application by Leveraging Modern Heterogeneous Computing Systemx"
694+
// https://www.diva-portal.org/smash/get/diva2:1786161/FULLTEXT01.pdf
695+
696+
// 1. Convert data-to-be-reduced from float to half
697+
// and place it in a shared memory array
698+
data_to_be_reduced[4*threadIdx.x] = gx;
699+
data_to_be_reduced[4*threadIdx.x + 1] = gy;
700+
data_to_be_reduced[4*threadIdx.x + 2] = gz;
701+
702+
// 2. Perform reduction via tensor units
703+
reduce_via_tensor_units(data_to_be_reduced);
704+
705+
// 3. Retrieve results from shared memory
706+
gx = data_to_be_reduced[0];
707+
gy = data_to_be_reduced[1];
708+
gz = data_to_be_reduced[2];
709+
710+
/* End: Reduction using tensor units */
711+
#else
659712
REDUCEFLOATSUM(gx, pFloatAccumulator);
660713
REDUCEFLOATSUM(gy, pFloatAccumulator);
661714
REDUCEFLOATSUM(gz, pFloatAccumulator);
715+
#endif
662716

663717
global_energy = energy;
664718
#ifndef FLOAT_GRADIENTS

cuda/kernels.cu

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,90 @@ __device__ inline int64_t ullitolli(uint64_t u)
9393
#define ATOMICADDF32(pAccumulator, value) atomicAdd(pAccumulator, (value))
9494
#define ATOMICSUBF32(pAccumulator, value) atomicAdd(pAccumulator, -(value))
9595

96+
#ifdef USE_NVTENSOR
97+
/* Begin: Reduction using tensor units */
98+
99+
// Implementation based on M.Sc. thesis by Gabin Schieffer at KTH:
100+
// "Accelerating a Molecular Docking Application by Leveraging Modern Heterogeneous Computing Systemx"
101+
// https://www.diva-portal.org/smash/get/diva2:1786161/FULLTEXT01.pdf
102+
103+
/*
104+
* WMMA Extension for single precision matmul using Tensor Cores
105+
* and error correction technique (TCEC)
106+
* https://github.com/wmmae/wmma_extension/blob/main/docs/mma_f32.md
107+
*/
108+
#include <wmma_extension/tcec/tcec.hpp>
109+
using tf32 = nvcuda::wmma::precision::tf32;
110+
111+
/*
112+
* Tensor Cores
113+
* https://developer.nvidia.com/blog/programming-tensor-cores-cuda-9
114+
*
115+
* Don't forget to compile specifying the architecture, e.g., sm_86.
116+
* For AutoDock-GPU, this can be done via the TARGETS option.
117+
* make DEVICE=GPU TESTLS=ad NUMWI=64 TARGETS=86 test
118+
* https://stackoverflow.com/a/53634598/1616865
119+
*/
120+
#include <mma.h>
121+
using namespace nvcuda;
122+
123+
#define TILE_SIZE (16 * 16)
124+
125+
constexpr int rowscols_M = 16; // Number of rows (or cols) in the M dimension
126+
constexpr int rowscols_N = 16; // Number of rows (or cols) in the N dimension
127+
constexpr int rowscols_K = 16; // Number of rows (or cols) in the K dimension
128+
129+
__device__ void reduce_via_tensor_units(float *data_to_be_reduced) {
130+
__syncthreads();
131+
132+
if (threadIdx.x <= 31) { // Only one warp performs reduction
133+
__shared__ __align__ (256) float Q_square[TILE_SIZE]; // storage for 16x16 matrix and 4x4 tiles of I4 matrix after
134+
135+
// Declaring and filling fragments - Those are *not* shared
136+
mtk::wmma::tcec::fragment<wmma::matrix_b, rowscols_M, rowscols_N, rowscols_K, tf32, wmma::col_major> frag_P;
137+
mtk::wmma::tcec::fragment<wmma::accumulator, rowscols_M, rowscols_N, rowscols_K, tf32> frag_V;
138+
139+
mtk::wmma::tcec::fragment<wmma::matrix_a, rowscols_M, rowscols_N, rowscols_K, tf32, wmma::col_major> frag_Q;
140+
mtk::wmma::tcec::fragment<wmma::matrix_b, rowscols_M, rowscols_N, rowscols_K, tf32, wmma::col_major> frag_W;
141+
mtk::wmma::tcec::fragment<wmma::accumulator, rowscols_M, rowscols_N, rowscols_K, tf32> frag_C;
142+
143+
mtk::wmma::tcec::fill_fragment(frag_P, 1.0f); // P: only ones
144+
mtk::wmma::tcec::fill_fragment(frag_V, 0.0f); // Output: initialize to zeros
145+
mtk::wmma::tcec::fill_fragment(frag_C, 0.0f); // Final result
146+
147+
// 1. Accumulate the values: V <- AP + V
148+
for(uint i = 0; i < (4 * NUM_OF_THREADS_PER_BLOCK)/TILE_SIZE; i++){
149+
const unsigned int offset = i * TILE_SIZE;
150+
151+
mtk::wmma::tcec::fragment<wmma::matrix_a, rowscols_M, rowscols_N, rowscols_K, tf32, wmma::col_major> frag_A;
152+
mtk::wmma::tcec::load_matrix_sync(frag_A, data_to_be_reduced + offset, 16);
153+
mtk::wmma::tcec::mma_sync(frag_V, frag_A, frag_P, frag_V);
154+
}
155+
156+
// W <- V (required since we need V as a "wmma::matrix_b")
157+
mtk::wmma::tcec::store_matrix_sync(Q_square, frag_V, 16, wmma::mem_col_major);
158+
mtk::wmma::tcec::load_matrix_sync(frag_W, Q_square, 16);
159+
160+
// 2. Perform line sum: C <- QW + C (zero)
161+
// a) create a 4x4 tiled matrix containing 4x4 identity matrix in each tile:
162+
// - TENSOR=ON requires NUMWI to be larger than 32, so the following works and neatly gets rid of an additional function:
163+
const unsigned int k = (threadIdx.x<<3);
164+
const unsigned int kk = 16 - (threadIdx.x>>1);
165+
for(uint i = 0; i < 8; i++) Q_square[k + i] = ((i + kk) & 3) ? 0.0f : 1.0f;
166+
mtk::wmma::tcec::load_matrix_sync(frag_Q, Q_square, 16);
167+
// b) perform sum
168+
mtk::wmma::tcec::mma_sync(frag_C, frag_Q, frag_W, frag_C);
169+
170+
// 3. Store result in shared memory
171+
mtk::wmma::tcec::store_matrix_sync(data_to_be_reduced, frag_C, 16, wmma::mem_col_major);
172+
}
173+
174+
__syncthreads();
175+
}
176+
177+
/* End: Reduction using tensor units */
178+
#endif
179+
96180
#define REDUCEFLOATSUM(value, pAccumulator) \
97181
if (threadIdx.x == 0) \
98182
{ \

host/src/performdocking.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,13 @@ void setup_gpu_for_docking(
450450
cudaDeviceProp props;
451451
RTERROR(cudaGetDevice(&(cData.devnum)),"ERROR in cudaGetDevice:");
452452
RTERROR(cudaGetDeviceProperties(&props,cData.devnum),"ERROR in cudaGetDeviceProperties:");
453+
#ifdef USE_NVTENSOR
454+
if(props.major < 8){
455+
printf("Error: Compute capability 8.0 or higher is needed for tensor core sum reductions.\n");
456+
printf(" Available device %s has compute capability %d.%d.\n", props.name, props.major, props.minor);
457+
exit(-1);
458+
}
459+
#endif
453460
tData.device_name = (char*) malloc(strlen(props.name)+32); // make sure array is large enough to hold device number text too
454461
strcpy(tData.device_name, props.name);
455462
if(gpuCount>1) snprintf(&tData.device_name[strlen(props.name)], strlen(props.name)+32, " (#%d / %d)",cData.devnum+1,gpuCount);

test_cuda.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ if [[ "$4" != "" ]]; then
2626
done
2727
TARGETS="$4"
2828
else
29-
TARGETS=`awk -F'_' '{ if(\$2>50) print \$2 }' <<< "$TARGETS_SUPPORTED" | tr "\n" " "`
29+
TARGETS=`awk -F'_' "{ if(\\$2>=$5) print \\$2 }" <<< "$TARGETS_SUPPORTED" | tr "\n" " "`
3030
fi
3131
printf "Compiling for targets: %s\n" "$TARGETS" >&2
3232
cd "$script_dir"

wmma_extension

Submodule wmma_extension added at 7175b5f

0 commit comments

Comments
 (0)