From d3844696174a332d5607352dda2e29a24a8e9d34 Mon Sep 17 00:00:00 2001 From: XiaoyuDu Date: Wed, 12 Oct 2022 14:06:15 -0400 Subject: [PATCH 1/3] commit 2 --- src/main.cpp | 5 +- stream_compaction/cpu.cu | 47 ++++++++++- stream_compaction/efficient.cu | 140 ++++++++++++++++++++++++++++++++- stream_compaction/naive.cu | 46 ++++++++++- stream_compaction/thrust.cu | 7 ++ 5 files changed, 239 insertions(+), 6 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 896ac2b..f7f6edc 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]; @@ -139,6 +139,8 @@ int main(int argc, char* argv[]) { printElapsedTime(StreamCompaction::Efficient::timer().getGpuElapsedTimeForPreviousOperation(), "(CUDA Measured)"); //printArray(count, c, true); printCmpLenResult(count, expectedCount, b, c); + + zeroArray(SIZE, c); printDesc("work-efficient compact, non-power-of-two"); @@ -146,6 +148,7 @@ int main(int argc, char* argv[]) { printElapsedTime(StreamCompaction::Efficient::timer().getGpuElapsedTimeForPreviousOperation(), "(CUDA Measured)"); //printArray(count, c, true); printCmpLenResult(count, expectedNPOT, b, c); + system("pause"); // stop Win32 console from closing on exit delete[] a; diff --git a/stream_compaction/cpu.cu b/stream_compaction/cpu.cu index 719fa11..25cb9f9 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] = odata[i - 1] + idata[i - 1]; + } timer().endCpuTimer(); } @@ -31,8 +35,15 @@ namespace StreamCompaction { int compactWithoutScan(int n, int *odata, const int *idata) { timer().startCpuTimer(); // TODO + int num = 0; + for (int i = 0; i < n; ++i) { + if (idata[i] != 0) { + odata[num] = idata[i]; + ++num; + } + } timer().endCpuTimer(); - return -1; + return num; } /** @@ -43,8 +54,40 @@ namespace StreamCompaction { int compactWithScan(int n, int *odata, const int *idata) { timer().startCpuTimer(); // TODO + int* boolArr = new int[n]; + int* scanArr = new int[n]; + int num = 0; + + //build boolArr + for (int i = 0; i < n; ++i) { + if (idata[i] != 0) { + boolArr[i] = 1; + } + else { + boolArr[i] = 0; + } + } + + //build scanArr + scanArr[0] = 0; + for (int i = 1; i < n; ++i) { + scanArr[i] = boolArr[i - 1] + scanArr[i - 1]; + } + + //fill odata + for (int i = 0; i < n; ++i) { + if (boolArr[i] == 1) { + odata[scanArr[i]] = idata[i]; + } + } + + //calculate num to return + num = scanArr[n - 1] + boolArr[n - 1]; + + delete[] boolArr; + delete[] scanArr; timer().endCpuTimer(); - return -1; + return num; } } } diff --git a/stream_compaction/efficient.cu b/stream_compaction/efficient.cu index 2db346e..be89ce0 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,93 @@ namespace StreamCompaction { return timer; } + __global__ void kernUpSweep(int nCeil, int d, int* dev_idata) { + int index = threadIdx.x + (blockIdx.x * blockDim.x); + //increase1 2^(d+1), increase2 2^d + int threadNeeded = 1 << (nCeil - d - 1); + if (index < threadNeeded) { + int increase1 = 1 << (d + 1); + int increase2 = 1 << d; + int multiIdx = index * increase1; + dev_idata[multiIdx + increase1 - 1] += dev_idata[multiIdx + increase2 - 1]; + } + } + + __global__ void kernDownSweep(int nCeil, int d, int* dev_idata){ + int index = threadIdx.x + (blockIdx.x * blockDim.x); + int threadNeeded = 1 << (nCeil - d - 1); + if (index < threadNeeded) { + int increase1 = 1 << (d + 1); + int increase2 = 1 << d; + int multiIdx = index * increase1; + int t = dev_idata[multiIdx + increase2 - 1]; + dev_idata[multiIdx + increase2 - 1] = dev_idata[multiIdx + increase1 - 1]; + dev_idata[multiIdx + increase1 - 1] += t; + } + } + + __global__ void kernMapToBoolean(int n, int* temp_Arr, int* dev_idata) { + int index = threadIdx.x + (blockIdx.x * blockDim.x); + if (index < n) { + if (dev_idata[index] != 0) { + temp_Arr[index] = 1; + } + } + } + + __global__ void kernScatter(int n, int* dev_tempArr, int* dev_finalArr, int* dev_idata) { + int index = threadIdx.x + (blockIdx.x * blockDim.x); + if (index < (n - 1)) { + int currScan = dev_tempArr[index]; + int nextScan = dev_tempArr[index + 1]; + if (currScan < nextScan) { + dev_finalArr[currScan] = dev_idata[index]; + } + } + } + /** * Performs prefix-sum (aka scan) on idata, storing the result into odata. */ void scan(int n, int *odata, const int *idata) { + + //used to round the array sizes to the next power of two. + int nCeil = ilog2ceil(n); + int n2PowCeil = 1 << nCeil; + + int* dev_idata; + cudaMalloc((void**)&dev_idata, n2PowCeil * sizeof(int)); + checkCUDAError("cudaMalloc dev_idata failed!"); + + cudaMemcpy(dev_idata, idata, n * sizeof(int), cudaMemcpyHostToDevice); + checkCUDAError("cudaMemcpy idata to dev_idata failed!"); + //timer start timer().startGpuTimer(); + if (n2PowCeil != n) { + cudaMemset(&(dev_idata[n]), 0, (n2PowCeil - n) * sizeof(int)); + checkCUDAError("cudaMemset failed!"); + } + + //open n threads is enough + dim3 fullBlocksPerGrid((blockSize + n - 1) / blockSize); // TODO + //up-sweep + int depth = ilog2ceil(n2PowCeil) - 1; + for (int d = 0; d <= depth; ++d) { + kernUpSweep << > > (nCeil, d, dev_idata); + } + //down-sweep + cudaMemset(&(dev_idata[n2PowCeil -1]), 0, sizeof(int)); + for (int d = depth; d >= 0; --d) { + kernDownSweep << > > (nCeil, d, dev_idata); + } timer().endGpuTimer(); + + + cudaMemcpy(odata, dev_idata, n * sizeof(int), cudaMemcpyDeviceToHost); + checkCUDAError("memory dev_idata to odata failed!"); + + cudaFree(dev_idata); } /** @@ -31,10 +113,64 @@ namespace StreamCompaction { * @returns The number of elements remaining after compaction. */ int compact(int n, int *odata, const int *idata) { + + int nCeil = ilog2ceil(n); + int n2PowCeil = 1 << nCeil; + int* dev_idata; + int* dev_tempArr; + int* dev_finalArr; + + cudaMalloc((void**)&dev_idata, n2PowCeil * sizeof(int)); + checkCUDAError("cudaMalloc dev_idata failed!"); + + cudaMalloc((void**)&dev_tempArr, n2PowCeil * sizeof(int)); + checkCUDAError("cudaMalloc dev_tempArr failed!"); + + cudaMemcpy(dev_idata, idata, n * sizeof(int), cudaMemcpyHostToDevice); + checkCUDAError("cudaMemcpy from idata to dev_idata failed!"); + + //start timer().startGpuTimer(); - // TODO + if (n2PowCeil != n) { + cudaMemset(&(dev_idata[n]), 0, (n2PowCeil - n) * sizeof(int)); + checkCUDAError("cudaMemset dev_idata failed!"); + } + + cudaMemset(dev_tempArr, 0, n2PowCeil * sizeof(int)); + checkCUDAError("cudaMemset dev_tempArr failed!"); + + dim3 fullBlocksPerGrid((blockSize + n - 1) / blockSize); + + // build boolean array + kernMapToBoolean << > > (n, dev_tempArr, dev_idata); + int lastElement = idata[n - 1]; + + //up-sweep + int depth = ilog2ceil(n2PowCeil) - 1; + for (int d = 0; d <= depth; ++d) { + kernUpSweep << > > (nCeil, d, dev_tempArr); + } + //create final array based on up-sweep result + int numOfResults; + cudaMemcpy(&numOfResults, &(dev_tempArr[n2PowCeil - 1]), sizeof(int), cudaMemcpyDeviceToHost); + cudaMalloc((void**)&dev_finalArr, numOfResults * sizeof(int)); + //down-sweep + cudaMemset(&(dev_tempArr[n2PowCeil - 1]), 0, sizeof(int)); + for (int d = depth; d >= 0; --d) { + kernDownSweep << > > (nCeil, d, dev_tempArr); + } + //scatter + kernScatter << > > (n, dev_tempArr, dev_finalArr, dev_idata); + timer().endGpuTimer(); - return -1; + //end + + cudaMemcpy(odata, dev_finalArr, numOfResults * sizeof(int), cudaMemcpyDeviceToHost); + if (lastElement) { + odata[numOfResults - 1] = lastElement; + } + + return numOfResults; } } } diff --git a/stream_compaction/naive.cu b/stream_compaction/naive.cu index 4308876..cef8fce 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; @@ -12,14 +14,56 @@ namespace StreamCompaction { return timer; } // TODO: __global__ + __global__ void kernNaive(int n, int check, const int* dev_idata, int* dev_odata) { + int index = threadIdx.x + (blockIdx.x * blockDim.x); + if (index < n) { + if (index >= check) { + dev_odata[index] = dev_idata[index - check] + dev_idata[index]; + } + else { + dev_odata[index] = dev_idata[index]; + } + } + } /** * 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_idata; + int* dev_odata; + dim3 fullBlocksPerGrid((blockSize + n - 1) / blockSize); + + cudaMalloc((void**)&dev_idata, n * sizeof(int)); + checkCUDAError("generate dev_temp1 failed!"); + + cudaMalloc((void**)&dev_odata, n * sizeof(int)); + checkCUDAError("generate dev_temp2 failed!"); + + //make dev_idata shift to right by 1 + int identity = 0; + cudaMemcpy(dev_idata, &identity, sizeof(int), cudaMemcpyHostToDevice); + + cudaMemcpy(&dev_idata[1], idata, (n - 1) * sizeof(int), cudaMemcpyHostToDevice); + checkCUDAError("memory copy to dev_idata failed!"); + + timer().startGpuTimer(); + + for (int d = 1; d <= ilog2ceil(n); ++d) { + int check = pow(2, d - 1); + kernNaive << > > (n, check, dev_idata, dev_odata); + std::swap(dev_idata, dev_odata); + } + timer().endGpuTimer(); + + cudaMemcpy(odata, dev_idata, n * sizeof(int), cudaMemcpyDeviceToHost); + checkCUDAError("memory copy to odata failed!"); + + cudaFree(dev_idata); + cudaFree(dev_odata); + } } } diff --git a/stream_compaction/thrust.cu b/stream_compaction/thrust.cu index 1def45e..913230d 100644 --- a/stream_compaction/thrust.cu +++ b/stream_compaction/thrust.cu @@ -22,7 +22,14 @@ namespace StreamCompaction { // 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::host_vector host_thrust_idata(idata, idata+n); + thrust::device_vector dev_thrust_idata(host_thrust_idata); + thrust::exclusive_scan(dev_thrust_idata.begin(), dev_thrust_idata.end(), dev_thrust_idata.begin()); + host_thrust_idata = dev_thrust_idata; timer().endGpuTimer(); + + thrust::copy(host_thrust_idata.begin(), host_thrust_idata.end(), odata); } } } From d6c730e73bcfc98c6487ed0e2f37c9bea98df6ea Mon Sep 17 00:00:00 2001 From: XiaoyuDu Date: Wed, 12 Oct 2022 14:29:33 -0400 Subject: [PATCH 2/3] commit2 --- src/main.cpp | 2 +- stream_compaction/efficient.cu | 23 ++++++++++++++--------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index f7f6edc..af0506a 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 << 27; // 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 be89ce0..bdd4dbc 100644 --- a/stream_compaction/efficient.cu +++ b/stream_compaction/efficient.cu @@ -14,10 +14,9 @@ namespace StreamCompaction { return timer; } - __global__ void kernUpSweep(int nCeil, int d, int* dev_idata) { + __global__ void kernUpSweep(int threadNeeded, int d, int* dev_idata) { int index = threadIdx.x + (blockIdx.x * blockDim.x); //increase1 2^(d+1), increase2 2^d - int threadNeeded = 1 << (nCeil - d - 1); if (index < threadNeeded) { int increase1 = 1 << (d + 1); int increase2 = 1 << d; @@ -26,9 +25,8 @@ namespace StreamCompaction { } } - __global__ void kernDownSweep(int nCeil, int d, int* dev_idata){ + __global__ void kernDownSweep(int threadNeeded, int d, int* dev_idata){ int index = threadIdx.x + (blockIdx.x * blockDim.x); - int threadNeeded = 1 << (nCeil - d - 1); if (index < threadNeeded) { int increase1 = 1 << (d + 1); int increase2 = 1 << d; @@ -82,17 +80,20 @@ namespace StreamCompaction { } //open n threads is enough - dim3 fullBlocksPerGrid((blockSize + n - 1) / blockSize); // TODO //up-sweep int depth = ilog2ceil(n2PowCeil) - 1; for (int d = 0; d <= depth; ++d) { - kernUpSweep << > > (nCeil, d, dev_idata); + int threadNeeded = 1 << (nCeil - d - 1); + dim3 fullBlocksPerGrid((blockSize + threadNeeded - 1) / blockSize); + kernUpSweep << > > (threadNeeded, d, dev_idata); } //down-sweep cudaMemset(&(dev_idata[n2PowCeil -1]), 0, sizeof(int)); for (int d = depth; d >= 0; --d) { - kernDownSweep << > > (nCeil, d, dev_idata); + int threadNeeded = 1 << (nCeil - d - 1); + dim3 fullBlocksPerGrid((blockSize + threadNeeded - 1) / blockSize); + kernDownSweep << > > (threadNeeded, d, dev_idata); } timer().endGpuTimer(); @@ -148,7 +149,9 @@ namespace StreamCompaction { //up-sweep int depth = ilog2ceil(n2PowCeil) - 1; for (int d = 0; d <= depth; ++d) { - kernUpSweep << > > (nCeil, d, dev_tempArr); + int threadNeeded = 1 << (nCeil - d - 1); + dim3 fullBlocksPerGrid((blockSize + threadNeeded - 1) / blockSize); + kernUpSweep << > > (threadNeeded, d, dev_tempArr); } //create final array based on up-sweep result int numOfResults; @@ -157,7 +160,9 @@ namespace StreamCompaction { //down-sweep cudaMemset(&(dev_tempArr[n2PowCeil - 1]), 0, sizeof(int)); for (int d = depth; d >= 0; --d) { - kernDownSweep << > > (nCeil, d, dev_tempArr); + int threadNeeded = 1 << (nCeil - d - 1); + dim3 fullBlocksPerGrid((blockSize + threadNeeded - 1) / blockSize); + kernDownSweep << > > (threadNeeded, d, dev_tempArr); } //scatter kernScatter << > > (n, dev_tempArr, dev_finalArr, dev_idata); From f5c38296458e417c4f4bcfe0623d3bb7c87fa0ef Mon Sep 17 00:00:00 2001 From: XiaoyuDu Date: Wed, 12 Oct 2022 16:39:29 -0400 Subject: [PATCH 3/3] commit 3 --- README.md | 79 ++++++++++++++++++++++++++++++++---- images/1.png | Bin 0 -> 54463 bytes src/main.cpp | 2 +- stream_compaction/thrust.cu | 5 ++- 4 files changed, 76 insertions(+), 10 deletions(-) create mode 100644 images/1.png diff --git a/README.md b/README.md index 0e38ddb..1437571 100644 --- a/README.md +++ b/README.md @@ -3,12 +3,77 @@ 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) +* XiaoyuDu +* Tested on: Windows 10, i9-11900KF @ 3.50GHz, RTX 3080 (Personal PC) + +### Description +This project tested for different method of scan and compact. + +### Feature +I implemented all the features for part 1 - 5. +* CPU Scan & Stream Compaction +* Naive GPU Scan Algorithm +* Work-Efficient GPU Scan & Stream Compaction +* Thrust's Implementation +* GPU Work-Efficient Method Optimization + +### Performance Analysis +My optimized number of blocks is 128. +I campared different method with different size array, and the result plot is shown below. I am a bit confused why my Thrust implementation takes so long to run. I think my implementation should be correct. +![](./images/1.png) -### (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.) +Below is the test result with 2^20 size array. +``` +**************** +** SCAN TESTS ** +**************** + [ 28 9 12 41 33 49 46 3 11 27 35 5 47 ... 8 0 ] +==== cpu scan, power-of-two ==== + elapsed time: 1.7669ms (std::chrono Measured) + [ 0 28 37 49 90 123 172 218 221 232 259 294 299 ... 25674595 25674603 ] +==== cpu scan, non-power-of-two ==== + elapsed time: 1.7544ms (std::chrono Measured) + [ 0 28 37 49 90 123 172 218 221 232 259 294 299 ... 25674502 25674539 ] + passed +==== naive scan, power-of-two ==== + elapsed time: 0.510176ms (CUDA Measured) + passed +==== naive scan, non-power-of-two ==== + elapsed time: 0.695424ms (CUDA Measured) + passed +==== work-efficient scan, power-of-two ==== + elapsed time: 0.43328ms (CUDA Measured) + passed +==== work-efficient scan, non-power-of-two ==== + elapsed time: 0.631104ms (CUDA Measured) + passed +==== thrust scan, power-of-two ==== + elapsed time: 28.3783ms (CUDA Measured) + passed +==== thrust scan, non-power-of-two ==== + elapsed time: 7.89008ms (CUDA Measured) + passed +***************************** +** STREAM COMPACTION TESTS ** +***************************** + [ 1 2 1 1 1 2 0 2 2 3 0 3 3 ... 3 0 ] +==== cpu compact without scan, power-of-two ==== + elapsed time: 3.7818ms (std::chrono Measured) + [ 1 2 1 1 1 2 2 2 3 3 3 2 3 ... 3 3 ] + passed +==== cpu compact without scan, non-power-of-two ==== + elapsed time: 3.5542ms (std::chrono Measured) + [ 1 2 1 1 1 2 2 2 3 3 3 2 3 ... 3 3 ] + passed +==== cpu compact with scan ==== + elapsed time: 9.8808ms (std::chrono Measured) + [ 1 2 1 1 1 2 2 2 3 3 3 2 3 ... 3 3 ] + passed +==== work-efficient compact, power-of-two ==== + elapsed time: 1.07133ms (CUDA Measured) + passed +==== work-efficient compact, non-power-of-two ==== + elapsed time: 0.849984ms (CUDA Measured) + passed +``` \ No newline at end of file diff --git a/images/1.png b/images/1.png new file mode 100644 index 0000000000000000000000000000000000000000..caabacbc5237dcfabe4a585250fb91f0bd7149ba GIT binary patch literal 54463 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 uAOSEjN<5b5EbCcG6PyXBUSfxwQwf_O0U?Woi literal 0 HcmV?d00001 diff --git a/src/main.cpp b/src/main.cpp index af0506a..f494cec 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -13,7 +13,7 @@ #include #include "testing_helpers.hpp" -const int SIZE = 1 << 27; // feel free to change the size of array +const int SIZE = 1 << 20; // 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/thrust.cu b/stream_compaction/thrust.cu index 913230d..b8062e9 100644 --- a/stream_compaction/thrust.cu +++ b/stream_compaction/thrust.cu @@ -18,18 +18,19 @@ 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::host_vector host_thrust_idata(idata, idata+n); thrust::device_vector dev_thrust_idata(host_thrust_idata); + timer().startGpuTimer(); thrust::exclusive_scan(dev_thrust_idata.begin(), dev_thrust_idata.end(), dev_thrust_idata.begin()); - host_thrust_idata = dev_thrust_idata; timer().endGpuTimer(); + host_thrust_idata = dev_thrust_idata; thrust::copy(host_thrust_idata.begin(), host_thrust_idata.end(), odata); + } } }