From 0d4a986f31d267da41376768c50b059256d33dec Mon Sep 17 00:00:00 2001 From: Xiaonan Date: Mon, 15 Sep 2025 23:34:18 -0400 Subject: [PATCH 01/15] CPU implementation --- stream_compaction/cpu.cu | 54 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/stream_compaction/cpu.cu b/stream_compaction/cpu.cu index 719fa115..7754bc5b 100644 --- a/stream_compaction/cpu.cu +++ b/stream_compaction/cpu.cu @@ -12,6 +12,15 @@ namespace StreamCompaction { return timer; } + void scanWithoutTimer(int n, int* odata, const int* idata) { + int sum = 0; + for (int i = 0; i < n; ++i) + { + odata[i] = sum; + sum += idata[i]; + } + } + /** * CPU scan (prefix sum). * For performance analysis, this is supposed to be a simple for loop. @@ -20,6 +29,9 @@ namespace StreamCompaction { void scan(int n, int *odata, const int *idata) { timer().startCpuTimer(); // TODO + + scanWithoutTimer(n, odata, idata); + timer().endCpuTimer(); } @@ -31,8 +43,20 @@ namespace StreamCompaction { int compactWithoutScan(int n, int *odata, const int *idata) { timer().startCpuTimer(); // TODO + int count = 0; + int curr = 0; + for (int i = 0; i < n; ++i) + { + curr = idata[i]; + + if (curr != 0) { + odata[count] = curr; + count++; + } + } + timer().endCpuTimer(); - return -1; + return count; } /** @@ -43,8 +67,34 @@ namespace StreamCompaction { int compactWithScan(int n, int *odata, const int *idata) { timer().startCpuTimer(); // TODO + + int* tempArray = new int[n]; + + for (int i = 0; i < n; ++i) + { + tempArray[i] = (idata[i] != 0) ? 1 : 0; + } + + int* scanResult = new int[n]; + scanWithoutTimer(n, scanResult, tempArray); + + //Scatter + int finalLength = scanResult[n - 1]; + + for (int i = 0; i < n; ++i) + { + if (tempArray[i] == 1) { + odata[scanResult[i]] = idata[i]; + } + } + + + delete[] tempArray; + delete[] scanResult; + + timer().endCpuTimer(); - return -1; + return finalLength; } } } From 0e8d7d5e93a5433b132c1acd6e5fd00484db4d89 Mon Sep 17 00:00:00 2001 From: Xiaonan Date: Tue, 16 Sep 2025 11:30:39 -0400 Subject: [PATCH 02/15] GPU naive --- stream_compaction/naive.cu | 39 +++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/stream_compaction/naive.cu b/stream_compaction/naive.cu index 43088769..67a10d4e 100644 --- a/stream_compaction/naive.cu +++ b/stream_compaction/naive.cu @@ -3,6 +3,8 @@ #include "common.h" #include "naive.h" +#define BLOCK_SIZE 128 + namespace StreamCompaction { namespace Naive { using StreamCompaction::Common::PerformanceTimer; @@ -13,12 +15,47 @@ namespace StreamCompaction { } // TODO: __global__ + __global__ void kernParallelScan(int n, int pow2d, int *odata, const int *idata) { + int k = blockIdx.x * blockDim.x + threadIdx.x; + + if (k >= n) { + return; + } + + if (k >= pow2d){ + odata[k] = idata[k - pow2d] + idata[k]; + } + else { + odata[k] = idata[k]; + } + } + /** * 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 fullBlocksPerGrid((n + BLOCK_SIZE - 1) / BLOCK_SIZE); + + int* dev_A; + int* dev_B; + + cudaMalloc((void**)&dev_A, n * sizeof(int)); + cudaMalloc((void**)&dev_B, n * sizeof(int)); + + + cudaMemcpy(dev_A, idata, n * sizeof(int), cudaMemcpyHostToDevice); + + + for (int d = 0; d < ilog2ceil(n); ++d) + { + kernParallelScan <<>> (n, pow(2, d), dev_B, dev_A); + std::swap(dev_A, dev_B); + } + + cudaMemcpy(odata + 1, dev_A, (n - 1) * sizeof(int), cudaMemcpyDefault); + timer().endGpuTimer(); } } From cc48da2f9526ac82a477210622cd0a134a775f19 Mon Sep 17 00:00:00 2001 From: Xiaonan Date: Tue, 16 Sep 2025 12:06:14 -0400 Subject: [PATCH 03/15] cudaFree --- stream_compaction/naive.cu | 3 +++ 1 file changed, 3 insertions(+) diff --git a/stream_compaction/naive.cu b/stream_compaction/naive.cu index 67a10d4e..2d3840a5 100644 --- a/stream_compaction/naive.cu +++ b/stream_compaction/naive.cu @@ -56,6 +56,9 @@ namespace StreamCompaction { cudaMemcpy(odata + 1, dev_A, (n - 1) * sizeof(int), cudaMemcpyDefault); + cudaFree(dev_A); + cudaFree(dev_B); + timer().endGpuTimer(); } } From b6171364b473370ea98d2deb6aa508d99d6c9182 Mon Sep 17 00:00:00 2001 From: Xiaonan Date: Tue, 16 Sep 2025 13:40:53 -0400 Subject: [PATCH 04/15] work-efficient scan --- stream_compaction/efficient.cu | 65 +++++++++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/stream_compaction/efficient.cu b/stream_compaction/efficient.cu index 2db346ee..d35550de 100644 --- a/stream_compaction/efficient.cu +++ b/stream_compaction/efficient.cu @@ -3,6 +3,8 @@ #include "common.h" #include "efficient.h" +#define BLOCK_SIZE 128 + namespace StreamCompaction { namespace Efficient { using StreamCompaction::Common::PerformanceTimer; @@ -12,12 +14,73 @@ namespace StreamCompaction { return timer; } + __global__ void kernWorkEfficientUpSweep(int paddedN, int n, int d, int* idata) { + int k = blockIdx.x * blockDim.x + threadIdx.x; + + int halfStride = 1 << (d); + int fullStride = halfStride * 2; + + if (k >= n || ((k + 1) % fullStride != 0)) { + return; + } + + // up-sweep + idata[k] += idata[k - halfStride]; + } + + __global__ void setLastElementToZero(int paddedN, int* idata) + { + idata[paddedN - 1] = 0; + } + + __global__ void kernWorkEfficientDownSweep(int paddedN, int n, int d, int* idata) { + int k = blockIdx.x * blockDim.x + threadIdx.x; + + int halfStride = 1 << (d); + int fullStride = halfStride * 2; + + if (k >= paddedN || ((k + 1) % fullStride != 0)) { + return; + } + + // down-sweep + int originalLeftChildValue = idata[k - halfStride]; + int parentValue = idata[k]; + + idata[k - halfStride] = parentValue ; + idata[k] = parentValue + originalLeftChildValue; + } + + /** * 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 paddedN = 1 << ilog2ceil(n); + dim3 fullBlocksPerGrid((paddedN + BLOCK_SIZE - 1) / BLOCK_SIZE); + + int* dev_idata; + + cudaMalloc((void**)&dev_idata, paddedN * sizeof(int)); + cudaMemset(dev_idata, 0, paddedN * sizeof(int)); + cudaMemcpy(dev_idata, idata, n * sizeof(int), cudaMemcpyHostToDevice); + + for (int d = 0; d <= ilog2ceil(n) - 1; d++) { + kernWorkEfficientUpSweep << > > (paddedN, n, d, dev_idata); + } + + setLastElementToZero << <1, 1 >> > (paddedN, dev_idata); + + for (int d = ilog2ceil(n) - 1; d >= 0; d--) { + kernWorkEfficientDownSweep << > > (paddedN, n, d, dev_idata); + } + + cudaMemcpy(odata, dev_idata, n * sizeof(int), cudaMemcpyDeviceToHost); + + cudaFree(dev_idata); + timer().endGpuTimer(); } From ec71f5a108f7e6cd3452fe3ba43bfcfc3f03b2c3 Mon Sep 17 00:00:00 2001 From: Xiaonan Date: Tue, 16 Sep 2025 14:36:38 -0400 Subject: [PATCH 05/15] Stream Compaction --- stream_compaction/efficient.cu | 94 ++++++++++++++++++++++++++++------ 1 file changed, 77 insertions(+), 17 deletions(-) diff --git a/stream_compaction/efficient.cu b/stream_compaction/efficient.cu index d35550de..d17c18c5 100644 --- a/stream_compaction/efficient.cu +++ b/stream_compaction/efficient.cu @@ -51,21 +51,15 @@ namespace StreamCompaction { idata[k] = parentValue + originalLeftChildValue; } - - /** - * Performs prefix-sum (aka scan) on idata, storing the result into odata. - */ - void scan(int n, int *odata, const int *idata) { - timer().startGpuTimer(); - + void scanWithoutTimer(int n, int* odata, const int* idata) { int paddedN = 1 << ilog2ceil(n); dim3 fullBlocksPerGrid((paddedN + BLOCK_SIZE - 1) / BLOCK_SIZE); - - int* dev_idata; - cudaMalloc((void**)&dev_idata, paddedN * sizeof(int)); - cudaMemset(dev_idata, 0, paddedN * sizeof(int)); - cudaMemcpy(dev_idata, idata, n * sizeof(int), cudaMemcpyHostToDevice); + int* dev_idata; + + cudaMalloc((void**)&dev_idata, paddedN * sizeof(int)); + cudaMemset(dev_idata, 0, paddedN * sizeof(int)); + cudaMemcpy(dev_idata, idata, n * sizeof(int), cudaMemcpyHostToDevice); for (int d = 0; d <= ilog2ceil(n) - 1; d++) { kernWorkEfficientUpSweep << > > (paddedN, n, d, dev_idata); @@ -77,13 +71,47 @@ namespace StreamCompaction { kernWorkEfficientDownSweep << > > (paddedN, n, d, dev_idata); } - cudaMemcpy(odata, dev_idata, n * sizeof(int), cudaMemcpyDeviceToHost); - - cudaFree(dev_idata); + cudaMemcpy(odata, dev_idata, n * sizeof(int), cudaMemcpyDeviceToHost); + cudaFree(dev_idata); + } + + /** + * Performs prefix-sum (aka scan) on idata, storing the result into odata. + */ + void scan(int n, int *odata, const int *idata) { + timer().startGpuTimer(); + scanWithoutTimer(n, odata, idata); timer().endGpuTimer(); } + __global__ void kernComputeCompactFlag(int n, int *odata, const int* idata) + { + int idx = blockIdx.x * blockDim.x + threadIdx.x; + + if (idx >= n) { + return; + } + + if (idata[idx] != 0) { + odata[idx] = 1; + } + else { + odata[idx] = 0; + } + } + + __global__ void kernCompactScatter(int n, int* odata, const int* dev_idata, const int* flags, const int* scanResult) + { + int idx = blockIdx.x * blockDim.x + threadIdx.x; + + if (idx >= n || flags[idx] == 0) { + return; + } + + odata[scanResult[idx]] = dev_idata[idx]; + } + /** * Performs stream compaction on idata, storing the result into odata. * All zeroes are discarded. @@ -95,9 +123,41 @@ namespace StreamCompaction { */ int compact(int n, int *odata, const int *idata) { timer().startGpuTimer(); - // TODO + + dim3 fullBlocksPerGrid((n + BLOCK_SIZE - 1) / BLOCK_SIZE); + + int* dev_idata; + int* dev_flags; + int* dev_scanResult; + int* dev_odata; + + cudaMalloc((void**)&dev_idata, n * sizeof(int)); + cudaMalloc((void**)&dev_flags, n * sizeof(int)); + cudaMalloc((void**)&dev_scanResult, n * sizeof(int)); + + cudaMemcpy(dev_idata, idata, n * sizeof(int), cudaMemcpyHostToDevice); + + //compute the temporary array + kernComputeCompactFlag<< > > (n, dev_flags, dev_idata); + cudaMemcpy(odata, dev_flags, n * sizeof(int), cudaMemcpyDeviceToHost); + + //compute the scan of the temporary array + int* host_scanResult = new int[n]; + scanWithoutTimer(n, host_scanResult, odata); + cudaMemcpy(dev_scanResult, host_scanResult, n * sizeof(int), cudaMemcpyHostToDevice); + + //compute the scatter + int totalCount = host_scanResult[n - 1] + odata[n - 1]; + cudaMalloc((void**)&dev_odata, totalCount * sizeof(int)); + kernCompactScatter<< > > (n, dev_odata, dev_idata, dev_flags, dev_scanResult); + + cudaMemcpy(odata, dev_odata, totalCount * sizeof(int), cudaMemcpyDeviceToHost); + + + cudaFree(dev_flags); + timer().endGpuTimer(); - return -1; + return totalCount; } } } From 39a2277d17256134cd22bcf79b7824aa06bf0a2d Mon Sep 17 00:00:00 2001 From: Xiaonan Date: Tue, 16 Sep 2025 15:07:18 -0400 Subject: [PATCH 06/15] optimize the scan() --- stream_compaction/efficient.cu | 60 ++++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 18 deletions(-) diff --git a/stream_compaction/efficient.cu b/stream_compaction/efficient.cu index d17c18c5..9e2cf5ef 100644 --- a/stream_compaction/efficient.cu +++ b/stream_compaction/efficient.cu @@ -50,30 +50,48 @@ namespace StreamCompaction { idata[k - halfStride] = parentValue ; idata[k] = parentValue + originalLeftChildValue; } - - void scanWithoutTimer(int n, int* odata, const int* idata) { + + void scan_device(int n, int* dev_odata, const int* dev_idata) { int paddedN = 1 << ilog2ceil(n); dim3 fullBlocksPerGrid((paddedN + BLOCK_SIZE - 1) / BLOCK_SIZE); - int* dev_idata; - - cudaMalloc((void**)&dev_idata, paddedN * sizeof(int)); - cudaMemset(dev_idata, 0, paddedN * sizeof(int)); - cudaMemcpy(dev_idata, idata, n * sizeof(int), cudaMemcpyHostToDevice); + int* dev_temp; // Use a temporary buffer for the in-place algorithm + cudaMalloc((void**)&dev_temp, paddedN * sizeof(int)); + cudaMemset(dev_temp, 0, paddedN * sizeof(int)); + cudaMemcpy(dev_temp, dev_idata, n * sizeof(int), cudaMemcpyDeviceToDevice); - for (int d = 0; d <= ilog2ceil(n) - 1; d++) { - kernWorkEfficientUpSweep << > > (paddedN, n, d, dev_idata); + // Up-Sweep + for (int d = 0; d < ilog2ceil(n); d++) { + kernWorkEfficientUpSweep << > > (paddedN, n, d, dev_temp); } - setLastElementToZero << <1, 1 >> > (paddedN, dev_idata); + setLastElementToZero << <1, 1 >> > (paddedN, dev_temp); + // Down-Sweep for (int d = ilog2ceil(n) - 1; d >= 0; d--) { - kernWorkEfficientDownSweep << > > (paddedN, n, d, dev_idata); + kernWorkEfficientDownSweep << > > (paddedN, n, d, dev_temp); } - cudaMemcpy(odata, dev_idata, n * sizeof(int), cudaMemcpyDeviceToHost); + cudaMemcpy(dev_odata, dev_temp, n * sizeof(int), cudaMemcpyDeviceToDevice); + + cudaFree(dev_temp); + } + + void scanWithoutTimer(int n, int* odata, const int* idata) { + int* dev_idata; + int* dev_odata; + + cudaMalloc((void**)&dev_idata, n * sizeof(int)); + cudaMalloc((void**)&dev_odata, n * sizeof(int)); + + cudaMemcpy(dev_idata, idata, n * sizeof(int), cudaMemcpyHostToDevice); + + scan_device(n, dev_odata, dev_idata); + + cudaMemcpy(odata, dev_odata, n * sizeof(int), cudaMemcpyDeviceToHost); cudaFree(dev_idata); + cudaFree(dev_odata); } /** @@ -139,22 +157,28 @@ namespace StreamCompaction { //compute the temporary array kernComputeCompactFlag<< > > (n, dev_flags, dev_idata); - cudaMemcpy(odata, dev_flags, n * sizeof(int), cudaMemcpyDeviceToHost); //compute the scan of the temporary array - int* host_scanResult = new int[n]; - scanWithoutTimer(n, host_scanResult, odata); - cudaMemcpy(dev_scanResult, host_scanResult, n * sizeof(int), cudaMemcpyHostToDevice); + scan_device(n, dev_scanResult, dev_flags); //compute the scatter - int totalCount = host_scanResult[n - 1] + odata[n - 1]; + int lastElementOfScan = 0; + int lastElementOfFlags = 0; + cudaMemcpy(&lastElementOfScan, &dev_scanResult[n - 1], sizeof(int), cudaMemcpyDeviceToHost); + cudaMemcpy(&lastElementOfFlags, &dev_flags[n - 1], sizeof(int), cudaMemcpyDeviceToHost); + + + int totalCount = lastElementOfScan + lastElementOfFlags; cudaMalloc((void**)&dev_odata, totalCount * sizeof(int)); kernCompactScatter<< > > (n, dev_odata, dev_idata, dev_flags, dev_scanResult); cudaMemcpy(odata, dev_odata, totalCount * sizeof(int), cudaMemcpyDeviceToHost); - + cudaFree(dev_idata); cudaFree(dev_flags); + cudaFree(dev_idata); + cudaFree(dev_odata); + timer().endGpuTimer(); return totalCount; From fe372e6f2e64bec7732763d9f4b57db9c1b815b3 Mon Sep 17 00:00:00 2001 From: Xiaonan Date: Tue, 16 Sep 2025 15:13:24 -0400 Subject: [PATCH 07/15] move code to common.cu --- stream_compaction/common.cu | 21 +++++++++++++++++++-- stream_compaction/efficient.cu | 31 ++----------------------------- 2 files changed, 21 insertions(+), 31 deletions(-) diff --git a/stream_compaction/common.cu b/stream_compaction/common.cu index 2ed6d630..fa818af3 100644 --- a/stream_compaction/common.cu +++ b/stream_compaction/common.cu @@ -23,7 +23,18 @@ 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 = blockIdx.x * blockDim.x + threadIdx.x; + + if (idx >= n) { + return; + } + + if (idata[idx] != 0) { + bools[idx] = 1; + } + else { + bools[idx] = 0; + } } /** @@ -32,7 +43,13 @@ namespace StreamCompaction { */ __global__ void kernScatter(int n, int *odata, const int *idata, const int *bools, const int *indices) { - // TODO + int idx = blockIdx.x * blockDim.x + threadIdx.x; + + if (idx >= n || bools[idx] == 0) { + return; + } + + odata[indices[idx]] = idata[idx]; } } diff --git a/stream_compaction/efficient.cu b/stream_compaction/efficient.cu index 9e2cf5ef..e57ed23d 100644 --- a/stream_compaction/efficient.cu +++ b/stream_compaction/efficient.cu @@ -103,33 +103,6 @@ namespace StreamCompaction { timer().endGpuTimer(); } - __global__ void kernComputeCompactFlag(int n, int *odata, const int* idata) - { - int idx = blockIdx.x * blockDim.x + threadIdx.x; - - if (idx >= n) { - return; - } - - if (idata[idx] != 0) { - odata[idx] = 1; - } - else { - odata[idx] = 0; - } - } - - __global__ void kernCompactScatter(int n, int* odata, const int* dev_idata, const int* flags, const int* scanResult) - { - int idx = blockIdx.x * blockDim.x + threadIdx.x; - - if (idx >= n || flags[idx] == 0) { - return; - } - - odata[scanResult[idx]] = dev_idata[idx]; - } - /** * Performs stream compaction on idata, storing the result into odata. * All zeroes are discarded. @@ -156,7 +129,7 @@ namespace StreamCompaction { cudaMemcpy(dev_idata, idata, n * sizeof(int), cudaMemcpyHostToDevice); //compute the temporary array - kernComputeCompactFlag<< > > (n, dev_flags, dev_idata); + StreamCompaction::Common::kernMapToBoolean << > > (n, dev_flags, dev_idata); //compute the scan of the temporary array scan_device(n, dev_scanResult, dev_flags); @@ -170,7 +143,7 @@ namespace StreamCompaction { int totalCount = lastElementOfScan + lastElementOfFlags; cudaMalloc((void**)&dev_odata, totalCount * sizeof(int)); - kernCompactScatter<< > > (n, dev_odata, dev_idata, dev_flags, dev_scanResult); + StreamCompaction::Common::kernScatter << > > (n, dev_odata, dev_idata, dev_flags, dev_scanResult); cudaMemcpy(odata, dev_odata, totalCount * sizeof(int), cudaMemcpyDeviceToHost); From f40139bc0f6fa8bde66d2a7885259459ce6dad02 Mon Sep 17 00:00:00 2001 From: Xiaonan Date: Tue, 16 Sep 2025 15:31:04 -0400 Subject: [PATCH 08/15] thrust --- stream_compaction/thrust.cu | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/stream_compaction/thrust.cu b/stream_compaction/thrust.cu index 1def45e7..470334ee 100644 --- a/stream_compaction/thrust.cu +++ b/stream_compaction/thrust.cu @@ -18,11 +18,18 @@ namespace StreamCompaction { * Performs prefix-sum (aka scan) on idata, storing the result into odata. */ void scan(int n, int *odata, const int *idata) { + thrust::host_vector host_idata(idata, idata + n); + thrust::device_vector dev_idata = host_idata; + thrust::device_vector dev_odata(n); + 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(dev_idata.begin(), dev_idata.end(), dev_odata.begin()); timer().endGpuTimer(); + + thrust::copy(dev_odata.begin(), dev_odata.end(), odata); } } } From 58482c65a41f44664f5e4a269cdb0bc70347538e Mon Sep 17 00:00:00 2001 From: Xiaonan Date: Tue, 16 Sep 2025 15:52:01 -0400 Subject: [PATCH 09/15] modify the position of timer --- stream_compaction/efficient.cu | 6 ++---- stream_compaction/naive.cu | 6 +++--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/stream_compaction/efficient.cu b/stream_compaction/efficient.cu index e57ed23d..c8f2cf9f 100644 --- a/stream_compaction/efficient.cu +++ b/stream_compaction/efficient.cu @@ -113,7 +113,6 @@ namespace StreamCompaction { * @returns The number of elements remaining after compaction. */ int compact(int n, int *odata, const int *idata) { - timer().startGpuTimer(); dim3 fullBlocksPerGrid((n + BLOCK_SIZE - 1) / BLOCK_SIZE); @@ -129,6 +128,7 @@ namespace StreamCompaction { cudaMemcpy(dev_idata, idata, n * sizeof(int), cudaMemcpyHostToDevice); //compute the temporary array + timer().startGpuTimer(); StreamCompaction::Common::kernMapToBoolean << > > (n, dev_flags, dev_idata); //compute the scan of the temporary array @@ -144,7 +144,7 @@ namespace StreamCompaction { int totalCount = lastElementOfScan + lastElementOfFlags; cudaMalloc((void**)&dev_odata, totalCount * sizeof(int)); StreamCompaction::Common::kernScatter << > > (n, dev_odata, dev_idata, dev_flags, dev_scanResult); - + timer().endGpuTimer(); cudaMemcpy(odata, dev_odata, totalCount * sizeof(int), cudaMemcpyDeviceToHost); cudaFree(dev_idata); @@ -152,8 +152,6 @@ namespace StreamCompaction { cudaFree(dev_idata); cudaFree(dev_odata); - - timer().endGpuTimer(); return totalCount; } } diff --git a/stream_compaction/naive.cu b/stream_compaction/naive.cu index 2d3840a5..cd155fc8 100644 --- a/stream_compaction/naive.cu +++ b/stream_compaction/naive.cu @@ -27,14 +27,13 @@ namespace StreamCompaction { } else { odata[k] = idata[k]; - } + } } /** * Performs prefix-sum (aka scan) on idata, storing the result into odata. */ void scan(int n, int *odata, const int *idata) { - timer().startGpuTimer(); dim3 fullBlocksPerGrid((n + BLOCK_SIZE - 1) / BLOCK_SIZE); @@ -47,19 +46,20 @@ namespace StreamCompaction { cudaMemcpy(dev_A, idata, n * sizeof(int), cudaMemcpyHostToDevice); + timer().startGpuTimer(); for (int d = 0; d < ilog2ceil(n); ++d) { kernParallelScan <<>> (n, pow(2, d), dev_B, dev_A); std::swap(dev_A, dev_B); } + timer().endGpuTimer(); cudaMemcpy(odata + 1, dev_A, (n - 1) * sizeof(int), cudaMemcpyDefault); cudaFree(dev_A); cudaFree(dev_B); - timer().endGpuTimer(); } } } From dad0a5178092aa7892609150ffba12dca24d783f Mon Sep 17 00:00:00 2001 From: Xiaonan Date: Tue, 16 Sep 2025 16:09:50 -0400 Subject: [PATCH 10/15] optimize upsweep, downsweep --- stream_compaction/efficient.cu | 64 +++++++++++++++++++++++++++++++--- 1 file changed, 60 insertions(+), 4 deletions(-) diff --git a/stream_compaction/efficient.cu b/stream_compaction/efficient.cu index c8f2cf9f..18f7ceba 100644 --- a/stream_compaction/efficient.cu +++ b/stream_compaction/efficient.cu @@ -28,6 +28,22 @@ namespace StreamCompaction { idata[k] += idata[k - halfStride]; } + __global__ void kernUpSweep(int n, int d, int* data) { + // k is the ID of the k-th active thread (dense) + int k = blockIdx.x * blockDim.x + threadIdx.x; + + int fullStride = 1 << (d + 1); + int halfStride = 1 << d; + + int global_idx = (k + 1) * fullStride - 1; + + if (global_idx >= n) { + return; + } + + data[global_idx] += data[global_idx - halfStride]; + } + __global__ void setLastElementToZero(int paddedN, int* idata) { idata[paddedN - 1] = 0; @@ -50,6 +66,26 @@ namespace StreamCompaction { idata[k - halfStride] = parentValue ; idata[k] = parentValue + originalLeftChildValue; } + + __global__ void kernDownSweep(int n, int d, int* data) { + int k = blockIdx.x * blockDim.x + threadIdx.x; + + int fullStride = 1 << (d + 1); + int halfStride = 1 << d; + + int global_idx = (k + 1) * fullStride - 1; + + if (global_idx >= n) { + return; + } + + // Standard down-sweep logic using the calculated global index + int originalLeftChildValue = data[global_idx - halfStride]; + int parentValue = data[global_idx]; + + data[global_idx - halfStride] = parentValue; + data[global_idx] = parentValue + originalLeftChildValue; + } void scan_device(int n, int* dev_odata, const int* dev_idata) { int paddedN = 1 << ilog2ceil(n); @@ -60,16 +96,36 @@ namespace StreamCompaction { cudaMemset(dev_temp, 0, paddedN * sizeof(int)); cudaMemcpy(dev_temp, dev_idata, n * sizeof(int), cudaMemcpyDeviceToDevice); - // Up-Sweep + //// Up-Sweep + //for (int d = 0; d < ilog2ceil(n); d++) { + // kernWorkEfficientUpSweep << > > (paddedN, n, d, dev_temp); + //} + + //setLastElementToZero << <1, 1 >> > (paddedN, dev_temp); + + //// Down-Sweep + //for (int d = ilog2ceil(n) - 1; d >= 0; d--) { + // kernWorkEfficientDownSweep << > > (paddedN, n, d, dev_temp); + //} + + for (int d = 0; d < ilog2ceil(n); d++) { - kernWorkEfficientUpSweep << > > (paddedN, n, d, dev_temp); + // Calculate how many threads are actually needed for this pass + int numActiveThreads = paddedN / (1 << (d + 1)); + dim3 gridDim((numActiveThreads + BLOCK_SIZE - 1) / BLOCK_SIZE); + dim3 blockDim(BLOCK_SIZE); + + kernUpSweep << > > (paddedN, d, dev_temp); } setLastElementToZero << <1, 1 >> > (paddedN, dev_temp); - // Down-Sweep for (int d = ilog2ceil(n) - 1; d >= 0; d--) { - kernWorkEfficientDownSweep << > > (paddedN, n, d, dev_temp); + int numActiveThreads = paddedN / (1 << (d + 1)); + dim3 gridDim((numActiveThreads + BLOCK_SIZE - 1) / BLOCK_SIZE); + dim3 blockDim(BLOCK_SIZE); + + kernDownSweep << > > (paddedN, d, dev_temp); } cudaMemcpy(dev_odata, dev_temp, n * sizeof(int), cudaMemcpyDeviceToDevice); From ffdb33b9c7c13706ab3ee952e968ae8a11b1bd14 Mon Sep 17 00:00:00 2001 From: Xiaonan Date: Tue, 16 Sep 2025 16:21:02 -0400 Subject: [PATCH 11/15] add radix.h, radix.cu --- stream_compaction/CMakeLists.txt | 2 ++ stream_compaction/radix.cu | 47 ++++++++++++++++++++++++++++++++ stream_compaction/radix.h | 11 ++++++++ 3 files changed, 60 insertions(+) create mode 100644 stream_compaction/radix.cu create mode 100644 stream_compaction/radix.h diff --git a/stream_compaction/CMakeLists.txt b/stream_compaction/CMakeLists.txt index 19511caa..7378625a 100644 --- a/stream_compaction/CMakeLists.txt +++ b/stream_compaction/CMakeLists.txt @@ -4,6 +4,7 @@ set(headers "naive.h" "efficient.h" "thrust.h" + "radix.h" ) set(sources @@ -12,6 +13,7 @@ set(sources "naive.cu" "efficient.cu" "thrust.cu" + "radix.cu" ) list(SORT headers) diff --git a/stream_compaction/radix.cu b/stream_compaction/radix.cu new file mode 100644 index 00000000..90fac870 --- /dev/null +++ b/stream_compaction/radix.cu @@ -0,0 +1,47 @@ +#include +#include +#include "common.h" +#include "radix.h" +#include "efficient.h" + +#define BLOCK_SIZE 128 + +namespace StreamCompaction { + namespace Radix { + using StreamCompaction::Common::PerformanceTimer; + PerformanceTimer& timer() + { + static PerformanceTimer timer; + return timer; + } + + void sort(int n, int *odata, const int *idata) { + + dim3 fullBlocksPerGrid((n + BLOCK_SIZE - 1) / BLOCK_SIZE); + + int* dev_A; + int* dev_B; + + cudaMalloc((void**)&dev_A, n * sizeof(int)); + cudaMalloc((void**)&dev_B, n * sizeof(int)); + + + cudaMemcpy(dev_A, idata, n * sizeof(int), cudaMemcpyHostToDevice); + + timer().startGpuTimer(); + + for (int d = 0; d < ilog2ceil(n); ++d) + { + //kernParallelScan <<>> (n, pow(2, d), dev_B, dev_A); + std::swap(dev_A, dev_B); + } + timer().endGpuTimer(); + + cudaMemcpy(odata + 1, dev_A, (n - 1) * sizeof(int), cudaMemcpyDefault); + + cudaFree(dev_A); + cudaFree(dev_B); + + } + } +} diff --git a/stream_compaction/radix.h b/stream_compaction/radix.h new file mode 100644 index 00000000..aa489d93 --- /dev/null +++ b/stream_compaction/radix.h @@ -0,0 +1,11 @@ +#pragma once + +#include "common.h" + +namespace StreamCompaction { + namespace Radix { + StreamCompaction::Common::PerformanceTimer& timer(); + + void sort(int n, int *odata, const int *idata); + } +} From 32788fc094d668e8a4a8b78ae54dba2878e0d544 Mon Sep 17 00:00:00 2001 From: Xiaonan Date: Tue, 16 Sep 2025 20:22:36 -0400 Subject: [PATCH 12/15] radix sort --- src/main.cpp | 37 +++++++++++++++- stream_compaction/efficient.h | 2 + stream_compaction/radix.cu | 80 +++++++++++++++++++++++++++++------ 3 files changed, 105 insertions(+), 14 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 3d5c8820..c1d23262 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include "testing_helpers.hpp" const int SIZE = 1 << 8; // feel free to change the size of array @@ -147,7 +148,41 @@ int main(int argc, char* argv[]) { //printArray(count, c, true); printCmpLenResult(count, expectedNPOT, b, c); - system("pause"); // stop Win32 console from closing on exit + printf("\n"); + printf("*****************************\n"); + printf("** RADIX TESTS **\n"); + printf("*****************************\n"); + + genArray(SIZE, a, 1000); // Generate a new array with a larger range of numbers + printArray(SIZE, a, true); + + memcpy(b, a, SIZE * sizeof(int)); // Copy a into b + printDesc("cpu sort (std::sort), power-of-two"); + auto start_time = std::chrono::high_resolution_clock::now(); + std::sort(b, b + SIZE); // Sort b to create the correct reference answer + auto end_time = std::chrono::high_resolution_clock::now(); + std::chrono::duration elapsed = end_time - start_time; + printf(" elapsed time: %.4fms (std::chrono Measured)\n", elapsed.count()); + printArray(SIZE, b, true); + + zeroArray(SIZE, c); + printDesc("radix sort, power-of-two"); + StreamCompaction::Radix::sort(SIZE, c, a); + printElapsedTime(StreamCompaction::Radix::timer().getGpuElapsedTimeForPreviousOperation(), "(CUDA Measured)"); + printCmpResult(SIZE, b, c); + + memcpy(b, a, NPOT * sizeof(int)); + std::sort(b, b + NPOT); + + + zeroArray(SIZE, c); + printDesc("radix sort, non-power-of-two"); + StreamCompaction::Radix::sort(NPOT, c, a); + printElapsedTime(StreamCompaction::Radix::timer().getGpuElapsedTimeForPreviousOperation(), "(CUDA Measured)"); + printCmpResult(NPOT, b, c); + + + system("pause"); delete[] a; delete[] b; delete[] c; diff --git a/stream_compaction/efficient.h b/stream_compaction/efficient.h index 803cb4fe..1957cc76 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 scan_device(int n, int* dev_odata, const int* dev_idata); + int compact(int n, int *odata, const int *idata); } } diff --git a/stream_compaction/radix.cu b/stream_compaction/radix.cu index 90fac870..a1ff4966 100644 --- a/stream_compaction/radix.cu +++ b/stream_compaction/radix.cu @@ -15,33 +15,87 @@ namespace StreamCompaction { return timer; } + __global__ void kernRadixComputeE(int n, int d, int* eArr, const int* data) { + int k = blockIdx.x * blockDim.x + threadIdx.x; + if (k >= n) return; + + eArr[k] = ((data[k] >> d) & 1) == 0 ? 1 : 0; + } + + __global__ void kernRadixScatter(int n, int d, int totalFalses, int* odata, const int* idata, const int* fArr) { + int k = blockIdx.x * blockDim.x + threadIdx.x; + if (k >= n) return; + + int element = idata[k]; + int bit_is_false = ((element >> d) & 1) == 0; // 1 if bit is 0, 0 if bit is 1 + + int new_idx; + if (bit_is_false) { + // If the bit is 0 (false), its new position is given by the + // exclusive scan result 'fArr[k]'. This corresponds to f[i] in your image. + new_idx = fArr[k]; + } + else { + // If the bit is 1 (true), this calculates its destination address. + // This corresponds to t[i] = i - f[i] + totalFalses from your image. + int ones_offset = k - fArr[k]; + new_idx = totalFalses + ones_offset; + } + + odata[new_idx] = element; + } + void sort(int n, int *odata, const int *idata) { dim3 fullBlocksPerGrid((n + BLOCK_SIZE - 1) / BLOCK_SIZE); - int* dev_A; - int* dev_B; + int* dev_bufferA; + int* dev_bufferB; + int* dev_eArr; + int* dev_fArr; + + cudaMalloc((void**)&dev_bufferA, n * sizeof(int)); + cudaMalloc((void**)&dev_bufferB, n * sizeof(int)); + cudaMalloc((void**)&dev_eArr, n * sizeof(int)); + cudaMalloc((void**)&dev_fArr, n * sizeof(int)); - cudaMalloc((void**)&dev_A, n * sizeof(int)); - cudaMalloc((void**)&dev_B, n * sizeof(int)); + cudaMemcpy(dev_bufferA, idata, n * sizeof(int), cudaMemcpyHostToDevice); - cudaMemcpy(dev_A, idata, n * sizeof(int), cudaMemcpyHostToDevice); + int* dev_input = dev_bufferA; + int* dev_output = dev_bufferB; timer().startGpuTimer(); - for (int d = 0; d < ilog2ceil(n); ++d) - { - //kernParallelScan <<>> (n, pow(2, d), dev_B, dev_A); - std::swap(dev_A, dev_B); + for (int d = 0; d < sizeof(int) * 8; ++d) { + // e array + kernRadixComputeE << > > (n, d, dev_eArr, dev_input); + + // exclusive scan to get f array + StreamCompaction::Efficient::scan_device(n, dev_fArr, dev_eArr); + + // total count of zero + int totalFalses = 0; + if (n > 0) { + int last_idx, last_flag; + cudaMemcpy(&last_idx, &dev_fArr[n - 1], sizeof(int), cudaMemcpyDeviceToHost); + cudaMemcpy(&last_flag, &dev_eArr[n - 1], sizeof(int), cudaMemcpyDeviceToHost); + totalFalses = last_idx + last_flag; + } + + kernRadixScatter << > > (n, d, totalFalses, dev_output, dev_input, dev_fArr); + + std::swap(dev_input, dev_output); } - timer().endGpuTimer(); - cudaMemcpy(odata + 1, dev_A, (n - 1) * sizeof(int), cudaMemcpyDefault); + timer().endGpuTimer(); - cudaFree(dev_A); - cudaFree(dev_B); + cudaMemcpy(odata, dev_input, n * sizeof(int), cudaMemcpyDeviceToHost); + cudaFree(dev_bufferA); + cudaFree(dev_bufferB); + cudaFree(dev_eArr); + cudaFree(dev_fArr); } } } From 47af394b4084e9ba8358fa48d4072d0b80d7d482 Mon Sep 17 00:00:00 2001 From: Xiaonan Date: Tue, 16 Sep 2025 23:50:10 -0400 Subject: [PATCH 13/15] update readme --- README.md | 203 ++++++++++++++++++++++++++++++++- img/plot.png | Bin 0 -> 106419 bytes img/test.png | Bin 0 -> 25719 bytes src/main.cpp | 2 +- stream_compaction/efficient.cu | 50 +++++++- 5 files changed, 245 insertions(+), 10 deletions(-) create mode 100644 img/plot.png create mode 100644 img/test.png diff --git a/README.md b/README.md index 0e38ddb1..0c84e46f 100644 --- a/README.md +++ b/README.md @@ -3,12 +3,203 @@ 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) +![Stream Compaction Tests](./img/test.png) -### (TODO: Your README) + * Xiaonan Pan + * [LinkedIn](https://www.linkedin.com/in/xiaonan-pan-9b0b0b1a7), [My Blog](www.tsingloo.com), [GitHub](https://github.com/TsingLoo) + * Tested on: + * Windows 11 24H2 + * 13600KF @ 3.5Ghz + * 4070 SUPER 12GB + * 32GB RAM -Include analysis, etc. (Remember, this is public, so don't put -anything here that you don't want to share with the world.) +# Overview + +This project provides thoroughly tested CPU and GPU (CUDA) implementations of parallel **scan** (exclusive and inclusive) and **stream compaction** algorithms. Additionally, a CUDA-based **radix sort** is implemented and tested. The CUDA scan implementation specifically explores and contrasts both naive and work-efficient approaches. + + + +# Implementation + +Most of the GPU scan implementations for this project were based on the pseudocode provided in the lecture slides. Throughout the coding process, I found the mental transition between the serial "scheduler's view" and the parallel "thread's view" to be a fresh and critical challenge. For example, the work-efficient up-sweep algorithm is presented from a high-level, scheduler's perspective: + +```cpp +for d = 0 to log_2{n} - 1 + for all k = 0 to n – 1 by 2^(d+1) in parallel + x[k + 2^(d+1) – 1] += x[k + 2^(d) – 1]; +``` + +However, in a massively parallel CUDA kernel, there is no such central scheduler. Instead, thousands of threads awaken simultaneously, each with its own dense, unique ID `k`. The core of the implementation is to translate the high-level plan into an independent action for each thread. This is achieved with a filter condition. Once a thread passes a check like `if ((k + 1) % fullStride == 0)`, it has successfully identified itself as one of the active workers from the scheduler's plan. At that moment, the thread's own dense ID `k` becomes the actual memory address for the write operation, as shown in the kernel: +```cpp +__global__ void kernWorkEfficientUpSweep(int paddedN, int n, int d, int* idata) { + int k = blockIdx.x * blockDim.x + threadIdx.x; + + int halfStride = 1 << (d); + int fullStride = halfStride * 2; + + if (k >= n || ((k + 1) % fullStride != 0)) { + return; + } + + // up-sweep + idata[k] += idata[k - halfStride]; +} +``` + +Although further optimizations involve launching a compacted grid where each thread must explicitly calculate its target address, I still find this fundamental transition from a collective plan to individual, self-aware threads to be the most impressive aspect of parallel programming. This is the optimized version: +```cpp +__global__ void kernUpSweep(int n, int d, int* data) { + // k is the ID of the k-th active thread (dense) + int k = blockIdx.x * blockDim.x + threadIdx.x; + + int fullStride = 1 << (d + 1); + int halfStride = 1 << d; + + int global_idx = (k + 1) * fullStride - 1; + + if (global_idx >= n) { + return; + } + + data[global_idx] += data[global_idx - halfStride]; +} +``` + +# Performance Analysis + +![plot](./img/plot.png) + +The x-axis is the input size(N, where size = 2^N) + +The y-axis is the execution time (ms), the less the better + +## Explanation + +**The Crossover Point:** For small input sizes (roughly N < 20), the **CPU is significantly faster**. After this point, the GPU implementations become equal and faster than the CPU. The low performance of my work-efficient GPU implementation was due to excessive IO between the host and device. To reuse the `scan` function, intermediate results were copied from the device to the host, and the output of the scan was then copied back to the device. This data "round trip" is a significant performance bottleneck. To avoid this, the code was refactored to create a device side helper that operates exclusively on device pointers, without these time-consuming data transfers. + +# Extra Credit + +I implemented the Radix Sort and add the tests for it. In`main.cpp` it is called by + +```cpp +int *a = new int[SIZE]; +int *c = new int[SIZE]; + +//a is the input data, c is the output data +StreamCompaction::Radix::sort(SIZE, c, a); +``` + +```cpp +printf("\n"); +printf("*****************************\n"); +printf("** RADIX TESTS **\n"); +printf("*****************************\n"); + +genArray(SIZE, a, 1000); // Generate a new array with a larger range of numbers +printArray(SIZE, a, true); + +memcpy(b, a, SIZE * sizeof(int)); // Copy a into b +printDesc("cpu sort (std::sort), power-of-two"); +auto start_time = std::chrono::high_resolution_clock::now(); +std::sort(b, b + SIZE); // Sort b to create the correct reference answer +auto end_time = std::chrono::high_resolution_clock::now(); +std::chrono::duration elapsed = end_time - start_time; +printf(" elapsed time: %.4fms (std::chrono Measured)\n", elapsed.count()); +printArray(SIZE, b, true); + +zeroArray(SIZE, c); +printDesc("radix sort, power-of-two"); +StreamCompaction::Radix::sort(SIZE, c, a); +printElapsedTime(StreamCompaction::Radix::timer().getGpuElapsedTimeForPreviousOperation(), "(CUDA Measured)"); +printCmpResult(SIZE, b, c); + +memcpy(b, a, NPOT * sizeof(int)); +std::sort(b, b + NPOT); + + +zeroArray(SIZE, c); +printDesc("radix sort, non-power-of-two"); +StreamCompaction::Radix::sort(NPOT, c, a); +printElapsedTime(StreamCompaction::Radix::timer().getGpuElapsedTimeForPreviousOperation(), "(CUDA Measured)"); +printCmpResult(NPOT, b, c); +``` + + + + + +# Test Output + +`#define BLOCK_SIZE 256`,`const int SIZE = 1 << 10;` + + +```cpp +**************** +** SCAN TESTS ** +**************** + [ 45 35 35 0 7 15 46 20 46 47 42 7 18 ... 22 0 ] +==== cpu scan, power-of-two ==== + elapsed time: 0.0007ms (std::chrono Measured) + [ 0 45 80 115 115 122 137 183 203 249 296 338 345 ... 24413 24435 ] +==== cpu scan, non-power-of-two ==== + elapsed time: 0.0006ms (std::chrono Measured) + [ 0 45 80 115 115 122 137 183 203 249 296 338 345 ... 24328 24355 ] + passed +==== naive scan, power-of-two ==== + elapsed time: 0.238592ms (CUDA Measured) + passed +==== naive scan, non-power-of-two ==== + elapsed time: 0.055296ms (CUDA Measured) + passed +==== work-efficient scan, power-of-two ==== + elapsed time: 0.250784ms (CUDA Measured) + passed +==== work-efficient scan, non-power-of-two ==== + elapsed time: 0.11776ms (CUDA Measured) + passed +==== thrust scan, power-of-two ==== + elapsed time: 0.08192ms (CUDA Measured) + passed +==== thrust scan, non-power-of-two ==== + elapsed time: 0.03072ms (CUDA Measured) + passed + +***************************** +** STREAM COMPACTION TESTS ** +***************************** + [ 3 3 3 0 1 1 0 0 0 3 2 3 2 ... 2 0 ] +==== cpu compact without scan, power-of-two ==== + elapsed time: 0.0022ms (std::chrono Measured) + [ 3 3 3 1 1 3 2 3 2 2 3 3 2 ... 2 2 ] + passed +==== cpu compact without scan, non-power-of-two ==== + elapsed time: 0.0011ms (std::chrono Measured) + [ 3 3 3 1 1 3 2 3 2 2 3 3 2 ... 1 2 ] + passed +==== cpu compact with scan ==== + elapsed time: 0.0055ms (std::chrono Measured) + [ 3 3 3 1 1 3 2 3 2 2 3 3 2 ... 2 2 ] + passed +==== work-efficient compact, power-of-two ==== + elapsed time: 0.284672ms (CUDA Measured) + passed +==== work-efficient compact, non-power-of-two ==== + elapsed time: 0.1536ms (CUDA Measured) + passed + +***************************** +** RADIX TESTS ** +***************************** + [ 795 735 835 0 57 565 396 420 196 647 42 7 118 ... 122 648 ] +==== cpu sort (std::sort), power-of-two ==== + elapsed time: 0.0299ms (std::chrono Measured) + [ 0 2 2 2 3 5 5 6 6 6 7 8 8 ... 993 994 ] +==== radix sort, power-of-two ==== + elapsed time: 5.88186ms (CUDA Measured) + passed +==== radix sort, non-power-of-two ==== + elapsed time: 5.72314ms (CUDA Measured) + passed +Press any key to continue . . . +``` diff --git a/img/plot.png b/img/plot.png new file mode 100644 index 0000000000000000000000000000000000000000..00a40feede65a649c41993526f9a1eb9aef1e7b0 GIT binary patch literal 106419 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�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 W0-f=TkT+Q1pMtE)&3tJi-~R!w^z9%3 literal 0 HcmV?d00001 diff --git a/img/test.png b/img/test.png new file mode 100644 index 0000000000000000000000000000000000000000..a7b38e9f1241041db378cec0a4e5ab0e56e364a7 GIT binary patch literal 25719 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 #include "testing_helpers.hpp" -const int SIZE = 1 << 8; // feel free to change the size of array +const int SIZE = 1 << 18; // 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 18f7ceba..1cbb05cf 100644 --- a/stream_compaction/efficient.cu +++ b/stream_compaction/efficient.cu @@ -3,7 +3,7 @@ #include "common.h" #include "efficient.h" -#define BLOCK_SIZE 128 +#define BLOCK_SIZE 256 namespace StreamCompaction { namespace Efficient { @@ -154,9 +154,53 @@ namespace StreamCompaction { * Performs prefix-sum (aka scan) on idata, storing the result into odata. */ void scan(int n, int *odata, const int *idata) { + int paddedN = 1 << ilog2ceil(n); + dim3 fullBlocksPerGrid((paddedN + BLOCK_SIZE - 1) / BLOCK_SIZE); + + int* dev_temp; // Use a temporary buffer for the in-place algorithm + cudaMalloc((void**)&dev_temp, paddedN * sizeof(int)); + cudaMemset(dev_temp, 0, paddedN * sizeof(int)); + cudaMemcpy(dev_temp, idata, n * sizeof(int), cudaMemcpyHostToDevice); + timer().startGpuTimer(); - scanWithoutTimer(n, odata, idata); + //// Up-Sweep + //for (int d = 0; d < ilog2ceil(n); d++) { + // kernWorkEfficientUpSweep << > > (paddedN, n, d, dev_temp); + //} + + //setLastElementToZero << <1, 1 >> > (paddedN, dev_temp); + + //// Down-Sweep + //for (int d = ilog2ceil(n) - 1; d >= 0; d--) { + // kernWorkEfficientDownSweep << > > (paddedN, n, d, dev_temp); + //} + + for (int d = 0; d < ilog2ceil(n); d++) { + // Calculate how many threads are actually needed for this pass + int numActiveThreads = paddedN / (1 << (d + 1)); + dim3 gridDim((numActiveThreads + BLOCK_SIZE - 1) / BLOCK_SIZE); + dim3 blockDim(BLOCK_SIZE); + + kernUpSweep << > > (paddedN, d, dev_temp); + //cudaDeviceSynchronize(); + } + + setLastElementToZero << <1, 1 >> > (paddedN, dev_temp); + + for (int d = ilog2ceil(n) - 1; d >= 0; d--) { + int numActiveThreads = paddedN / (1 << (d + 1)); + dim3 gridDim((numActiveThreads + BLOCK_SIZE - 1) / BLOCK_SIZE); + dim3 blockDim(BLOCK_SIZE); + + kernDownSweep << > > (paddedN, d, dev_temp); + //cudaDeviceSynchronize(); + } + timer().endGpuTimer(); + + cudaMemcpy(odata, dev_temp, n * sizeof(int), cudaMemcpyDeviceToHost); + + cudaFree(dev_temp); } /** @@ -205,7 +249,7 @@ namespace StreamCompaction { cudaFree(dev_idata); cudaFree(dev_flags); - cudaFree(dev_idata); + cudaFree(dev_scanResult); cudaFree(dev_odata); return totalCount; From 3a006649a1417ef64249058012588416d8fb6b76 Mon Sep 17 00:00:00 2001 From: Xiaonan Date: Thu, 18 Sep 2025 07:14:05 -0400 Subject: [PATCH 14/15] further optimization to the work-efficient approach --- src/main.cpp | 28 ++++-- stream_compaction/efficient.cu | 155 ++++++++++++++++++--------------- 2 files changed, 106 insertions(+), 77 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index fdd44f84..d0ec89b6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -14,7 +14,9 @@ #include #include "testing_helpers.hpp" -const int SIZE = 1 << 18; // feel free to change the size of array +//Xiaonan + +const int SIZE = 1 << 22; // 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]; @@ -23,18 +25,20 @@ int *c = new int[SIZE]; int main(int argc, char* argv[]) { // Scan tests - printf("\n"); - printf("****************\n"); - printf("** SCAN TESTS **\n"); - printf("****************\n"); + //printf("\n"); + //printf("****************\n"); + //printf("** SCAN TESTS **\n"); + //printf("****************\n"); genArray(SIZE - 1, a, 50); // Leave a 0 at the end to test that edge case a[SIZE - 1] = 0; printArray(SIZE, a, true); - // initialize b using StreamCompaction::CPU::scan you implement - // We use b for further comparison. Make sure your StreamCompaction::CPU::scan is correct. - // At first all cases passed because b && c are all zeroes. + //printDesc("The calcualtion is " + paddedN / (1 << (d + 1))); + + //// initialize b using StreamCompaction::CPU::scan you implement + //// We use b for further comparison. Make sure your StreamCompaction::CPU::scan is correct. + //// At first all cases passed because b && c are all zeroes. zeroArray(SIZE, b); printDesc("cpu scan, power-of-two"); StreamCompaction::CPU::scan(SIZE, b, a); @@ -66,6 +70,7 @@ int main(int argc, char* argv[]) { StreamCompaction::Naive::scan(NPOT, c, a); printElapsedTime(StreamCompaction::Naive::timer().getGpuElapsedTimeForPreviousOperation(), "(CUDA Measured)"); //printArray(SIZE, c, true); + printCmpResult(NPOT, b, c); zeroArray(SIZE, c); @@ -73,6 +78,9 @@ int main(int argc, char* argv[]) { StreamCompaction::Efficient::scan(SIZE, c, a); printElapsedTime(StreamCompaction::Efficient::timer().getGpuElapsedTimeForPreviousOperation(), "(CUDA Measured)"); //printArray(SIZE, c, true); + printArray(SIZE, b, true); + printArray(SIZE, c, true); + printCmpResult(SIZE, b, c); zeroArray(SIZE, c); @@ -80,6 +88,10 @@ int main(int argc, char* argv[]) { StreamCompaction::Efficient::scan(NPOT, c, a); printElapsedTime(StreamCompaction::Efficient::timer().getGpuElapsedTimeForPreviousOperation(), "(CUDA Measured)"); //printArray(NPOT, c, true); + + printArray(SIZE, b, true); + printArray(SIZE, c, true); + printCmpResult(NPOT, b, c); zeroArray(SIZE, c); diff --git a/stream_compaction/efficient.cu b/stream_compaction/efficient.cu index 1cbb05cf..82e4d701 100644 --- a/stream_compaction/efficient.cu +++ b/stream_compaction/efficient.cu @@ -28,6 +28,29 @@ namespace StreamCompaction { idata[k] += idata[k - halfStride]; } + __global__ void setLastElementToZero(int paddedN, int* idata) + { + idata[paddedN - 1] = 0; + } + + __global__ void kernWorkEfficientDownSweep(int paddedN, int n, int d, int* idata) { + int k = blockIdx.x * blockDim.x + threadIdx.x; + + int halfStride = 1 << (d); + int fullStride = halfStride * 2; + + if (k >= paddedN || ((k + 1) % fullStride != 0)) { + return; + } + + // down-sweep + int originalLeftChildValue = idata[k - halfStride]; + int parentValue = idata[k]; + + idata[k - halfStride] = parentValue; + idata[k] = parentValue + originalLeftChildValue; + } + __global__ void kernUpSweep(int n, int d, int* data) { // k is the ID of the k-th active thread (dense) int k = blockIdx.x * blockDim.x + threadIdx.x; @@ -44,27 +67,39 @@ namespace StreamCompaction { data[global_idx] += data[global_idx - halfStride]; } - __global__ void setLastElementToZero(int paddedN, int* idata) - { - idata[paddedN - 1] = 0; - } - - __global__ void kernWorkEfficientDownSweep(int paddedN, int n, int d, int* idata) { + __global__ void kernUpSweep_DEBUG(int n, int d, int* data, int numActiveThreads) { + // k is the ID of the k-th active thread (dense) int k = blockIdx.x * blockDim.x + threadIdx.x; - int halfStride = 1 << (d); - int fullStride = halfStride * 2; + // This check is important: only let threads that are part of the + // compact grid do the printing. + if (k >= numActiveThreads) { + return; + } - if (k >= paddedN || ((k + 1) % fullStride != 0)) { + int fullStride = 1 << (d + 1); + int halfStride = 1 << d; + + int global_idx = (k + 1) * fullStride - 1; + + // --- Let's print the state of the first and last active threads --- + if (d == 0 && (k == 0 || k == numActiveThreads - 1)) { + printf("d=%d, active_thread_k=%d, global_idx=%d, n=%d\n", + d, k, global_idx, n); + } + + if (global_idx >= n) { return; } - // down-sweep - int originalLeftChildValue = idata[k - halfStride]; - int parentValue = idata[k]; + // Let's also check the values we are about to add + if (d == 0 && (k == 0 || k == numActiveThreads - 1)) { + int read_addr = global_idx - halfStride; + printf(" -> Thread %d will add data[%d] (value=%d) to data[%d] (value=%d)\n", + k, read_addr, data[read_addr], global_idx, data[global_idx]); + } - idata[k - halfStride] = parentValue ; - idata[k] = parentValue + originalLeftChildValue; + data[global_idx] += data[global_idx - halfStride]; } __global__ void kernDownSweep(int n, int d, int* data) { @@ -83,53 +118,57 @@ namespace StreamCompaction { int originalLeftChildValue = data[global_idx - halfStride]; int parentValue = data[global_idx]; + data[global_idx - halfStride] = parentValue; data[global_idx] = parentValue + originalLeftChildValue; } - + + /// + /// return an excluisve scan + /// + /// is the actual number of elements + /// be sure that the length of this should be n + /// be sure that the length of this should be n void scan_device(int n, int* dev_odata, const int* dev_idata) { int paddedN = 1 << ilog2ceil(n); - dim3 fullBlocksPerGrid((paddedN + BLOCK_SIZE - 1) / BLOCK_SIZE); - int* dev_temp; // Use a temporary buffer for the in-place algorithm + int* dev_temp; + cudaMalloc((void**)&dev_temp, paddedN * sizeof(int)); cudaMemset(dev_temp, 0, paddedN * sizeof(int)); cudaMemcpy(dev_temp, dev_idata, n * sizeof(int), cudaMemcpyDeviceToDevice); - //// Up-Sweep - //for (int d = 0; d < ilog2ceil(n); d++) { - // kernWorkEfficientUpSweep << > > (paddedN, n, d, dev_temp); - //} - - //setLastElementToZero << <1, 1 >> > (paddedN, dev_temp); - - //// Down-Sweep - //for (int d = ilog2ceil(n) - 1; d >= 0; d--) { - // kernWorkEfficientDownSweep << > > (paddedN, n, d, dev_temp); - //} - - for (int d = 0; d < ilog2ceil(n); d++) { + for (int d = 0; d < ilog2ceil(paddedN); d++) { // Calculate how many threads are actually needed for this pass int numActiveThreads = paddedN / (1 << (d + 1)); - dim3 gridDim((numActiveThreads + BLOCK_SIZE - 1) / BLOCK_SIZE); - dim3 blockDim(BLOCK_SIZE); + int blockSize = std::min(BLOCK_SIZE, numActiveThreads); + + + dim3 gridDim((numActiveThreads + blockSize - 1) / blockSize); + dim3 blockDim(blockSize); kernUpSweep << > > (paddedN, d, dev_temp); + + cudaDeviceSynchronize(); } setLastElementToZero << <1, 1 >> > (paddedN, dev_temp); - for (int d = ilog2ceil(n) - 1; d >= 0; d--) { + for (int d = ilog2ceil(paddedN) - 1; d >= 0; d--) { int numActiveThreads = paddedN / (1 << (d + 1)); - dim3 gridDim((numActiveThreads + BLOCK_SIZE - 1) / BLOCK_SIZE); - dim3 blockDim(BLOCK_SIZE); + int blockSize = std::min(BLOCK_SIZE, numActiveThreads); + + + dim3 gridDim((numActiveThreads + blockSize - 1) / blockSize); + dim3 blockDim(blockSize); kernDownSweep << > > (paddedN, d, dev_temp); + + cudaDeviceSynchronize(); } cudaMemcpy(dev_odata, dev_temp, n * sizeof(int), cudaMemcpyDeviceToDevice); - cudaFree(dev_temp); } @@ -155,52 +194,29 @@ namespace StreamCompaction { */ void scan(int n, int *odata, const int *idata) { int paddedN = 1 << ilog2ceil(n); + + // + int t = ilog2ceil(9); + dim3 fullBlocksPerGrid((paddedN + BLOCK_SIZE - 1) / BLOCK_SIZE); int* dev_temp; // Use a temporary buffer for the in-place algorithm + int* dev_odata; cudaMalloc((void**)&dev_temp, paddedN * sizeof(int)); + cudaMalloc((void**)&dev_odata, paddedN * sizeof(int)); cudaMemset(dev_temp, 0, paddedN * sizeof(int)); cudaMemcpy(dev_temp, idata, n * sizeof(int), cudaMemcpyHostToDevice); timer().startGpuTimer(); - //// Up-Sweep - //for (int d = 0; d < ilog2ceil(n); d++) { - // kernWorkEfficientUpSweep << > > (paddedN, n, d, dev_temp); - //} - - //setLastElementToZero << <1, 1 >> > (paddedN, dev_temp); - - //// Down-Sweep - //for (int d = ilog2ceil(n) - 1; d >= 0; d--) { - // kernWorkEfficientDownSweep << > > (paddedN, n, d, dev_temp); - //} - - for (int d = 0; d < ilog2ceil(n); d++) { - // Calculate how many threads are actually needed for this pass - int numActiveThreads = paddedN / (1 << (d + 1)); - dim3 gridDim((numActiveThreads + BLOCK_SIZE - 1) / BLOCK_SIZE); - dim3 blockDim(BLOCK_SIZE); - - kernUpSweep << > > (paddedN, d, dev_temp); - //cudaDeviceSynchronize(); - } - - setLastElementToZero << <1, 1 >> > (paddedN, dev_temp); - - for (int d = ilog2ceil(n) - 1; d >= 0; d--) { - int numActiveThreads = paddedN / (1 << (d + 1)); - dim3 gridDim((numActiveThreads + BLOCK_SIZE - 1) / BLOCK_SIZE); - dim3 blockDim(BLOCK_SIZE); - - kernDownSweep << > > (paddedN, d, dev_temp); - //cudaDeviceSynchronize(); - } + + scan_device(paddedN, dev_odata, dev_temp); timer().endGpuTimer(); - cudaMemcpy(odata, dev_temp, n * sizeof(int), cudaMemcpyDeviceToHost); + cudaMemcpy(odata, dev_odata, n * sizeof(int), cudaMemcpyDeviceToHost); cudaFree(dev_temp); + cudaFree(dev_odata); } /** @@ -232,6 +248,7 @@ namespace StreamCompaction { StreamCompaction::Common::kernMapToBoolean << > > (n, dev_flags, dev_idata); //compute the scan of the temporary array + int paddedN = 1 << ilog2ceil(n); scan_device(n, dev_scanResult, dev_flags); //compute the scatter From 5237a7f717458508c95f241a887dac83d0311d81 Mon Sep 17 00:00:00 2001 From: Xiaonan Date: Thu, 18 Sep 2025 08:04:50 -0400 Subject: [PATCH 15/15] remove temp arr in scan_device --- stream_compaction/efficient.cu | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/stream_compaction/efficient.cu b/stream_compaction/efficient.cu index 82e4d701..9f7a449a 100644 --- a/stream_compaction/efficient.cu +++ b/stream_compaction/efficient.cu @@ -130,46 +130,49 @@ namespace StreamCompaction { /// be sure that the length of this should be n /// be sure that the length of this should be n void scan_device(int n, int* dev_odata, const int* dev_idata) { + // Calculate the padded size, which is the next power of 2. int paddedN = 1 << ilog2ceil(n); - int* dev_temp; - - cudaMalloc((void**)&dev_temp, paddedN * sizeof(int)); - cudaMemset(dev_temp, 0, paddedN * sizeof(int)); - cudaMemcpy(dev_temp, dev_idata, n * sizeof(int), cudaMemcpyDeviceToDevice); + // STEP 1: No more cudaMalloc or dev_temp! + // STEP 2: Copy input directly to the output buffer. + // Then, pad the rest of the output buffer with zeros. + cudaMemcpy(dev_odata, dev_idata, n * sizeof(int), cudaMemcpyDeviceToDevice); + if (paddedN > n) { + cudaMemset(dev_odata + n, 0, (paddedN - n) * sizeof(int)); + } + // --- Up-Sweep Phase --- for (int d = 0; d < ilog2ceil(paddedN); d++) { - // Calculate how many threads are actually needed for this pass int numActiveThreads = paddedN / (1 << (d + 1)); int blockSize = std::min(BLOCK_SIZE, numActiveThreads); - dim3 gridDim((numActiveThreads + blockSize - 1) / blockSize); dim3 blockDim(blockSize); - kernUpSweep << > > (paddedN, d, dev_temp); - + // STEP 3: Kernels now operate directly on dev_odata. + kernUpSweep << > > (paddedN, d, dev_odata); cudaDeviceSynchronize(); } - setLastElementToZero << <1, 1 >> > (paddedN, dev_temp); + // Set the last element to zero for an exclusive scan. + setLastElementToZero << <1, 1 >> > (paddedN, dev_odata); + // --- Down-Sweep Phase --- for (int d = ilog2ceil(paddedN) - 1; d >= 0; d--) { int numActiveThreads = paddedN / (1 << (d + 1)); int blockSize = std::min(BLOCK_SIZE, numActiveThreads); - dim3 gridDim((numActiveThreads + blockSize - 1) / blockSize); dim3 blockDim(blockSize); - kernDownSweep << > > (paddedN, d, dev_temp); - + // STEP 3 (cont.): Kernels now operate directly on dev_odata. + kernDownSweep << > > (paddedN, d, dev_odata); cudaDeviceSynchronize(); } - cudaMemcpy(dev_odata, dev_temp, n * sizeof(int), cudaMemcpyDeviceToDevice); - cudaFree(dev_temp); + // STEP 4: The final cudaMemcpy and cudaFree are no longer needed. + // The result is already in dev_odata. } void scanWithoutTimer(int n, int* odata, const int* idata) {