From 31c5cb55f75851b2550e587ac1171e8a21500ccd Mon Sep 17 00:00:00 2001 From: lindayukeyi Date: Sat, 19 Sep 2020 13:56:36 -0400 Subject: [PATCH 1/6] 'cpu --- stream_compaction/cpu.cu | 38 +++++++++++++++++++++++++-- stream_compaction/naive.cu | 53 +++++++++++++++++++++++++++++++++++++- 2 files changed, 88 insertions(+), 3 deletions(-) diff --git a/stream_compaction/cpu.cu b/stream_compaction/cpu.cu index 719fa11..a316eaf 100644 --- a/stream_compaction/cpu.cu +++ b/stream_compaction/cpu.cu @@ -20,6 +20,10 @@ namespace StreamCompaction { void scan(int n, int *odata, const int *idata) { timer().startCpuTimer(); // TODO + odata[0] = 0; + for (int i = 1; i < n; i++) { + odata[i] = idata[i - 1] + odata[i - 1]; + } timer().endCpuTimer(); } @@ -31,8 +35,14 @@ namespace StreamCompaction { int compactWithoutScan(int n, int *odata, const int *idata) { timer().startCpuTimer(); // TODO + int index = 0; + for (int i = 0; i < n; i++) { + if (idata[i] != 0) { + odata[index++] = idata[i]; + } + } timer().endCpuTimer(); - return -1; + return index; } /** @@ -43,8 +53,32 @@ namespace StreamCompaction { int compactWithScan(int n, int *odata, const int *idata) { timer().startCpuTimer(); // TODO + int* mapdata = new int[n]; + int* scanned = new int[n]; + int count = 0; + + for (int i = 0; i < n; i++) { + if (idata[i] != 0) { + mapdata[i] = 1; + } + else { + mapdata[i] = 0; + } + } + scanned[0] = 0; + for (int i = 1; i < n; i++) { + scanned[i] = scanned[i - 1] + mapdata[i - 1]; + } + + for (int i = 0; i < n; i++) { + if (mapdata[i] == 1) { + odata[scanned[i]] = idata[i]; + count++; + } + } + timer().endCpuTimer(); - return -1; + return count; } } } diff --git a/stream_compaction/naive.cu b/stream_compaction/naive.cu index 4308876..44f7b44 100644 --- a/stream_compaction/naive.cu +++ b/stream_compaction/naive.cu @@ -3,6 +3,8 @@ #include "common.h" #include "naive.h" +#define blockSize 128 + namespace StreamCompaction { namespace Naive { using StreamCompaction::Common::PerformanceTimer; @@ -11,15 +13,64 @@ namespace StreamCompaction { static PerformanceTimer timer; return timer; } + // TODO: __global__ + __global__ void naiveScanParallel(int n, int startIndex, int* idata, int* odata) { + int index = (blockIdx.x * blockDim.x) + threadIdx.x; + if (index >= n) { + return; + } + if (index < startIndex) { + odata[index] = idata[index]; + } + else { + odata[index] = idata[index - startIndex] + idata[index]; + } + } + + // Convert inclusive to exclusive + __global__ void convert(int n, int* idata, int* odata) { + int index = (blockIdx.x * blockDim.x) + threadIdx.x; + if (index >= n) { + return; + } + odata[index] = idata[index - 1]; + } /** * Performs prefix-sum (aka scan) on idata, storing the result into odata. */ void scan(int n, int *odata, const int *idata) { - timer().startGpuTimer(); // TODO + dim3 threadsPerBlock(blockSize); + dim3 fullBlocksPerGrid((n + blockSize - 1) / blockSize); + int* dev_arr1; + int* dev_arr2; + int direction = 1; + + cudaMalloc((void**)&dev_arr1, n * sizeof(int)); + checkCUDAError("dev_arrr1 failed!"); + cudaMalloc((void**)&dev_arr2, n * sizeof(int)); + checkCUDAError("dev_arrr2 failed!"); + + cudaMemcpy(dev_arr1, idata, n * sizeof(int), cudaMemcpyHostToDevice); + timer().startGpuTimer(); + + for (int d = 1; d <= ilog2ceil(n); d++) { + int startIndex = pow(2, d - 1); + if (direction == 1) { + naiveScanParallel << > > (n, startIndex, dev_arr1, dev_arr2); + } + else { + naiveScanParallel << > > (n, startIndex, dev_arr2, dev_arr1); + } + direction *= -1; + } + convert << > > (n, dev_arr1, dev_arr2); timer().endGpuTimer(); + + cudaMemcpy(odata, dev_arr2, n * sizeof(int), cudaMemcpyDeviceToHost); + odata[0] = 0; } } } From 0bb85d0a6621367c904f3a0e7e1c7248c9482836 Mon Sep 17 00:00:00 2001 From: lindayukeyi Date: Sun, 20 Sep 2020 01:55:16 -0400 Subject: [PATCH 2/6] 'efficient' --- README.md | 8 +- src/main.cpp | 1 + stream_compaction/efficient.cu | 176 ++++++++++++++++++++++++++++++++- stream_compaction/naive.cu | 63 +++++++++--- 4 files changed, 232 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 0e38ddb..febe6da 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,10 @@ CUDA Stream Compaction ### (TODO: Your README) -Include analysis, etc. (Remember, this is public, so don't put -anything here that you don't want to share with the world.) +## Debug +1. Navie Scan + +- I ping-pong buffers to keep arr1 as the input data and arr2 as the output data. But I found it inefficient. So I use a flag to denote which one is the input arr at each iteration. +- I need to determine which array is the final result. At first, I thought dev_arr2 will always be the result. But as I change the array size, dev_arr1 and dev_arr2 are both possible answers. + diff --git a/src/main.cpp b/src/main.cpp index 896ac2b..3601484 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -47,6 +47,7 @@ int main(int argc, char* argv[]) { printArray(NPOT, b, true); printCmpResult(NPOT, b, c); + zeroArray(SIZE, c); printDesc("naive scan, power-of-two"); StreamCompaction::Naive::scan(SIZE, c, a); diff --git a/stream_compaction/efficient.cu b/stream_compaction/efficient.cu index 2db346e..d003b1f 100644 --- a/stream_compaction/efficient.cu +++ b/stream_compaction/efficient.cu @@ -3,6 +3,8 @@ #include "common.h" #include "efficient.h" +#define blockSize 128 + namespace StreamCompaction { namespace Efficient { using StreamCompaction::Common::PerformanceTimer; @@ -12,13 +14,153 @@ namespace StreamCompaction { return timer; } + __global__ void kernUpSweep(int n, int d, int* data) { + int thread = (blockIdx.x * blockDim.x) + threadIdx.x; + int step = 1 << (d + 1); + int start = step - 1; + int index = thread * step + start; + + if (index >= n) { + return; + } + data[index] += data[index - step / 2]; + } + + __global__ void kernDownSweep(int n, int d, int* data) { + int thread = (blockIdx.x * blockDim.x) + threadIdx.x; + int power1 = 1 << (d + 1); + int power2 = power1 >> 1; + + thread = thread * power1; + if (thread >= n) { + return; + } + + int right = thread + power1 - 1; + if (right >= n) { + return; + } + int left = thread + power2 - 1; + int t = data[left]; + data[left] = data[right]; + data[right] += t; + } + + __global__ void kernExtendArr(int extendNum, int n, int* idata, int* odata) { + int index = (blockIdx.x * blockDim.x) + threadIdx.x; + if (index >= extendNum) { + return; + } + if (index >= n) { + odata[index] = 0; + } + else { + odata[index] = idata[index]; + } + } + + __global__ void kernSetValue(int n, int value, int* data) { + int index = (blockIdx.x * blockDim.x) + threadIdx.x; + if (index == n) { + data[index] = value; + } + else { + return; + } + } + + /** * Performs prefix-sum (aka scan) on idata, storing the result into odata. */ void scan(int n, int *odata, const int *idata) { - timer().startGpuTimer(); // TODO + int* dev_extend; + int* dev_arr; + + dim3 threadsPerBlock(blockSize); + dim3 fullBlocksPerGrid((n + blockSize - 1) / blockSize); + + // Expand non power-2 to power-2 + int ceil = ilog2ceil(n); + int num = 1 << ceil; + int* extendData = new int[num]; + int* tmp = new int[num]; + + cudaMalloc((void**)&dev_extend, num * sizeof(int)); + checkCUDAError("dev_arrr failed!"); + + cudaMalloc((void**)&dev_arr, n * sizeof(int)); + checkCUDAError("dev_arrr failed!"); + + cudaMemcpy(dev_arr, idata, n * sizeof(int), cudaMemcpyHostToDevice); + + timer().startGpuTimer(); + kernExtendArr<<>>(num, n, dev_arr, dev_extend); + + for (int d = 0; d <= ceil; d++) { + kernUpSweep << > > (num, d, dev_extend); + /* + cudaMemcpy(tmp, dev_extend, num * sizeof(int), cudaMemcpyDeviceToHost); + printf("_________________level %d___________________\n", d); + for (int i = 0; i < num; i++) { + printf("%3d ", tmp[i]); + } + printf("\n"); + */ + } timer().endGpuTimer(); + + kernSetValue << > > (num - 1, 0, dev_extend); + /* + cudaMemcpy(tmp, dev_extend, num * sizeof(int), cudaMemcpyDeviceToHost); + printf("SetValue\n"); + for (int i = 0; i < num; i++) { + printf("%3d ", tmp[i]); + } + printf("\n"); + */ + + for (int d = ceil - 1; d >= 0; d--) { + kernDownSweep << > > (num, d, dev_extend); + /* + cudaMemcpy(tmp, dev_extend, num * sizeof(int), cudaMemcpyDeviceToHost); + printf("_________________level %d___________________\n", d); + for (int i = 0; i < num; i++) { + printf("%3d ", tmp[i]); + } + printf("\n"); + */ + } + + cudaMemcpy(odata, dev_extend, n * sizeof(int), cudaMemcpyDeviceToHost); + + /* + printf("_________________test____________________\n"); + for (int i = 0; i < n; i++) { + printf("%3d ", odata[i]); + } + */ + cudaFree(dev_extend); + cudaFree(dev_arr); + } + + __global__ void kernMap(int n, int* idata, int* odata) { + int index = (blockIdx.x * blockDim.x) + threadIdx.x; + if (index >= n) { + return; + } + odata[index] = idata[index] == 0 ? 0 : 1; + } + + __global__ void kernScatter(int n, int* mapdata, int* scandata, int* idata, int* odata) { + int index = (blockIdx.x * blockDim.x) + threadIdx.x; + if (index >= n) { + return; + } + if (mapdata[index] != 0) { + odata[scandata[index]] = idata[index]; + } } /** @@ -31,8 +173,40 @@ namespace StreamCompaction { * @returns The number of elements remaining after compaction. */ int compact(int n, int *odata, const int *idata) { + int* dev_map; + int* dev_scan; + int* dev_scatter; + int* dev_data; + int* host_map = new int[n]; + + dim3 threadsPerBlock(blockSize); + dim3 fullBlocksPerGrid((n + blockSize - 1) / blockSize); + + cudaMalloc((void**)&dev_map, n * sizeof(int)); + checkCUDAError("dev_arrr failed!"); + + + cudaMalloc((void**)&dev_scan, n * sizeof(int)); + checkCUDAError("dev_arrr failed!"); + + + cudaMalloc((void**)&dev_scatter, n * sizeof(int)); + checkCUDAError("dev_arrr failed!"); + + cudaMalloc((void**)&dev_data, n * sizeof(int)); + checkCUDAError("dev_arrr failed!"); + + cudaMemcpy(dev_data, idata, n * sizeof(int), cudaMemcpyHostToDevice); + timer().startGpuTimer(); // TODO + + // map + kernMap << > > (n, dev_data, dev_map); + + // scan + + // scatter timer().endGpuTimer(); return -1; } diff --git a/stream_compaction/naive.cu b/stream_compaction/naive.cu index 44f7b44..53bc400 100644 --- a/stream_compaction/naive.cu +++ b/stream_compaction/naive.cu @@ -15,16 +15,16 @@ namespace StreamCompaction { } // TODO: __global__ - __global__ void naiveScanParallel(int n, int startIndex, int* idata, int* odata) { + __global__ void naiveScanParallel(int n, int power, int* idata, int* odata) { int index = (blockIdx.x * blockDim.x) + threadIdx.x; if (index >= n) { return; } - if (index < startIndex) { - odata[index] = idata[index]; + if (index >= power) { + odata[index] = idata[index - power] + idata[index]; } else { - odata[index] = idata[index - startIndex] + idata[index]; + odata[index] = idata[index]; } } @@ -34,19 +34,25 @@ namespace StreamCompaction { if (index >= n) { return; } - odata[index] = idata[index - 1]; + if (index == 0) { + odata[index] = 0; + } + else { + odata[index] = idata[index - 1]; + } } /** * Performs prefix-sum (aka scan) on idata, storing the result into odata. */ - void scan(int n, int *odata, const int *idata) { + void scan(int n, int* odata, const int* idata) { // TODO dim3 threadsPerBlock(blockSize); dim3 fullBlocksPerGrid((n + blockSize - 1) / blockSize); int* dev_arr1; int* dev_arr2; int direction = 1; + int* tmp = new int[n]; cudaMalloc((void**)&dev_arr1, n * sizeof(int)); checkCUDAError("dev_arrr1 failed!"); @@ -56,21 +62,52 @@ namespace StreamCompaction { cudaMemcpy(dev_arr1, idata, n * sizeof(int), cudaMemcpyHostToDevice); timer().startGpuTimer(); - for (int d = 1; d <= ilog2ceil(n); d++) { - int startIndex = pow(2, d - 1); + int ceil = ilog2ceil(n); + for (int d = 1; d <= ceil; d++) { + int power = 1 << (d - 1); if (direction == 1) { - naiveScanParallel << > > (n, startIndex, dev_arr1, dev_arr2); + naiveScanParallel << > > (n, power, dev_arr1, dev_arr2); } else { - naiveScanParallel << > > (n, startIndex, dev_arr2, dev_arr1); + naiveScanParallel << > > (n, power, dev_arr2, dev_arr1); + } + /* + printf("level %d \n", d); + for (int i = 0; i < n; i++) { + printf("%3d ", tmp[i]); } + printf("\n"); + */ direction *= -1; } - convert << > > (n, dev_arr1, dev_arr2); + if (direction == 1) { + convert << > > (n, dev_arr1, dev_arr2); + /* + printf("result %d \n"); + for (int i = 0; i < n; i++) { + printf("%3d ", odata[i]); + } + printf("\n"); + */ + } + else { + convert << > > (n, dev_arr2, dev_arr1); + /* + printf("result %d \n"); + for (int i = 0; i < n; i++) { + printf("%3d ", odata[i]); + } + printf("\n"); + */ + } timer().endGpuTimer(); + if (direction == 1) { + cudaMemcpy(odata, dev_arr2, n * sizeof(int), cudaMemcpyDeviceToHost); + } + else { + cudaMemcpy(odata, dev_arr1, n * sizeof(int), cudaMemcpyDeviceToHost); + } - cudaMemcpy(odata, dev_arr2, n * sizeof(int), cudaMemcpyDeviceToHost); - odata[0] = 0; } } } From 20e3089a80ab482311f806e2d02e8b6db378b4e5 Mon Sep 17 00:00:00 2001 From: lindayukeyi Date: Sun, 20 Sep 2020 20:07:29 -0400 Subject: [PATCH 3/6] 'finishcode' --- README.md | 3 + src/main.cpp | 2 +- stream_compaction/efficient.cu | 138 +++++++++++++++++++++------------ stream_compaction/naive.cu | 5 ++ stream_compaction/thrust.cu | 9 ++- 5 files changed, 107 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index febe6da..c06994a 100644 --- a/README.md +++ b/README.md @@ -15,4 +15,7 @@ CUDA Stream Compaction - I ping-pong buffers to keep arr1 as the input data and arr2 as the output data. But I found it inefficient. So I use a flag to denote which one is the input arr at each iteration. - I need to determine which array is the final result. At first, I thought dev_arr2 will always be the result. But as I change the array size, dev_arr1 and dev_arr2 are both possible answers. +2. Efficient scan: My program works well with array size < 16 but crashed with array size >= 16. Error: Illigal memory +- Bug1: I forget to free the device buffers. +- Bug2(Main problem): Firstly, I try to avoid mod operations when implementing the kernUpSweep and kernDownSweep. Unluckily, it looks like my implementation has problems. So I still use mod operation to determine whether the current thread should do the calculations. diff --git a/src/main.cpp b/src/main.cpp index 3601484..31da647 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -13,7 +13,7 @@ #include #include "testing_helpers.hpp" -const int SIZE = 1 << 8; // feel free to change the size of array +const int SIZE = 1 << 25; // feel free to change the size of array const int NPOT = SIZE - 3; // Non-Power-Of-Two int *a = new int[SIZE]; int *b = new int[SIZE]; diff --git a/stream_compaction/efficient.cu b/stream_compaction/efficient.cu index d003b1f..1cfc771 100644 --- a/stream_compaction/efficient.cu +++ b/stream_compaction/efficient.cu @@ -8,6 +8,13 @@ namespace StreamCompaction { namespace Efficient { using StreamCompaction::Common::PerformanceTimer; + int* dev_extend; + + int* dev_map; + int* dev_scan; + int* dev_scatter; + int* dev_data; + PerformanceTimer& timer() { static PerformanceTimer timer; @@ -15,35 +22,32 @@ namespace StreamCompaction { } __global__ void kernUpSweep(int n, int d, int* data) { - int thread = (blockIdx.x * blockDim.x) + threadIdx.x; - int step = 1 << (d + 1); - int start = step - 1; - int index = thread * step + start; - + int index = (blockIdx.x * blockDim.x) + threadIdx.x; if (index >= n) { return; } - data[index] += data[index - step / 2]; + + int power = 1 << (d + 1); + int step = 1 << d; + if (index != 0 && (index + 1) % power == 0) { + data[index] += data[index - step]; + } } __global__ void kernDownSweep(int n, int d, int* data) { - int thread = (blockIdx.x * blockDim.x) + threadIdx.x; - int power1 = 1 << (d + 1); - int power2 = power1 >> 1; - - thread = thread * power1; - if (thread >= n) { + int index = (blockIdx.x * blockDim.x) + threadIdx.x; + if (index >= n) { return; } - int right = thread + power1 - 1; - if (right >= n) { - return; + int power = 1 << (d + 1); + int step = power >> 1; + + if (index != 0 && (index + 1) % power == 0) { + int t = data[index - step]; + data[index - step] = data[index]; + data[index] += t; } - int left = thread + power2 - 1; - int t = data[left]; - data[left] = data[right]; - data[right] += t; } __global__ void kernExtendArr(int extendNum, int n, int* idata, int* odata) { @@ -69,14 +73,21 @@ namespace StreamCompaction { } } + __global__ void kernScatter(int n, int* idata, int* map, int* scan, int* odata) { + int index = (blockIdx.x * blockDim.x) + threadIdx.x; + if (index >= n) { + return; + } + if (map[index] != 0) { + odata[scan[index]] = idata[index]; + } + } /** * Performs prefix-sum (aka scan) on idata, storing the result into odata. */ void scan(int n, int *odata, const int *idata) { // TODO - int* dev_extend; - int* dev_arr; dim3 threadsPerBlock(blockSize); dim3 fullBlocksPerGrid((n + blockSize - 1) / blockSize); @@ -90,13 +101,13 @@ namespace StreamCompaction { cudaMalloc((void**)&dev_extend, num * sizeof(int)); checkCUDAError("dev_arrr failed!"); - cudaMalloc((void**)&dev_arr, n * sizeof(int)); + cudaMalloc((void**)&dev_data, n * sizeof(int)); checkCUDAError("dev_arrr failed!"); - cudaMemcpy(dev_arr, idata, n * sizeof(int), cudaMemcpyHostToDevice); + cudaMemcpy(dev_data, idata, n * sizeof(int), cudaMemcpyHostToDevice); timer().startGpuTimer(); - kernExtendArr<<>>(num, n, dev_arr, dev_extend); + kernExtendArr<<>>(num, n, dev_data, dev_extend); for (int d = 0; d <= ceil; d++) { kernUpSweep << > > (num, d, dev_extend); @@ -142,7 +153,10 @@ namespace StreamCompaction { } */ cudaFree(dev_extend); - cudaFree(dev_arr); + cudaFree(dev_data); + + delete[] tmp; + delete[] extendData; } __global__ void kernMap(int n, int* idata, int* odata) { @@ -153,15 +167,6 @@ namespace StreamCompaction { odata[index] = idata[index] == 0 ? 0 : 1; } - __global__ void kernScatter(int n, int* mapdata, int* scandata, int* idata, int* odata) { - int index = (blockIdx.x * blockDim.x) + threadIdx.x; - if (index >= n) { - return; - } - if (mapdata[index] != 0) { - odata[scandata[index]] = idata[index]; - } - } /** * Performs stream compaction on idata, storing the result into odata. @@ -173,42 +178,79 @@ namespace StreamCompaction { * @returns The number of elements remaining after compaction. */ int compact(int n, int *odata, const int *idata) { - int* dev_map; - int* dev_scan; - int* dev_scatter; - int* dev_data; - int* host_map = new int[n]; + + + int ceil = ilog2ceil(n); + int num = 1 << ceil; + + int* host_scan = new int[num]; + int* tmp = new int[num]; dim3 threadsPerBlock(blockSize); dim3 fullBlocksPerGrid((n + blockSize - 1) / blockSize); - cudaMalloc((void**)&dev_map, n * sizeof(int)); - checkCUDAError("dev_arrr failed!"); + cudaMalloc((void**)&dev_map, num * sizeof(int)); + checkCUDAError("dev_map failed!"); - cudaMalloc((void**)&dev_scan, n * sizeof(int)); - checkCUDAError("dev_arrr failed!"); + cudaMalloc((void**)&dev_scan, num * sizeof(int)); + checkCUDAError("dev_scan failed!"); - cudaMalloc((void**)&dev_scatter, n * sizeof(int)); - checkCUDAError("dev_arrr failed!"); + + cudaMalloc((void**)&dev_scatter, num * sizeof(int)); + checkCUDAError("dev_scatter failed!"); cudaMalloc((void**)&dev_data, n * sizeof(int)); - checkCUDAError("dev_arrr failed!"); + checkCUDAError("dev_data failed!"); + + cudaMalloc((void**)&dev_extend, num * sizeof(int)); + checkCUDAError("dev_extend failed!"); cudaMemcpy(dev_data, idata, n * sizeof(int), cudaMemcpyHostToDevice); timer().startGpuTimer(); // TODO + // Extend non-power of 2 + kernExtendArr << > > (num, n, dev_data, dev_extend); // map - kernMap << > > (n, dev_data, dev_map); + kernMap << > > (num, dev_extend, dev_map); + cudaMemcpy(dev_scan, dev_map, num * sizeof(int), cudaMemcpyDeviceToDevice); // scan + for (int d = 0; d <= ceil; d++) { + kernUpSweep << > > (num, d, dev_scan); + } + + kernSetValue << > > (num - 1, 0, dev_scan); + + for (int d = ceil - 1; d >= 0; d--) { + kernDownSweep << > > (num, d, dev_scan); + } // scatter + kernScatter << > > (num, dev_extend, dev_map, dev_scan, dev_scatter); + + cudaMemcpy(odata, dev_scatter, n * sizeof(int), cudaMemcpyDeviceToHost); + cudaMemcpy(host_scan, dev_scan, num * sizeof(int), cudaMemcpyDeviceToHost); + timer().endGpuTimer(); - return -1; + + cudaFree(dev_extend); + cudaFree(dev_data); + cudaFree(dev_map); + cudaFree(dev_scan); + cudaFree(dev_scatter); + + int count = host_scan[n - 1]; + if (1 << ceil != n) { + count = host_scan[n]; + } + delete[] host_scan; + delete[] tmp; + + return count; } } } diff --git a/stream_compaction/naive.cu b/stream_compaction/naive.cu index 53bc400..47a493d 100644 --- a/stream_compaction/naive.cu +++ b/stream_compaction/naive.cu @@ -108,6 +108,11 @@ namespace StreamCompaction { cudaMemcpy(odata, dev_arr1, n * sizeof(int), cudaMemcpyDeviceToHost); } + cudaFree(dev_arr1); + cudaFree(dev_arr2); + + delete[] tmp; + } } } diff --git a/stream_compaction/thrust.cu b/stream_compaction/thrust.cu index 1def45e..7b9692b 100644 --- a/stream_compaction/thrust.cu +++ b/stream_compaction/thrust.cu @@ -6,6 +6,8 @@ #include "common.h" #include "thrust.h" +#define blockSize 128 + namespace StreamCompaction { namespace Thrust { using StreamCompaction::Common::PerformanceTimer; @@ -14,6 +16,7 @@ namespace StreamCompaction { static PerformanceTimer timer; return timer; } + /** * Performs prefix-sum (aka scan) on idata, storing the result into odata. */ @@ -21,7 +24,11 @@ namespace StreamCompaction { timer().startGpuTimer(); // TODO use `thrust::exclusive_scan` // example: for device_vectors dv_in and dv_out: - // thrust::exclusive_scan(dv_in.begin(), dv_in.end(), dv_out.begin()); + // thrust::exclusive_scan(dv_in.begin(), dv_in.end(), dv_out.begin + thrust::exclusive_scan(idata, idata + n, odata); + for (int i = 0; i < n; i++) { + //printf("%d ", odata[i]); + } timer().endGpuTimer(); } } From 3adedeafd56b5716040475b7e41d33cae6936be0 Mon Sep 17 00:00:00 2001 From: lindayukeyi Date: Wed, 23 Sep 2020 02:29:30 -0400 Subject: [PATCH 4/6] 'readme' --- README.md | 180 +++++++++++++++++++++++++- img/downsweep.png | Bin 0 -> 33455 bytes img/naivescan.png | Bin 0 -> 37927 bytes img/performance-non-smaller.png | Bin 0 -> 12886 bytes img/performance-smaller.png | Bin 0 -> 13623 bytes img/performance-vs-blocksize.png | Bin 0 -> 16401 bytes img/performance-vs-size-non-power.png | Bin 0 -> 12880 bytes img/performance-vs-size-power.png | Bin 0 -> 12523 bytes img/pot-vs-npot.png | Bin 0 -> 8590 bytes img/upsweep.png | Bin 0 -> 36788 bytes src/main.cpp | 2 +- stream_compaction/common.h | 1 + stream_compaction/efficient.cu | 43 ++---- stream_compaction/naive.cu | 1 - stream_compaction/thrust.cu | 26 +++- 15 files changed, 208 insertions(+), 45 deletions(-) create mode 100644 img/downsweep.png create mode 100644 img/naivescan.png create mode 100644 img/performance-non-smaller.png create mode 100644 img/performance-smaller.png create mode 100644 img/performance-vs-blocksize.png create mode 100644 img/performance-vs-size-non-power.png create mode 100644 img/performance-vs-size-power.png create mode 100644 img/pot-vs-npot.png create mode 100644 img/upsweep.png diff --git a/README.md b/README.md index c06994a..611089e 100644 --- a/README.md +++ b/README.md @@ -3,11 +3,181 @@ CUDA Stream Compaction **University of Pennsylvania, CIS 565: GPU Programming and Architecture, Project 2** -* (TODO) YOUR NAME HERE - * (TODO) [LinkedIn](), [personal website](), [twitter](), etc. -* Tested on: (TODO) Windows 22, i7-2222 @ 2.22GHz 22GB, GTX 222 222MB (Moore 2222 Lab) +* Keyi Yu + * [LinkedIn](https://www.linkedin.com/in/keyi-linda-yu-8b1178137/) +* Tested on: Windows 10, i7-10750H @ 2.60GHz 16GB, GeForce RTX 2070 with Max-Q 8228MB (My Machine) + +Project2-Stream Compaction +===================================== + +In this project, I implemented Scan(Prefix Sum) in both CPU and GPU versions, and GPU stream compaction in CUDA from scratch. Then I did some tests on both power-of-two-sized arrays and non-power-of-two-sized arrays. These algorithms are widely used and will be important for accelerating path tracer later on. +![](images/1.gif) + +Contects +------------------------------------- +- [Introduction](#Introduction) +- [Scan](#Scan) + - [Method1: CPU](#Method1:-CPU) + - [Method2: Naive](#Method2:-Naive-GPU) + - [Method3: Work Efficient](#Method3:-Work-Efficient-GPU) +- [Stream Compaction](#Stream-Compaction) + - [Method1: CPU](#Method1:-CPU) + - [Method2: Work Efficient](#Method3:-Work-Efficient-GPU) +- [Performance Analysis](#Performance-Analysis) +- [Debug](#Debug) +- [Questions](#Questions) +- [Optimization](#Optimization) +- [Reference](#Reference) + +Introduction +------------------------------------- +A simple and common parallel algorithm building block is the all-prefix-sums operation. The all-prefix-sums operation on an array of data is commonly known as scan. There are many uses for scan, including sorting, lexical analysis, string comparison and etc. (Reference from [NVIDIA GPU Gem3](https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch39.html)) In this project, I implemented stream compaction with the scan operation. + +Scan +------------------------------------- +Scan has two types: exlusive scan and inclusive scan. An exlusive scan can be generated from inclusive scan by shifting the array right by one element and inerting the identity. In this project, I implemented exclusive scan. + +### Method1: CPU +The idea of implementing scan on CPU is very straightforward. I used a for loop to iterate the array and add each entry to the previous sum. The prefixSum represents the sum of the previous i entries. So prefixSum[0] = 0. In this way, the result of the prefixSum is the exclusive scan. The pesudo code is: +``` +for each entry arr[i] in arr from [0, n - 1]: + prefixSum[i] = arr[i - 1] + prefixSum[i - 1] +``` + +### Method2: Navie GPU +This method is similar to parallel reduction. This algorithm is based on the scan algorithm presented by Hillis and Steele (1986) and demonstrated for GPUs by Horn (2005). Figure below shows the procedure. + +We can notice that this method performs O(nlogn) addition operations. Recall that Method1 only performs O(n) addition operaions. Therefore, this naive scan is not work-efficient. + +Another problem of the peusdo code shown in the picture is that in-place algorithm doesn't work if the array size is larger than the warp size(warp size is 32 in G80). So we have to use two arrays: input array and output array. At each iteration, we will do the add operation and store the results in the output array. In the next iteration, the previous output array will be the input array and so on and so forth. Peusdo code in [Example 2](#img/example-2.jpg) shows this idea. + +![](img/naivescan.png) +![naivescan](img/example-2.jpg) + +### Method3: Work Efficient GPU +This algorithm is based on the one presented by Blelloch (1990). The idea is to build a balanced binary tree on the input data and sweep it to and from the root to compute the prefix sum. + +Step1: Up Sweep +![](img/upsweep.png) + +Step2: Down Sweep +![](img/downsweep.png) + +Stream Compaction +------------------------------------- +### Method1: CPU +This method is to iterate the array and select those we want. +``` +for each entry arr[i] from arr in range[0, n - 1]: + if arr[i] == 1 + Add it to the result arr + else + do nothing +``` +### Method2: Work-efficient GPU +With the work-efficient scan implemented above, it is easy to implement this algorithm. This algoeithm has three steps: + +1. In parallel, determine whether each entry is 0 or not. +``` +if entry arr[i] == 0: + map[i] = 0; +else: + map[i] = 1; +``` + +2. Use work-efficient scan on the map array in step1 to get exclusive scan result. +3. In parallel, write the non-zero entry i to the output array with index map[i] + +## Performance Analysis and Questions +### Roughly optimize the block sizes of each of your implementations for minimal run time on your GPU. +I changed the blocksize when the array size was 2^20. The figure below shows the performance of three algorithms with different blocksize. As we can see, when the blocksize is 128, 256, and 512, the three methods all perform better. So I choose 128 as the blocksize for my following analysis. + +![](img/performance-vs-blocksize.png) + +### Compare all of these GPU Scan implementations (Naive, Work-Efficient, and Thrust) to the serial CPU version of Scan. Plot a graph of the comparison (with array size on the independent axis). +Here are some graphs and output showing the performance of the four methods mentioned above. +1. Performance vs Array size (Power-of-Two) + +Large range | Small range +:-------------------------:|:-------------------------: +![](img/performance-vs-size-power.png) | ![](img/performance-smaller.png) + + +2. Performance vs Array size (Non-Power-of-Two) + +Large range | Small range +:-------------------------:|:-------------------------: +![](img/performance-vs-size-non-power.png) | ![](img/performance-non-smaller.png) + +The array size greatly affected the performance of the four algorithms. When the data size is small(i.e. 256 or 65536), CPU performs better than the GPU algorithms. However, as the size of the array increases, GPU performs better than(up to 10 times) the CPU. + +I read the Nsight performance analysis and noticed that there is still memory transfer from host to device or device to host. As the size of the array increases, CPU costs much more time to compute and thus those memory transfer time is negligible. Also there is a cudaCreateEvent call, which may cost some time. I think these time cost would be the bottleneck of the gpu algorithms, even for the thrust libaray implementation. + +3. POT vs NPOT + +![](img/POT-VS-NPOT.png) + +The figure shows the performance of thrust::exclusive_scan on Power-of-Two array and Non-Power-of-Two array. The result is expected because we need to pad 0 to the NPOT before UpSweep operation, which will cost extra time and memory. I guess the reason why the performance is quite close is that the size of the NPOT array is slightly smaller that the size of POT array. + + +4. Output in CMD +``` +**************** +** SCAN TESTS ** +**************** + [ 0 30 45 48 26 27 21 49 46 22 34 10 47 ... 17 0 ] +==== cpu scan, power-of-two ==== + elapsed time: 4.3786ms (std::chrono Measured) + [ 0 0 30 75 123 149 176 197 246 292 314 348 358 ... 25686177 25686194 ] +==== cpu scan, non-power-of-two ==== + elapsed time: 1.4756ms (std::chrono Measured) + [ 0 0 30 75 123 149 176 197 246 292 314 348 358 ... 25686095 25686126 ] + passed +==== naive scan, power-of-two ==== + elapsed time: 0.7624ms (CUDA Measured) + passed +==== naive scan, non-power-of-two ==== + elapsed time: 0.76464ms (CUDA Measured) + passed +==== work-efficient scan, power-of-two ==== + elapsed time: 1.03267ms (CUDA Measured) + passed +==== work-efficient scan, non-power-of-two ==== + elapsed time: 1.03699ms (CUDA Measured) + passed +==== thrust scan, power-of-two ==== + elapsed time: 0.196608ms (CUDA Measured) + passed +==== thrust scan, non-power-of-two ==== + elapsed time: 0.211552ms (CUDA Measured) + passed + +***************************** +** STREAM COMPACTION TESTS ** +***************************** + [ 2 2 1 0 0 1 1 3 0 0 2 0 1 ... 3 0 ] +==== cpu compact without scan, power-of-two ==== + elapsed time: 2.3306ms (std::chrono Measured) + [ 2 2 1 1 1 3 2 1 1 2 3 2 2 ... 1 3 ] + passed +==== cpu compact without scan, non-power-of-two ==== + elapsed time: 2.3035ms (std::chrono Measured) + [ 2 2 1 1 1 3 2 1 1 2 3 2 2 ... 1 1 ] + passed +==== cpu compact with scan ==== + elapsed time: 9.6631ms (std::chrono Measured) + [ 2 2 1 1 1 3 2 1 1 2 3 2 2 ... 1 3 ] + passed +==== work-efficient compact, power-of-two ==== + elapsed time: 1.14995ms (CUDA Measured) + passed +==== work-efficient compact, non-power-of-two ==== + elapsed time: 1.15507ms (CUDA Measured) + passed +``` + + -### (TODO: Your README) ## Debug 1. Navie Scan @@ -17,5 +187,5 @@ CUDA Stream Compaction 2. Efficient scan: My program works well with array size < 16 but crashed with array size >= 16. Error: Illigal memory - Bug1: I forget to free the device buffers. -- Bug2(Main problem): Firstly, I try to avoid mod operations when implementing the kernUpSweep and kernDownSweep. Unluckily, it looks like my implementation has problems. So I still use mod operation to determine whether the current thread should do the calculations. +- Bug2(Main problem): Firstly, I try to avoid module operations when implementing the kernUpSweep and kernDownSweep. Unluckily, it looks like my implementation has problems. So I still use mod operation to determine whether the current thread should do the calculations. diff --git a/img/downsweep.png b/img/downsweep.png new file mode 100644 index 0000000000000000000000000000000000000000..a1944c4f6a98209652855960fd687344efbbb863 GIT binary patch literal 33455 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 literal 0 HcmV?d00001 diff --git a/img/naivescan.png b/img/naivescan.png new file mode 100644 index 0000000000000000000000000000000000000000..f45f2d079c3227f4df6a4d0275ff9b8392813fab GIT binary patch literal 37927 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 Reo#|$J?3$=_VAg1{x3*rW;Xx; literal 0 HcmV?d00001 diff --git a/img/performance-non-smaller.png b/img/performance-non-smaller.png new file mode 100644 index 0000000000000000000000000000000000000000..d97b0bcce50093d482475f68430dada3251ae9a2 GIT binary patch literal 12886 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 c@wv=?+F*v(%SO5S3 literal 0 HcmV?d00001 diff --git a/img/performance-smaller.png b/img/performance-smaller.png new file mode 100644 index 0000000000000000000000000000000000000000..5469ae1e2792fecb07801e7c4d4d8b6abcb48754 GIT binary patch literal 13623 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 FKL9*s**yRN literal 0 HcmV?d00001 diff --git a/img/performance-vs-blocksize.png b/img/performance-vs-blocksize.png new file mode 100644 index 0000000000000000000000000000000000000000..c04088b59886fa5650d9a1b2de92cacdea85928f GIT binary patch literal 16401 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 W?OI>$Lcsq}09Q;bjjN%zAN~i&2DF&~ literal 0 HcmV?d00001 diff --git a/img/upsweep.png b/img/upsweep.png new file mode 100644 index 0000000000000000000000000000000000000000..d92cba8342fce386844c1a7ba7c90077dedf7d3a GIT binary patch literal 36788 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 literal 0 HcmV?d00001 diff --git a/src/main.cpp b/src/main.cpp index 31da647..81f4b55 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -13,7 +13,7 @@ #include #include "testing_helpers.hpp" -const int SIZE = 1 << 25; // feel free to change the size of array +const int SIZE = 1 << 24; // feel free to change the size of array const int NPOT = SIZE - 3; // Non-Power-Of-Two int *a = new int[SIZE]; int *b = new int[SIZE]; diff --git a/stream_compaction/common.h b/stream_compaction/common.h index d2c1fed..eb1daf2 100644 --- a/stream_compaction/common.h +++ b/stream_compaction/common.h @@ -12,6 +12,7 @@ #define FILENAME (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) #define checkCUDAError(msg) checkCUDAErrorFn(msg, FILENAME, __LINE__) +#define blockSize 128 /** * Check for CUDA errors; print and exit if there was a problem. diff --git a/stream_compaction/efficient.cu b/stream_compaction/efficient.cu index 1cfc771..6d060e2 100644 --- a/stream_compaction/efficient.cu +++ b/stream_compaction/efficient.cu @@ -3,7 +3,6 @@ #include "common.h" #include "efficient.h" -#define blockSize 128 namespace StreamCompaction { namespace Efficient { @@ -73,12 +72,12 @@ namespace StreamCompaction { } } - __global__ void kernScatter(int n, int* idata, int* map, int* scan, int* odata) { + __global__ void kernScatter(int n, int* idata, int* scan, int* odata) { int index = (blockIdx.x * blockDim.x) + threadIdx.x; if (index >= n) { return; } - if (map[index] != 0) { + if (idata[index] != 0) { odata[scan[index]] = idata[index]; } } @@ -107,44 +106,23 @@ namespace StreamCompaction { cudaMemcpy(dev_data, idata, n * sizeof(int), cudaMemcpyHostToDevice); timer().startGpuTimer(); - kernExtendArr<<>>(num, n, dev_data, dev_extend); + + kernExtendArr << > > (num, n, dev_data, dev_extend); for (int d = 0; d <= ceil; d++) { kernUpSweep << > > (num, d, dev_extend); - /* - cudaMemcpy(tmp, dev_extend, num * sizeof(int), cudaMemcpyDeviceToHost); - printf("_________________level %d___________________\n", d); - for (int i = 0; i < num; i++) { - printf("%3d ", tmp[i]); - } - printf("\n"); - */ } - timer().endGpuTimer(); kernSetValue << > > (num - 1, 0, dev_extend); - /* - cudaMemcpy(tmp, dev_extend, num * sizeof(int), cudaMemcpyDeviceToHost); - printf("SetValue\n"); - for (int i = 0; i < num; i++) { - printf("%3d ", tmp[i]); - } - printf("\n"); - */ for (int d = ceil - 1; d >= 0; d--) { kernDownSweep << > > (num, d, dev_extend); - /* - cudaMemcpy(tmp, dev_extend, num * sizeof(int), cudaMemcpyDeviceToHost); - printf("_________________level %d___________________\n", d); - for (int i = 0; i < num; i++) { - printf("%3d ", tmp[i]); - } - printf("\n"); - */ } + timer().endGpuTimer(); cudaMemcpy(odata, dev_extend, n * sizeof(int), cudaMemcpyDeviceToHost); + + /* printf("_________________test____________________\n"); @@ -215,8 +193,7 @@ namespace StreamCompaction { kernExtendArr << > > (num, n, dev_data, dev_extend); // map - kernMap << > > (num, dev_extend, dev_map); - cudaMemcpy(dev_scan, dev_map, num * sizeof(int), cudaMemcpyDeviceToDevice); + kernMap << > > (num, dev_extend, dev_scan); // scan for (int d = 0; d <= ceil; d++) { @@ -230,12 +207,12 @@ namespace StreamCompaction { kernDownSweep << > > (num, d, dev_scan); } // scatter - kernScatter << > > (num, dev_extend, dev_map, dev_scan, dev_scatter); + kernScatter << > > (num, dev_extend, dev_scan, dev_scatter); + timer().endGpuTimer(); cudaMemcpy(odata, dev_scatter, n * sizeof(int), cudaMemcpyDeviceToHost); cudaMemcpy(host_scan, dev_scan, num * sizeof(int), cudaMemcpyDeviceToHost); - timer().endGpuTimer(); cudaFree(dev_extend); cudaFree(dev_data); diff --git a/stream_compaction/naive.cu b/stream_compaction/naive.cu index 47a493d..f00cfd4 100644 --- a/stream_compaction/naive.cu +++ b/stream_compaction/naive.cu @@ -3,7 +3,6 @@ #include "common.h" #include "naive.h" -#define blockSize 128 namespace StreamCompaction { namespace Naive { diff --git a/stream_compaction/thrust.cu b/stream_compaction/thrust.cu index 7b9692b..cc0b964 100644 --- a/stream_compaction/thrust.cu +++ b/stream_compaction/thrust.cu @@ -6,8 +6,6 @@ #include "common.h" #include "thrust.h" -#define blockSize 128 - namespace StreamCompaction { namespace Thrust { using StreamCompaction::Common::PerformanceTimer; @@ -21,15 +19,33 @@ namespace StreamCompaction { * Performs prefix-sum (aka scan) on idata, storing the result into odata. */ void scan(int n, int *odata, const int *idata) { - timer().startGpuTimer(); // TODO use `thrust::exclusive_scan` // example: for device_vectors dv_in and dv_out: // thrust::exclusive_scan(dv_in.begin(), dv_in.end(), dv_out.begin - thrust::exclusive_scan(idata, idata + n, odata); + + //thrust::exclusive_scan(idata, idata + n, odata); + + int* dev_idata, * dev_odata; + cudaMalloc((void**)&dev_idata, n * sizeof(int)); + cudaMalloc((void**)&dev_odata, n * sizeof(int)); + + cudaMemcpy(dev_idata, idata, n * sizeof(int), cudaMemcpyHostToDevice); + + + thrust::device_ptr dev_thrust_odata(dev_odata); + thrust::device_ptr dev_thrust_idata(dev_idata); + + timer().startGpuTimer(); + thrust::exclusive_scan(dev_thrust_idata, dev_thrust_idata + n, dev_thrust_odata); + + timer().endGpuTimer(); + + cudaMemcpy(odata, thrust::raw_pointer_cast(dev_thrust_odata), n * sizeof(int), cudaMemcpyDeviceToHost); + /* for (int i = 0; i < n; i++) { //printf("%d ", odata[i]); } - timer().endGpuTimer(); + */ } } } From 8e1f20b2c1ba518be52b6a767f0960d9a31973a1 Mon Sep 17 00:00:00 2001 From: lindayukeyi Date: Wed, 23 Sep 2020 15:54:15 -0400 Subject: [PATCH 5/6] 'ec1' --- README.md | 2 +- src/main.cpp | 16 ++++++ stream_compaction/efficient.cu | 101 +++++++++++++++++++++++++++++++-- stream_compaction/efficient.h | 2 + 4 files changed, 115 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 611089e..a118cbb 100644 --- a/README.md +++ b/README.md @@ -115,7 +115,7 @@ I read the Nsight performance analysis and noticed that there is still memory tr 3. POT vs NPOT -![](img/POT-VS-NPOT.png) +![](img/pot-vs-npot.png) The figure shows the performance of thrust::exclusive_scan on Power-of-Two array and Non-Power-of-Two array. The result is expected because we need to pad 0 to the NPOT before UpSweep operation, which will cost extra time and memory. I guess the reason why the performance is quite close is that the size of the NPOT array is slightly smaller that the size of POT array. diff --git a/src/main.cpp b/src/main.cpp index 81f4b55..e69ab2e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -96,6 +96,22 @@ int main(int argc, char* argv[]) { //printArray(NPOT, c, true); printCmpResult(NPOT, b, c); + zeroArray(SIZE, c); + printDesc("work-efficient scan optimized, power-of-two"); + StreamCompaction::Efficient::scanOptimized(SIZE, c, a); + printElapsedTime(StreamCompaction::Efficient::timer().getGpuElapsedTimeForPreviousOperation(), "(CUDA Measured)"); + //printArray(SIZE, c, true); + printCmpResult(SIZE, b, c); + + zeroArray(SIZE, c); + printDesc("work-efficient scan optimized, non-power-of-two"); + StreamCompaction::Efficient::scanOptimized(NPOT, c, a); + printElapsedTime(StreamCompaction::Efficient::timer().getGpuElapsedTimeForPreviousOperation(), "(CUDA Measured)"); + //printArray(NPOT, c, true); + printCmpResult(NPOT, b, c); + + + printf("\n"); printf("*****************************\n"); printf("** STREAM COMPACTION TESTS **\n"); diff --git a/stream_compaction/efficient.cu b/stream_compaction/efficient.cu index 6d060e2..37d2dfb 100644 --- a/stream_compaction/efficient.cu +++ b/stream_compaction/efficient.cu @@ -33,6 +33,18 @@ namespace StreamCompaction { } } + __global__ void kernUpSweepOptimized(int n, int stride, int* data, int start) { + int k = (blockIdx.x * blockDim.x) + threadIdx.x; + if (k >= n) { + return; + } + + int index = k * stride + start; + data[index] += data[index - stride / 2]; + + + } + __global__ void kernDownSweep(int n, int d, int* data) { int index = (blockIdx.x * blockDim.x) + threadIdx.x; if (index >= n) { @@ -49,6 +61,21 @@ namespace StreamCompaction { } } + __global__ void kernDownSweepOptimized(int n, int stride, int* data, int start) { + int k = (blockIdx.x * blockDim.x) + threadIdx.x; + if (k >= n) { + return; + } + + int index = start + k * stride; + int power = stride; + if (index != 0 && (index + 1) % power == 0) { + int t = data[index - stride / 2]; + data[index - stride / 2] = data[index]; + data[index] += t; + } + } + __global__ void kernExtendArr(int extendNum, int n, int* idata, int* odata) { int index = (blockIdx.x * blockDim.x) + threadIdx.x; if (index >= extendNum) { @@ -62,6 +89,14 @@ namespace StreamCompaction { } } + __global__ void kernMap(int n, int* idata, int* odata) { + int index = (blockIdx.x * blockDim.x) + threadIdx.x; + if (index >= n) { + return; + } + odata[index] = idata[index] == 0 ? 0 : 1; + } + __global__ void kernSetValue(int n, int value, int* data) { int index = (blockIdx.x * blockDim.x) + threadIdx.x; if (index == n) { @@ -72,6 +107,14 @@ namespace StreamCompaction { } } + __global__ void kernSetValueOptimized(int n, int value, int* data, int stride) { + int index = (blockIdx.x * blockDim.x) + threadIdx.x; + if (index >= n) { + return; + } + data[index + stride - 1] = value; + } + __global__ void kernScatter(int n, int* idata, int* scan, int* odata) { int index = (blockIdx.x * blockDim.x) + threadIdx.x; if (index >= n) { @@ -82,6 +125,7 @@ namespace StreamCompaction { } } + /** * Performs prefix-sum (aka scan) on idata, storing the result into odata. */ @@ -137,12 +181,59 @@ namespace StreamCompaction { delete[] extendData; } - __global__ void kernMap(int n, int* idata, int* odata) { - int index = (blockIdx.x * blockDim.x) + threadIdx.x; - if (index >= n) { - return; + void scanOptimized(int n, int* odata, const int* idata) { + dim3 threadsPerBlock(blockSize); + + + // Expand non power-2 to power-2 + int ceil = ilog2ceil(n); + int num = 1 << ceil; + int* extendData = new int[num]; + int* tmp = new int[num]; + + cudaMalloc((void**)&dev_extend, num * sizeof(int)); + checkCUDAError("dev_arrr failed!"); + + cudaMalloc((void**)&dev_data, n * sizeof(int)); + checkCUDAError("dev_arrr failed!"); + + cudaMemcpy(dev_data, idata, n * sizeof(int), cudaMemcpyHostToDevice); + + + dim3 fullBlocksPerGrid((num + blockSize - 1) / blockSize); + + timer().startGpuTimer(); + + kernExtendArr << > > (num, n, dev_data, dev_extend); + + for (int d = 1; d <= ceil; d++) { + int threadNum = 1 << (ceil - d); + int stride = 1 << d; + int start = stride - 1; + fullBlocksPerGrid = (threadNum + blockSize - 1) / blockSize; + + kernUpSweepOptimized << > > (threadNum, stride, dev_extend, start); } - odata[index] = idata[index] == 0 ? 0 : 1; + + kernSetValueOptimized << <1, threadsPerBlock >> > (1, 0, dev_extend, num); + + for (int d = ceil - 1; d >= 0; d--) { + int threadNum = 1 << (ceil - d - 1); + int stride = 1 << (d + 1); + int start = stride - 1; + fullBlocksPerGrid = (threadNum + blockSize - 1) / blockSize; + + kernDownSweepOptimized << > > (threadNum, stride, dev_extend, start); + } + timer().endGpuTimer(); + + cudaMemcpy(odata, dev_extend, n * sizeof(int), cudaMemcpyDeviceToHost); + + cudaFree(dev_extend); + cudaFree(dev_data); + + delete[] tmp; + delete[] extendData; } diff --git a/stream_compaction/efficient.h b/stream_compaction/efficient.h index 803cb4f..d1db131 100644 --- a/stream_compaction/efficient.h +++ b/stream_compaction/efficient.h @@ -8,6 +8,8 @@ namespace StreamCompaction { void scan(int n, int *odata, const int *idata); + void scanOptimized(int n, int* odata, const int* idata); + int compact(int n, int *odata, const int *idata); } } From 1257dab3452da1930ad2ed5a849a343443300159 Mon Sep 17 00:00:00 2001 From: lindayukeyi Date: Wed, 23 Sep 2020 15:56:58 -0400 Subject: [PATCH 6/6] 'readme' --- README.md | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a118cbb..d611e93 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Contects - [Performance Analysis](#Performance-Analysis) - [Debug](#Debug) - [Questions](#Questions) -- [Optimization](#Optimization) +- [Extra Credit](#Extra-Credit) - [Reference](#Reference) Introduction @@ -189,3 +189,58 @@ The figure shows the performance of thrust::exclusive_scan on Power-of-Two array - Bug1: I forget to free the device buffers. - Bug2(Main problem): Firstly, I try to avoid module operations when implementing the kernUpSweep and kernDownSweep. Unluckily, it looks like my implementation has problems. So I still use mod operation to determine whether the current thread should do the calculations. +## Extra Credit +1. Improve performance of work-efficient scan + + The work-efficient scan performs worse than the naive scan. Because more and more threads become lazy after several iterations, we need to choose the proper number of threads at each iteration. + + So we need a mapping from the threadId to the array index. + ``` + compute the # of number changed at each iteration, stride, start index + threadId is from 0 to NumberChangedAtEachIteration + array index = threadId * strideAtEachIteration + startIndexAtEachIteration + ``` + Number of threads needed each time is greatly reduced by doing so. Obviously, the performance is much better than the previous version. But there is still a gap between the optimized method and thrust exslusive scan. I think maybe I can use the optimization method mentioned in CUDA Performance class and use shared memory. +``` +**************** +** SCAN TESTS ** +**************** + [ 32 37 45 13 5 4 16 28 34 3 47 5 21 ... 45 0 ] +==== cpu scan, power-of-two ==== + elapsed time: 65.1664ms (std::chrono Measured) + [ 0 32 69 114 127 132 136 152 180 214 217 264 269 ... 410887714 410887759 ] +==== cpu scan, non-power-of-two ==== + elapsed time: 24.5645ms (std::chrono Measured) + [ 0 32 69 114 127 132 136 152 180 214 217 264 269 ... 410887594 410887640 ] + passed +==== naive scan, power-of-two ==== + elapsed time: 13.1808ms (CUDA Measured) + passed +==== naive scan, non-power-of-two ==== + elapsed time: 13.1909ms (CUDA Measured) + passed +==== work-efficient scan, power-of-two ==== + elapsed time: 17.7448ms (CUDA Measured) + passed +==== work-efficient scan, non-power-of-two ==== + elapsed time: 17.7584ms (CUDA Measured) + passed +==== thrust scan, power-of-two ==== + elapsed time: 0.685632ms (CUDA Measured) + passed +==== thrust scan, non-power-of-two ==== + elapsed time: 0.69632ms (CUDA Measured) + passed +==== work-efficient scan optimized, power-of-two ==== + elapsed time: 5.94595ms (CUDA Measured) + passed +==== work-efficient scan optimized, non-power-of-two ==== + elapsed time: 5.95277ms (CUDA Measured) + passed + +``` + +## Reference + +1. [Parallel Algotithms Slides](#https://onedrive.live.com/view.aspx?resid=A6B78147D66DD722!95162&ithint=file%2cpptx&authkey=!AFs_WCO3UbQLC40) +2. [NVIDIA GPU Gem3](https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch39.html)