From eed6a6ecac8f4d938116374d377062ea5eb02cc6 Mon Sep 17 00:00:00 2001 From: FENGKAI WU Date: Mon, 18 Sep 2017 15:26:36 -0400 Subject: [PATCH 1/9] cpu naive done --- stream_compaction/cpu.cu | 61 +++++++++++++++++++++++---- stream_compaction/efficient.cu | 76 ++++++++++++++++++++++++++++++---- stream_compaction/naive.cu | 73 ++++++++++++++++++++++++++++---- 3 files changed, 184 insertions(+), 26 deletions(-) diff --git a/stream_compaction/cpu.cu b/stream_compaction/cpu.cu index 05ce667..fe4c1ee 100644 --- a/stream_compaction/cpu.cu +++ b/stream_compaction/cpu.cu @@ -1,17 +1,36 @@ #include #include "cpu.h" -#include "common.h" +#include "common.h" namespace StreamCompaction { namespace CPU { - using StreamCompaction::Common::PerformanceTimer; - PerformanceTimer& timer() - { - static PerformanceTimer timer; - return timer; + using StreamCompaction::Common::PerformanceTimer; + PerformanceTimer& timer() + { + static PerformanceTimer timer; + return timer; } + void scanImpl(int n, int *odata, const int *idata) { + + int pre; + + for (int i = 0; i < n; ++i) + { + + if (i == 0) { + pre = idata[i]; + odata[i] = 0; + } + else { + int temp = idata[i]; + odata[i] = odata[i - 1] + pre; + pre = temp; + } + } + } + /** * CPU scan (prefix sum). * For performance analysis, this is supposed to be a simple for loop. @@ -19,7 +38,7 @@ namespace StreamCompaction { */ void scan(int n, int *odata, const int *idata) { timer().startCpuTimer(); - // TODO + scanImpl(n, odata, idata); timer().endCpuTimer(); } @@ -30,7 +49,17 @@ namespace StreamCompaction { */ int compactWithoutScan(int n, int *odata, const int *idata) { timer().startCpuTimer(); - // TODO + int count = 0; + + int k = 0; + for (int i = 0; i < n; ++i) + { + if (idata[i] != 0) + { + count++; + odata[k++] = idata[i]; + } + } timer().endCpuTimer(); return -1; } @@ -42,7 +71,21 @@ namespace StreamCompaction { */ int compactWithScan(int n, int *odata, const int *idata) { timer().startCpuTimer(); - // TODO + for (int i = 0; i < n; ++i) + { + odata[i] = (idata[i] != 0); + } + + scanImpl(n, odata, odata); + + int count = 0; + for (int i = 0; i < n; ++i) + { + if (idata[i] != 0) { + odata[odata[i]] = idata[i]; + count++; + } + } timer().endCpuTimer(); return -1; } diff --git a/stream_compaction/efficient.cu b/stream_compaction/efficient.cu index 36c5ef2..af2cc77 100644 --- a/stream_compaction/efficient.cu +++ b/stream_compaction/efficient.cu @@ -2,23 +2,83 @@ #include #include "common.h" #include "efficient.h" +#include "device_launch_parameters.h" namespace StreamCompaction { namespace Efficient { - using StreamCompaction::Common::PerformanceTimer; - PerformanceTimer& timer() - { - static PerformanceTimer timer; - return timer; + using StreamCompaction::Common::PerformanceTimer; + PerformanceTimer& timer() + { + static PerformanceTimer timer; + return timer; } + __global__ void kernUpSweep(int n, int offset, int *odata) + { + int idx = threadIdx.x + blockIdx.x * blockDim.x; + + if (idx >= n) return; + + if (idx % offset == 0) + { + odata[idx + offset - 1] += odata[idx + (offset >> 1) - 1]; + } + } + + __global__ void kernDownSweep(int n, int offset, int *odata) + { + int idx = threadIdx.x + blockIdx.x * blockDim.x; + + if (idx >= n) return; + + if (idx == n - 1) { + odata[idx] = 0; + return; + } + + if (idx % offset == 0) + { + int temp = odata[idx + (offset >> 1) - 1]; + odata[idx + (offset >> 1) - 1] = odata[idx + offset - 1]; + odata[idx + offset - 1] += temp; + } + } + /** * Performs prefix-sum (aka scan) on idata, storing the result into odata. */ void scan(int n, int *odata, const int *idata) { - timer().startGpuTimer(); - // TODO - timer().endGpuTimer(); + int *dev_odata, *dev_idata; + dev_odata = nullptr; + dev_idata = nullptr; + + //cudaMalloc(&dev_idata, n * sizeof(int)); + cudaMalloc(&dev_odata, n * sizeof(int)); + //cudaMemcpy(dev_idata, idata, n * sizeof(int), cudaMemcpyHostToDevice); + cudaMemcpy(dev_odata, odata, n * sizeof(int), cudaMemcpyHostToDevice); + + int depth = ilog2ceil(n); + int blockSize = 128; + dim3 threadPerBlock(blockSize); + dim3 blocksPerGrid((blockSize + n - 1) / blockSize); + + timer().startGpuTimer(); + + for (int i = 0; i < depth; ++i) + { + kernUpSweep << > > (n, 1 << (i + 1), dev_odata); + } + + for (int i = depth - 1; i >= 0; --i) + { + kernDownSweep << > > (n, 1 << (i + 1), dev_odata); + } + + timer().endGpuTimer(); + + cudaMemcpy(odata, dev_odata, n * sizeof(int), cudaMemcpyDeviceToHost); + cudaFree(dev_idata); + cudaFree(dev_odata); } /** diff --git a/stream_compaction/naive.cu b/stream_compaction/naive.cu index 9218f8e..caf5614 100644 --- a/stream_compaction/naive.cu +++ b/stream_compaction/naive.cu @@ -2,24 +2,79 @@ #include #include "common.h" #include "naive.h" +#include "device_launch_parameters.h" namespace StreamCompaction { namespace Naive { - using StreamCompaction::Common::PerformanceTimer; - PerformanceTimer& timer() - { - static PerformanceTimer timer; - return timer; + using StreamCompaction::Common::PerformanceTimer; + PerformanceTimer& timer() + { + static PerformanceTimer timer; + return timer; } - // TODO: __global__ + + __global__ void kernScanNaive(int N, int d, int *odata, const int *idata) + { + int idx = blockIdx.x * blockDim.x + threadIdx.x; + + if (idx >= N) return; + + int num_d = 1 << (d - 1); + if (idx >= num_d) + { + odata[idx] = idata[idx - num_d] + idata[idx]; + } + else + { + odata[idx] = idata[idx]; + } + } + + __global__ void kernInclusiveToExclusive(int N, int *odata, const int *idata) + { + int idx = blockIdx.x * blockDim.x + threadIdx.x; + + if (idx >= N) return; + + odata[idx] = idx == 0 ? 0 : idata[idx - 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 - timer().endGpuTimer(); + //Dimensions + int blockSize = 128; + int depth = ilog2ceil(n); + dim3 threadsPerGrid(blockSize); + dim3 blocksPerGrid((n + blockSize - 1) / blockSize); + + //Memory allocation + int *dev_idata, *dev_odata; + + dev_idata = nullptr; + dev_odata = nullptr; + cudaMalloc(&dev_idata, n * sizeof(int)); + cudaMalloc(&dev_odata, n * sizeof(int)); + cudaMemcpy(dev_idata, idata, n * sizeof(int), cudaMemcpyHostToDevice); + + timer().startGpuTimer(); + + + for (int d = 1; d <= depth; ++d) { + kernScanNaive << > >(n, d, dev_odata, dev_idata); + int *temp = dev_odata; + dev_odata = dev_idata; + dev_idata = temp; + } + + kernInclusiveToExclusive << < blocksPerGrid, threadsPerGrid >> > (n, dev_odata, dev_idata); + + timer().endGpuTimer(); + + cudaMemcpy(odata, dev_odata, n * sizeof(int), cudaMemcpyDeviceToHost); + cudaFree(&dev_idata); + cudaFree(&dev_odata); } } } From 82274d89fcd710538af40e943e1a5083a1c50cd9 Mon Sep 17 00:00:00 2001 From: FENGKAI WU Date: Tue, 19 Sep 2017 10:19:27 -0400 Subject: [PATCH 2/9] efficient --- src/main.cpp | 12 +++--- stream_compaction/cpu.cu | 4 +- stream_compaction/efficient.cu | 73 +++++++++++++++++++++++----------- stream_compaction/naive.cu | 22 +++++----- stream_compaction/thrust.cu | 30 ++++++++++---- 5 files changed, 92 insertions(+), 49 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 7305641..cafe9b2 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[SIZE], b[SIZE], c[SIZE]; @@ -25,7 +25,7 @@ int main(int argc, char* argv[]) { printf("** SCAN TESTS **\n"); printf("****************\n"); - genArray(SIZE - 1, a, 50); // Leave a 0 at the end to test that edge case + genArray(SIZE - 1, a, 2); // Leave a 0 at the end to test that edge case a[SIZE - 1] = 0; printArray(SIZE, a, true); @@ -49,28 +49,28 @@ int main(int argc, char* argv[]) { printDesc("naive scan, power-of-two"); StreamCompaction::Naive::scan(SIZE, c, a); printElapsedTime(StreamCompaction::Naive::timer().getGpuElapsedTimeForPreviousOperation(), "(CUDA Measured)"); - //printArray(SIZE, c, true); + printArray(SIZE, c, true); printCmpResult(SIZE, b, c); zeroArray(SIZE, c); printDesc("naive scan, non-power-of-two"); StreamCompaction::Naive::scan(NPOT, c, a); printElapsedTime(StreamCompaction::Naive::timer().getGpuElapsedTimeForPreviousOperation(), "(CUDA Measured)"); - //printArray(SIZE, c, true); + printArray(SIZE, c, true); printCmpResult(NPOT, b, c); zeroArray(SIZE, c); printDesc("work-efficient scan, power-of-two"); StreamCompaction::Efficient::scan(SIZE, c, a); printElapsedTime(StreamCompaction::Efficient::timer().getGpuElapsedTimeForPreviousOperation(), "(CUDA Measured)"); - //printArray(SIZE, c, true); + printArray(SIZE, c, true); printCmpResult(SIZE, b, c); zeroArray(SIZE, c); printDesc("work-efficient scan, non-power-of-two"); StreamCompaction::Efficient::scan(NPOT, c, a); printElapsedTime(StreamCompaction::Efficient::timer().getGpuElapsedTimeForPreviousOperation(), "(CUDA Measured)"); - //printArray(NPOT, c, true); + printArray(NPOT, c, true); printCmpResult(NPOT, b, c); zeroArray(SIZE, c); diff --git a/stream_compaction/cpu.cu b/stream_compaction/cpu.cu index fe4c1ee..0941ac2 100644 --- a/stream_compaction/cpu.cu +++ b/stream_compaction/cpu.cu @@ -61,7 +61,7 @@ namespace StreamCompaction { } } timer().endCpuTimer(); - return -1; + return count; } /** @@ -87,7 +87,7 @@ namespace StreamCompaction { } } timer().endCpuTimer(); - return -1; + return count; } } } diff --git a/stream_compaction/efficient.cu b/stream_compaction/efficient.cu index af2cc77..c3bef01 100644 --- a/stream_compaction/efficient.cu +++ b/stream_compaction/efficient.cu @@ -19,10 +19,17 @@ namespace StreamCompaction { if (idx >= n) return; - if (idx % offset == 0) + if (n == 1) { - odata[idx + offset - 1] += odata[idx + (offset >> 1) - 1]; + odata[offset - 1] = 0; + return; } + + int cur = (idx + 1) * offset - 1; + + int prev = cur - (offset / 2); + + odata[cur] += odata[prev]; } __global__ void kernDownSweep(int n, int offset, int *odata) @@ -31,54 +38,74 @@ namespace StreamCompaction { if (idx >= n) return; - if (idx == n - 1) { - odata[idx] = 0; - return; - } + int cur = (idx + 1) * offset - 1; + + int prev = cur - (offset / 2); - if (idx % offset == 0) + int temp = odata[prev]; + odata[prev] = odata[cur]; + odata[cur] += temp; + } + + int getPadded(int n) { + int countOfOnes = 0; + int ret = 1; + while (n != 1) { - int temp = odata[idx + (offset >> 1) - 1]; - odata[idx + (offset >> 1) - 1] = odata[idx + offset - 1]; - odata[idx + offset - 1] += temp; + if (n & 1 == 1) + { + ++countOfOnes; + } + n >>= 1; + ret <<= 1; } + if (countOfOnes == 0) return ret; + else return ret << 1; } /** * Performs prefix-sum (aka scan) on idata, storing the result into odata. */ void scan(int n, int *odata, const int *idata) { - int *dev_odata, *dev_idata; + int *dev_odata; dev_odata = nullptr; - dev_idata = nullptr; - //cudaMalloc(&dev_idata, n * sizeof(int)); - cudaMalloc(&dev_odata, n * sizeof(int)); - //cudaMemcpy(dev_idata, idata, n * sizeof(int), cudaMemcpyHostToDevice); - cudaMemcpy(dev_odata, odata, n * sizeof(int), cudaMemcpyHostToDevice); + int numToCompute = getPadded(n); + + cudaMalloc(&dev_odata, numToCompute * sizeof(int)); + cudaMemset(dev_odata, 0, numToCompute); + cudaMemcpy(dev_odata, idata, n * sizeof(int), cudaMemcpyHostToDevice); int depth = ilog2ceil(n); - int blockSize = 128; + int blockSize = 256; + int offset = 1; dim3 threadPerBlock(blockSize); - dim3 blocksPerGrid((blockSize + n - 1) / blockSize); timer().startGpuTimer(); for (int i = 0; i < depth; ++i) { - kernUpSweep << > > (n, 1 << (i + 1), dev_odata); + numToCompute /= 2; + offset *= 2; + int blocksPerGrid = (numToCompute + blockSize - 1) / blockSize; + kernUpSweep << > > (numToCompute, offset, dev_odata); + } - for (int i = depth - 1; i >= 0; --i) - { - kernDownSweep << > > (n, 1 << (i + 1), dev_odata); + numToCompute = 1; + for (int i = 0; i < depth; ++i) + { + int blocksPerGrid = (numToCompute + blockSize - 1) / blockSize; + kernDownSweep << > > (numToCompute, offset, dev_odata); + numToCompute *= 2; + offset /= 2; } timer().endGpuTimer(); cudaMemcpy(odata, dev_odata, n * sizeof(int), cudaMemcpyDeviceToHost); - cudaFree(dev_idata); cudaFree(dev_odata); + cudaDeviceSynchronize(); } /** diff --git a/stream_compaction/naive.cu b/stream_compaction/naive.cu index caf5614..045fe26 100644 --- a/stream_compaction/naive.cu +++ b/stream_compaction/naive.cu @@ -13,16 +13,15 @@ namespace StreamCompaction { return timer; } - __global__ void kernScanNaive(int N, int d, int *odata, const int *idata) + __global__ void kernScanNaive(int N, int stride, int *odata, const int *idata) { int idx = blockIdx.x * blockDim.x + threadIdx.x; if (idx >= N) return; - int num_d = 1 << (d - 1); - if (idx >= num_d) + if (idx >= stride) { - odata[idx] = idata[idx - num_d] + idata[idx]; + odata[idx] = idata[idx - stride] + idata[idx]; } else { @@ -44,10 +43,9 @@ namespace StreamCompaction { */ void scan(int n, int *odata, const int *idata) { //Dimensions - int blockSize = 128; + int blockSize = 256; int depth = ilog2ceil(n); - dim3 threadsPerGrid(blockSize); - dim3 blocksPerGrid((n + blockSize - 1) / blockSize); + int blocksPerGrid = (n + blockSize - 1) / blockSize; //Memory allocation int *dev_idata, *dev_odata; @@ -60,21 +58,23 @@ namespace StreamCompaction { timer().startGpuTimer(); - - for (int d = 1; d <= depth; ++d) { - kernScanNaive << > >(n, d, dev_odata, dev_idata); + int stride = 1; + for (int d = 0; d < depth; ++d) { + kernScanNaive << > >(n, stride, dev_odata, dev_idata); int *temp = dev_odata; dev_odata = dev_idata; dev_idata = temp; + stride *= 2; } - kernInclusiveToExclusive << < blocksPerGrid, threadsPerGrid >> > (n, dev_odata, dev_idata); + kernInclusiveToExclusive << < blocksPerGrid, blockSize >> > (n, dev_odata, dev_idata); timer().endGpuTimer(); cudaMemcpy(odata, dev_odata, n * sizeof(int), cudaMemcpyDeviceToHost); cudaFree(&dev_idata); cudaFree(&dev_odata); + cudaDeviceSynchronize(); } } } diff --git a/stream_compaction/thrust.cu b/stream_compaction/thrust.cu index 36b732d..9ad8db0 100644 --- a/stream_compaction/thrust.cu +++ b/stream_compaction/thrust.cu @@ -8,21 +8,37 @@ namespace StreamCompaction { namespace Thrust { - using StreamCompaction::Common::PerformanceTimer; - PerformanceTimer& timer() - { - static PerformanceTimer timer; - return timer; + using StreamCompaction::Common::PerformanceTimer; + PerformanceTimer& timer() + { + static PerformanceTimer timer; + return timer; } /** * 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()); + + int *dev_idata, *dev_odata; + dev_idata = nullptr; + dev_odata = nullptr; + + cudaMalloc(&dev_idata, n * sizeof(int)); + cudaMalloc(&dev_odata, n * sizeof(int)); + cudaMemcpy(dev_idata, idata, n * sizeof(int), cudaMemcpyHostToDevice); + + thrust::device_ptr thrust_idata(dev_idata); + thrust::device_ptr thrust_odata(dev_odata); + + timer().startGpuTimer(); + thrust::exclusive_scan(thrust_idata, thrust_idata + n, thrust_odata); timer().endGpuTimer(); + + cudaMemcpy(odata, dev_odata, n * sizeof(int), cudaMemcpyDeviceToHost); + cudaFree(dev_idata); + cudaFree(dev_odata); } } } From 48cd0e9fcd529a790964ce521c005f65ef51ca47 Mon Sep 17 00:00:00 2001 From: FENGKAI WU Date: Tue, 19 Sep 2017 20:48:07 -0400 Subject: [PATCH 3/9] efficient --- img/Scan.png | Bin 0 -> 13298 bytes img/cmpact.png | Bin 0 -> 11502 bytes src/main.cpp | 31 +++++++---- stream_compaction/common.cu | 16 +++++- stream_compaction/efficient.cu | 96 ++++++++++++++++++++++++++++++--- stream_compaction/naive.cu | 13 ++++- 6 files changed, 135 insertions(+), 21 deletions(-) create mode 100644 img/Scan.png create mode 100644 img/cmpact.png diff --git a/img/Scan.png b/img/Scan.png new file mode 100644 index 0000000000000000000000000000000000000000..e90f03bf74f2f0c6135cd61809d86bdab125c865 GIT binary patch literal 13298 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 literal 0 HcmV?d00001 diff --git a/src/main.cpp b/src/main.cpp index cafe9b2..983df19 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -13,19 +13,27 @@ #include #include "testing_helpers.hpp" -const int SIZE = 1 << 25; // feel free to change the size of array -const int NPOT = SIZE - 3; // Non-Power-Of-Two -int a[SIZE], b[SIZE], c[SIZE]; + int main(int argc, char* argv[]) { // Scan tests + if (argc != 2) { + printf("test.exe [sizeOfArray, please input 1-25]"); + return 1; + } + const int SIZE = 1 << atoi(argv[1]); // feel free to change the size of array + const int NPOT = SIZE - 3; // Non-Power-Of-Two + //int a[SIZE], b[SIZE], c[SIZE]; + int* b = new int[SIZE]; + int* a = new int[SIZE]; + int* c = new int[SIZE]; printf("\n"); - printf("****************\n"); - printf("** SCAN TESTS **\n"); - printf("****************\n"); + printf("********************\n"); + printf("** SCAN TESTS, %d **\n", atoi(argv[1])); + printf("********************\n"); - genArray(SIZE - 1, a, 2); // Leave a 0 at the end to test that edge case + genArray(SIZE - 1, a, 10); // Leave a 0 at the end to test that edge case a[SIZE - 1] = 0; printArray(SIZE, a, true); @@ -129,15 +137,18 @@ int main(int argc, char* argv[]) { printDesc("work-efficient compact, power-of-two"); count = StreamCompaction::Efficient::compact(SIZE, c, a); printElapsedTime(StreamCompaction::Efficient::timer().getGpuElapsedTimeForPreviousOperation(), "(CUDA Measured)"); - //printArray(count, c, true); + printArray(count, c, true); printCmpLenResult(count, expectedCount, b, c); zeroArray(SIZE, c); printDesc("work-efficient compact, non-power-of-two"); count = StreamCompaction::Efficient::compact(NPOT, c, a); printElapsedTime(StreamCompaction::Efficient::timer().getGpuElapsedTimeForPreviousOperation(), "(CUDA Measured)"); - //printArray(count, c, true); + printArray(count, c, true); printCmpLenResult(count, expectedNPOT, b, c); - system("pause"); // stop Win32 console from closing on exit + delete[] a; + delete[] b; + delete[] c; + //system("pause"); // stop Win32 console from closing on exit } diff --git a/stream_compaction/common.cu b/stream_compaction/common.cu index 8fc0211..0347bec 100644 --- a/stream_compaction/common.cu +++ b/stream_compaction/common.cu @@ -1,4 +1,5 @@ #include "common.h" +#include "device_launch_parameters.h" void checkCUDAErrorFn(const char *msg, const char *file, int line) { cudaError_t err = cudaGetLastError(); @@ -23,7 +24,11 @@ namespace StreamCompaction { * which map to 0 will be removed, and elements which map to 1 will be kept. */ __global__ void kernMapToBoolean(int n, int *bools, const int *idata) { - // TODO + int idx = threadIdx.x + blockIdx.x * blockDim.x; + + if (idx >= n) return; + + bools[idx] = (int)(idata[idx] != 0); } /** @@ -32,7 +37,14 @@ namespace StreamCompaction { */ __global__ void kernScatter(int n, int *odata, const int *idata, const int *bools, const int *indices) { - // TODO + int idx = threadIdx.x + blockDim.x * blockIdx.x; + + if (idx >= n) return; + + if (bools[idx]) + { + odata[indices[idx]] = idata[idx]; + } } } diff --git a/stream_compaction/efficient.cu b/stream_compaction/efficient.cu index c3bef01..630ae12 100644 --- a/stream_compaction/efficient.cu +++ b/stream_compaction/efficient.cu @@ -73,13 +73,12 @@ namespace StreamCompaction { int numToCompute = getPadded(n); cudaMalloc(&dev_odata, numToCompute * sizeof(int)); - cudaMemset(dev_odata, 0, numToCompute); + cudaMemset(dev_odata, 0, numToCompute * sizeof(int)); cudaMemcpy(dev_odata, idata, n * sizeof(int), cudaMemcpyHostToDevice); int depth = ilog2ceil(n); - int blockSize = 256; + int blockSize = 1024; int offset = 1; - dim3 threadPerBlock(blockSize); timer().startGpuTimer(); @@ -89,7 +88,6 @@ namespace StreamCompaction { offset *= 2; int blocksPerGrid = (numToCompute + blockSize - 1) / blockSize; kernUpSweep << > > (numToCompute, offset, dev_odata); - } numToCompute = 1; @@ -105,9 +103,34 @@ namespace StreamCompaction { cudaMemcpy(odata, dev_odata, n * sizeof(int), cudaMemcpyDeviceToHost); cudaFree(dev_odata); - cudaDeviceSynchronize(); } + //__global__ void kernScanEachBlock(int n, int *a) { + + //} + + //void scanUsingSharedMem(int n, int *odata, const int *idata) { + // int numPadded = getPadded(n); + + // int *dev_idata, *dev_odata; + + // dev_idata = nullptr; + // cudaMalloc(&dev_idata, numPadded * sizeof(int)); + // cudaMemset(dev_idata, 0, numPadded * sizeof(int)); + // cudaMemcpy(dev_idata, idata, n * sizeof(int), cudaMemcpyHostToDevice); + + // dev_odata = nullptr; + // cudaMalloc(&dev_odata, numPadded * sizeof(int)); + + // int blockSize = 1024; + // int numOfBlocks = (numPadded + blockSize - 1) / blockSize; + + // kernScanEachBlock << > > (blockSize, dev_idata, dev_odata); + + // cudaFree(dev_idata); + //} + + /** * Performs stream compaction on idata, storing the result into odata. * All zeroes are discarded. @@ -118,10 +141,67 @@ namespace StreamCompaction { * @returns The number of elements remaining after compaction. */ int compact(int n, int *odata, const int *idata) { - timer().startGpuTimer(); - // TODO + int numToCompute = getPadded(n); + + int *dev_odata, *dev_idata, *dev_bools, *dev_indices; + dev_odata = nullptr; + dev_idata = nullptr; + dev_bools = nullptr; + dev_indices = nullptr; + + cudaMalloc(&dev_bools, n * sizeof(int)); + cudaMalloc(&dev_idata, n * sizeof(int)); + cudaMalloc(&dev_odata, n * sizeof(int)); + cudaMemcpy(dev_idata, idata, n * sizeof(int), cudaMemcpyHostToDevice); + cudaMemset(dev_bools, 0, n * sizeof(int)); + + cudaMalloc(&dev_indices, numToCompute * sizeof(int)); + cudaMemset(dev_indices, 0, numToCompute * sizeof(int)); + + int depth = ilog2ceil(n); + int offset = 1; + int blockSize = 1024; + int blocksPerGrid = (n + blockSize - 1) / blockSize; + + timer().startGpuTimer(); + Common::kernMapToBoolean << > > (numToCompute, dev_bools, dev_idata); + cudaMemcpy(dev_indices, dev_bools, n * sizeof(int), cudaMemcpyDeviceToDevice); + + for (int i = 0; i < depth; ++i) + { + numToCompute /= 2; + offset *= 2; + blocksPerGrid = (numToCompute + blockSize - 1) / blockSize; + kernUpSweep << > > (numToCompute, offset, dev_indices); + + } + + numToCompute = 1; + for (int i = 0; i < depth; ++i) + { + blocksPerGrid = (numToCompute + blockSize - 1) / blockSize; + kernDownSweep << > > (numToCompute, offset, dev_indices); + numToCompute *= 2; + offset /= 2; + } + + int ret; + cudaMemcpy(&ret, dev_indices + n - 1, sizeof(int), cudaMemcpyDeviceToHost); + ret += idata[n - 1] == 0 ? 0 : 1; + + + blocksPerGrid = (n + blockSize - 1) / blockSize; + Common::kernScatter << > > (n, dev_odata, dev_idata, dev_bools, dev_indices); + timer().endGpuTimer(); - return -1; + + cudaMemcpy(odata, dev_odata, ret * sizeof(int), cudaMemcpyDeviceToHost); + + cudaFree(dev_odata); + cudaFree(dev_idata); + cudaFree(dev_bools); + cudaFree(dev_indices); + return ret; } } } diff --git a/stream_compaction/naive.cu b/stream_compaction/naive.cu index 045fe26..8cce7c3 100644 --- a/stream_compaction/naive.cu +++ b/stream_compaction/naive.cu @@ -4,6 +4,8 @@ #include "naive.h" #include "device_launch_parameters.h" +#define SECTION_SIZE 4 + namespace StreamCompaction { namespace Naive { using StreamCompaction::Common::PerformanceTimer; @@ -12,6 +14,14 @@ namespace StreamCompaction { static PerformanceTimer timer; return timer; } + + __global__ void kernNaivSham(int *x, int*y, int n) + { + __shared__ int xy[SECTION_SIZE]; + + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < n) xy[threadIdx.x] = x[i]; + } __global__ void kernScanNaive(int N, int stride, int *odata, const int *idata) { @@ -43,7 +53,7 @@ namespace StreamCompaction { */ void scan(int n, int *odata, const int *idata) { //Dimensions - int blockSize = 256; + int blockSize = 1024; int depth = ilog2ceil(n); int blocksPerGrid = (n + blockSize - 1) / blockSize; @@ -56,6 +66,7 @@ namespace StreamCompaction { cudaMalloc(&dev_odata, n * sizeof(int)); cudaMemcpy(dev_idata, idata, n * sizeof(int), cudaMemcpyHostToDevice); + timer().startGpuTimer(); int stride = 1; From 9a72c99f9cc0712214906f9d633b467427877099 Mon Sep 17 00:00:00 2001 From: wufk Date: Tue, 19 Sep 2017 20:54:47 -0400 Subject: [PATCH 4/9] Update README.md add img --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b71c458..404545b 100644 --- a/README.md +++ b/README.md @@ -3,11 +3,14 @@ CUDA Stream Compaction **University of Pennsylvania, CIS 565: GPU Programming and Architecture, Project 2** -* (TODO) YOUR NAME HERE -* Tested on: (TODO) Windows 22, i7-2222 @ 2.22GHz 22GB, GTX 222 222MB (Moore 2222 Lab) +* Fengkai Wu +* Tested on: Windows 10, i7-6700 @ 3.40GHz 16GB, Quadro K620 4095MB (Twn M70 Lab) ### (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.) +![img_1](https://github.com/wufk/Project2-Stream-Compaction/blob/master/img/Scan.png) + +![img_2](https://github.com/wufk/Project2-Stream-Compaction/blob/master/img/cmpact.png) From 3fe1363c7f18d086d0f0e4923d66a3a582bb096d Mon Sep 17 00:00:00 2001 From: FENGKAI WU Date: Tue, 19 Sep 2017 21:09:06 -0400 Subject: [PATCH 5/9] performance img --- img/proj2Perform.PNG | Bin 0 -> 79694 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 img/proj2Perform.PNG diff --git a/img/proj2Perform.PNG b/img/proj2Perform.PNG new file mode 100644 index 0000000000000000000000000000000000000000..ce413bea502b0eed3c1285de381b0247eee46b9d GIT binary patch literal 79694 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 literal 0 HcmV?d00001 From 88de4a475be29ac311bf5c22080536e587c45d2c Mon Sep 17 00:00:00 2001 From: FENGKAI WU Date: Tue, 19 Sep 2017 21:26:09 -0400 Subject: [PATCH 6/9] table --- img/proj2table.PNG | Bin 0 -> 35756 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 img/proj2table.PNG diff --git a/img/proj2table.PNG b/img/proj2table.PNG new file mode 100644 index 0000000000000000000000000000000000000000..3037da3d4602717c5f96b7b2f9c61d265397d975 GIT binary patch literal 35756 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 N^zYru)B8Q-e*kBM9w`6- literal 0 HcmV?d00001 From 6284b641f94e5cb2d905ef165eff5bdc5e76c391 Mon Sep 17 00:00:00 2001 From: wufk Date: Tue, 19 Sep 2017 21:51:20 -0400 Subject: [PATCH 7/9] Update README.md --- README.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 404545b..57ca924 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,22 @@ CUDA Stream Compaction * Fengkai Wu * Tested on: Windows 10, i7-6700 @ 3.40GHz 16GB, Quadro K620 4095MB (Twn M70 Lab) -### (TODO: Your README) +### Analysis Include analysis, etc. (Remember, this is public, so don't put anything here that you don't want to share with the world.) +The running time of exclusive scan under different algorithms are as follows: ![img_1](https://github.com/wufk/Project2-Stream-Compaction/blob/master/img/Scan.png) +![img_1](https://github.com/wufk/Project2-Stream-Compaction/blob/master/img/proj2table.PNG) + +The running time of stream compaction is as follows: ![img_2](https://github.com/wufk/Project2-Stream-Compaction/blob/master/img/cmpact.png) + +As the graph shows, naive scan takes extreme long time to finish the job while the efficient way is much fast. However, the GPU performance is still not as good as CPU. In my Implementation of efficient scan, for each downSweep/upSweep, the number of actual number of working threads is re-computed. The launching blocks are also derived from the number of threads to be used. Bits shifting and modulus operation are also avoided. Other possible factors that downplay the performance might due to too many kernel calls when sweeping up and down, large use of global memory and too many threads required when the array size is large. + +Possible ways to further enhance the performance in the future includes using shared memory and dividing and scanning the array by blocks. + +The timeline of execution is as follows: +![img_2](https://github.com/wufk/Project2-Stream-Compaction/blob/master/img/proj2Perform.PNG) From 05e97bd755f2e4fd249650446ea0fa63e7a73d80 Mon Sep 17 00:00:00 2001 From: wufk Date: Tue, 19 Sep 2017 22:16:58 -0400 Subject: [PATCH 8/9] Update README.md --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 57ca924..4de752d 100644 --- a/README.md +++ b/README.md @@ -23,5 +23,11 @@ As the graph shows, naive scan takes extreme long time to finish the job while t Possible ways to further enhance the performance in the future includes using shared memory and dividing and scanning the array by blocks. -The timeline of execution is as follows: +Another worth noticing is that thrust runs way faster when the array size is non multiple of two. + +The timeline of execution when the array size is 2^20 is as follows: ![img_2](https://github.com/wufk/Project2-Stream-Compaction/blob/master/img/proj2Perform.PNG) + +It shows that CUDA library of memery manipulation is very expensive. Furthermore, we can see that using thrust and using our own algorithms of scanning is calling different CUDA runtime API. Thrust is calling cudaDeviceSynchronize function while our algorithms call cudaEventSynchronize. This may partly explain why thrust run way faster, in that it is optimized in device and hardware while our effort is just focusing on algorithm and high level part. + +In summary, to get better performance in GPU computing, architecture makes a huge difference and optimization must focus on better allocating resources and making use of the specaiality of GPU hardware. From 725194a79c70c7d0bb06a89901fe6db2b7cc6ec4 Mon Sep 17 00:00:00 2001 From: Fengkai Wu Date: Sun, 10 Dec 2017 01:59:45 -0500 Subject: [PATCH 9/9] share mem & no BC --- src/main.cpp | 27 +++++++- stream_compaction/CMakeLists.txt | 2 +- stream_compaction/cpu.cu | 8 +++ stream_compaction/cpu.h | 2 + stream_compaction/efficient.cu | 106 +++++++++++++++++++++++++++++++ stream_compaction/efficient.h | 2 + 6 files changed, 145 insertions(+), 2 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 983df19..fdd967f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -28,14 +28,21 @@ int main(int argc, char* argv[]) { int* a = new int[SIZE]; int* c = new int[SIZE]; + int *input_inclusive = new int[SIZE]; + int *b_inclusive = new int[SIZE]; + int *c_inclusive = new int[SIZE]; + printf("\n"); printf("********************\n"); printf("** SCAN TESTS, %d **\n", atoi(argv[1])); printf("********************\n"); genArray(SIZE - 1, a, 10); // Leave a 0 at the end to test that edge case + genArray(SIZE - 1, input_inclusive, 10); a[SIZE - 1] = 0; + input_inclusive[SIZE - 1] = 0; printArray(SIZE, a, true); + printArray(SIZE, input_inclusive, true); // initialize b using StreamCompaction::CPU::scan you implement // We use b for further comparison. Make sure your StreamCompaction::CPU::scan is correct. @@ -46,6 +53,11 @@ int main(int argc, char* argv[]) { printElapsedTime(StreamCompaction::CPU::timer().getCpuElapsedTimeForPreviousOperation(), "(std::chrono Measured)"); printArray(SIZE, b, true); + zeroArray(SIZE, b_inclusive); + printDesc("cpu inclusive scan, power of two"); + StreamCompaction::CPU::inScan(SIZE, b_inclusive, input_inclusive); + printArray(SIZE, b_inclusive, true); + zeroArray(SIZE, c); printDesc("cpu scan, non-power-of-two"); StreamCompaction::CPU::scan(NPOT, c, a); @@ -53,6 +65,18 @@ int main(int argc, char* argv[]) { printArray(NPOT, b, true); printCmpResult(NPOT, b, c); + zeroArray(SIZE, c); + printDesc("cpu inclusive scan, non power of two"); + StreamCompaction::CPU::inScan(SIZE, c, input_inclusive); + printArray(SIZE, c, true); + printCmpResult(NPOT, b_inclusive, c); + + zeroArray(SIZE, c); + printDesc("SM inclusive scan, non power of two"); + StreamCompaction::Efficient::scanSM(NPOT, c, input_inclusive); + printArray(SIZE, c, true); + printCmpResult(NPOT, b_inclusive, c); + zeroArray(SIZE, c); printDesc("naive scan, power-of-two"); StreamCompaction::Naive::scan(SIZE, c, a); @@ -150,5 +174,6 @@ int main(int argc, char* argv[]) { delete[] a; delete[] b; delete[] c; - //system("pause"); // stop Win32 console from closing on exit + system + ("pause"); // stop Win32 console from closing on exit } diff --git a/stream_compaction/CMakeLists.txt b/stream_compaction/CMakeLists.txt index cdbef77..e31ca3c 100644 --- a/stream_compaction/CMakeLists.txt +++ b/stream_compaction/CMakeLists.txt @@ -13,5 +13,5 @@ set(SOURCE_FILES cuda_add_library(stream_compaction ${SOURCE_FILES} - OPTIONS -arch=sm_20 + OPTIONS -arch=sm_30 ) diff --git a/stream_compaction/cpu.cu b/stream_compaction/cpu.cu index 0941ac2..cc57cb9 100644 --- a/stream_compaction/cpu.cu +++ b/stream_compaction/cpu.cu @@ -42,6 +42,14 @@ namespace StreamCompaction { timer().endCpuTimer(); } + void inScan(int n, int * odata, const int * idata) + { + odata[0] = idata[0]; + for (int i = 1; i < n; i++) { + odata[i] = odata[i - 1] + idata[i]; + } + } + /** * CPU stream compaction without using the scan function. * diff --git a/stream_compaction/cpu.h b/stream_compaction/cpu.h index 236ce11..4e56da7 100644 --- a/stream_compaction/cpu.h +++ b/stream_compaction/cpu.h @@ -8,6 +8,8 @@ namespace StreamCompaction { void scan(int n, int *odata, const int *idata); + void inScan(int n, int *odata, const int *idata); + int compactWithoutScan(int n, int *odata, const int *idata); int compactWithScan(int n, int *odata, const int *idata); diff --git a/stream_compaction/efficient.cu b/stream_compaction/efficient.cu index 630ae12..9f8926d 100644 --- a/stream_compaction/efficient.cu +++ b/stream_compaction/efficient.cu @@ -13,6 +13,93 @@ namespace StreamCompaction { return timer; } + __global__ void kernScanSMBC(int n, int *data) { + extern __shared__ int smem[]; + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i >= n) { + return; + } + int padding = i / warpSize; + smem[threadIdx.x + padding] = data[i]; + int temp[1024]; + + + + for (unsigned int stride = 1; stride < blockDim.x; stride *= 2) { + __syncthreads(); + for (int i = 0; i < 1024; i++) { + temp[i] = smem[i]; + } + + int index = (threadIdx.x + 1 ) * 2 * stride - 1; + int prev = index - stride; + index += index / warpSize; + prev += prev / warpSize; + if (index < blockDim.x) { + smem[index] += smem[prev]; + } + } + + for (int stride = 1024 / 4; stride > 0; stride /= 2) { + __syncthreads(); + for (int i = 0; i < 1024; i++) { + temp[i] = smem[i]; + } + int index = (threadIdx.x + 1 ) * stride * 2 - 1; + int next = index + stride; + index += index / warpSize; + next += next / warpSize; + if (next < blockDim.x) { + smem[next] += smem[index]; + } + } + + __syncthreads(); + for (int i = 0; i < 1024; i++) { + temp[i] = smem[i]; + } + data[i] = smem[threadIdx.x + padding]; + } + + __global__ void kernScanSM(int n, int *data) { + extern __shared__ int smem[]; + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i >= n) { + return; + } + smem[threadIdx.x] = data[i]; + int temp[1024]; + + + for (unsigned int stride = 1; stride < blockDim.x; stride *= 2) { + __syncthreads(); + for (int i = 0; i < 1024; i++) { + temp[i] = smem[i]; + } + int index = (threadIdx.x + 1) * 2 * stride - 1; + if (index < blockDim.x) { + smem[index] += smem[index - stride]; + } + } + + for (int stride = 1024 / 4; stride > 0; stride /= 2) { + __syncthreads(); + for (int i = 0; i < 1024; i++) { + temp[i] = smem[i]; + } + int index = (threadIdx.x + 1) * stride * 2 - 1; + if (index + stride < blockDim.x) { + smem[index + stride] += smem[index]; + } + } + + __syncthreads(); + for (int i = 0; i < 1024; i++) { + temp[i] = smem[i]; + } + data[i] = smem[threadIdx.x]; + } + __global__ void kernUpSweep(int n, int offset, int *odata) { int idx = threadIdx.x + blockIdx.x * blockDim.x; @@ -105,6 +192,25 @@ namespace StreamCompaction { cudaFree(dev_odata); } + void scanSM(int n, int * odata, const int * idata) + { + int *dev_odata; + dev_odata = nullptr; + + int numToCompute = getPadded(n); + + cudaMalloc(&dev_odata, numToCompute * sizeof(int)); + cudaMemset(dev_odata, 0, numToCompute * sizeof(int)); + cudaMemcpy(dev_odata, idata, n * sizeof(int), cudaMemcpyHostToDevice); + + int blockSize = 1024; + int blocksPerGrid = (numToCompute + blockSize - 1) / blockSize; + kernScanSMBC<< > > (numToCompute, dev_odata); + + cudaMemcpy(odata, dev_odata, n * sizeof(int), cudaMemcpyDeviceToHost); + cudaFree(dev_odata); + } + //__global__ void kernScanEachBlock(int n, int *a) { //} diff --git a/stream_compaction/efficient.h b/stream_compaction/efficient.h index 803cb4f..7615728 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 scanSM(int n, int *odata, const int *idata); + int compact(int n, int *odata, const int *idata); } }