From 482645b5931d6699c520a85a3182ecc244e485d2 Mon Sep 17 00:00:00 2001 From: Jiyu Huang <47465661+JiyuHuang@users.noreply.github.com> Date: Sun, 19 Sep 2021 15:05:01 -0400 Subject: [PATCH 01/13] todo: test --- stream_compaction/common.cu | 14 +++++- stream_compaction/common.h | 2 + stream_compaction/cpu.cu | 30 ++++++++++-- stream_compaction/efficient.cu | 89 ++++++++++++++++++++++++++++++++-- stream_compaction/naive.cu | 36 +++++++++++++- stream_compaction/thrust.cu | 10 ++-- 6 files changed, 166 insertions(+), 15 deletions(-) diff --git a/stream_compaction/common.cu b/stream_compaction/common.cu index 2ed6d63..e7563d0 100644 --- a/stream_compaction/common.cu +++ b/stream_compaction/common.cu @@ -23,7 +23,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 index = blockIdx.x * blockDim.x + threadIdx.x; + if (index >= n) { + return; + } + bools[index] = idata[index] == 0 ? 0 : 1; } /** @@ -32,7 +36,13 @@ namespace StreamCompaction { */ __global__ void kernScatter(int n, int *odata, const int *idata, const int *bools, const int *indices) { - // TODO + int index = blockIdx.x * blockDim.x + threadIdx.x; + if (index >= n) { + return; + } + if (bools[index] == 1) { + odata[[indices[index]]] = idata[index]; + } } } diff --git a/stream_compaction/common.h b/stream_compaction/common.h index d2c1fed..3d6f518 100644 --- a/stream_compaction/common.h +++ b/stream_compaction/common.h @@ -13,6 +13,8 @@ #define FILENAME (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) #define checkCUDAError(msg) checkCUDAErrorFn(msg, FILENAME, __LINE__) +const int blockSize = 128; + /** * Check for CUDA errors; print and exit if there was a problem. */ diff --git a/stream_compaction/cpu.cu b/stream_compaction/cpu.cu index 719fa11..3e54416 100644 --- a/stream_compaction/cpu.cu +++ b/stream_compaction/cpu.cu @@ -19,7 +19,10 @@ namespace StreamCompaction { */ void scan(int n, int *odata, const int *idata) { timer().startCpuTimer(); - // TODO + odata[0] = 0; + for (int i = 1; i < n; ++i) { + odata[i] = idata[i - 1] + odata[i - 1]; + } timer().endCpuTimer(); } @@ -30,9 +33,14 @@ namespace StreamCompaction { */ int compactWithoutScan(int n, int *odata, const int *idata) { timer().startCpuTimer(); - // TODO + int j = 0; + for (int i = 0; i < n; ++i) { + if (idata[i] != 0) { + odata[j++] = idata[i]; + } + } timer().endCpuTimer(); - return -1; + return j; } /** @@ -42,9 +50,21 @@ namespace StreamCompaction { */ int compactWithScan(int n, int *odata, const int *idata) { timer().startCpuTimer(); - // TODO + int *temp = new int[n]; + int *scan = new int[n]; + for (int i = 0; i < n; ++i) { + temp[i] = idata[i] == 0 ? 0 : 1; + } + scan(n, scan, temp); + int num = 0; + for (int i = 0; i < n; ++i) { + if (temp[i] != 0) { + odata[scan[i]] = idata[i]; + ++num; + } + } timer().endCpuTimer(); - return -1; + return num; } } } diff --git a/stream_compaction/efficient.cu b/stream_compaction/efficient.cu index 2db346e..3871c92 100644 --- a/stream_compaction/efficient.cu +++ b/stream_compaction/efficient.cu @@ -12,13 +12,68 @@ namespace StreamCompaction { return timer; } + __global__ void kernScanUpSweepPhase(int threads, int *dev_temp, int offset) { + int index = blockIdx.x * blockDim.x + threadIdx.x; + if (index >= threads) { + return; + } + index = (index + 1) * offset * 2 - 1; + dev_temp[index] += dev_temp[index - offset]; + } + + __global__ void kernScanDownSweepPhase(int threads, int *dev_temp, int offset) { + int index = blockIdx.x * blockDim.x + threadIdx.x; + if (index >= threads) { + return; + } + index = (index + 1) * offset * 2 - 1; + int t = dev_temp[index - offset]; + dev_temp[index - offset] = dev_temp[index]; + dev_temp[index] += t; + } + + void scanHelper(int steps, int size, int *dev_temp) { + + int threads = size / 2; + int offset = 1; + for (int i = 0; i < steps; ++i) { + dim3 blocks((threads + blockSize - 1) / blockSize); + kernScanUpSweepPhase<<>>(threads, dev_temp, offset); + threads /= 2; + offset *= 2; + } + + cudaMemset(dev_temp + size - 1, 0, sizeof(int)); + threads = 1; + offset = size / 2; + for (int i = 0; i < steps; ++i) { + dim3 blocks((threads + blockSize - 1) / blockSize); + kernScanDownSweepPhase<<>>(threads, dev_temp, offset); + threads *= 2; + offset /= 2; + } + } + /** * Performs prefix-sum (aka scan) on idata, storing the result into odata. */ void scan(int n, int *odata, const int *idata) { + + int steps = ilog2ceil(n); + int size = 1 << steps; + + int *dev_temp; + cudaMalloc((void**) &dev_temp, size * sizeof(int)); + cudaMemcpy(dev_temp, idata, n * sizeof(int), cudaMemcpyHostToDevice); + cudaMemset(dev_temp + n, 0, (size - n) * sizeof(int)); + timer().startGpuTimer(); - // TODO + scanHelper(steps, size, dev_temp); timer().endGpuTimer(); + + cudaMemcpy(odata, dev_temp, n * sizeof(int), cudaMemcpyDeviceToHost); + + cudaFree(dev_temp); } /** @@ -31,10 +86,38 @@ namespace StreamCompaction { * @returns The number of elements remaining after compaction. */ int compact(int n, int *odata, const int *idata) { + + dim3 blocks((n + blockSize - 1) / blockSize); + int steps = ilog2ceil(n); + int size = 1 << steps; + + int *dev_idata, *dev_odata, *dev_bools, *dev_indices; + cudaMalloc((void**) &dev_idata, n * sizeof(int)); + cudaMalloc((void**) &dev_odata, n * sizeof(int)); + cudaMalloc((void**) &dev_bools, n * sizeof(int)); + cudaMalloc((void**) &dev_indices, size * sizeof(int)); + + cudaMemcpy(dev_idata, idata, n * sizeof(int), cudaMemcpyHostToDevice); + cudaMemset(dev_indices + n, 0, (size - n) * sizeof(int)); + timer().startGpuTimer(); - // TODO + StreamCompaction::Common::kernMapToBoolean<<>>(n, dev_bools, dev_idata); + cudaMemcpy(dev_indices, dev_bools, n * sizeof(int), cudaMemcpyDeviceToDevice); + scanHelper(steps, size, dev_indices); + StreamCompaction::Common::kernScatter<<>>(n, dev_odata, dev_idata, dev_bools, dev_indices); timer().endGpuTimer(); - return -1; + + int lastBool, lastIdx; + cudaMemcpy(odata, dev_odata, n * sizeof(int), cudaMemcpyDeviceToHost); + cudaMemcpy(&lastBool, dev_bools + n - 1, sizeof(int), cudaMemcpyDeviceToHost); + cudaMemcpy(&lastIdx, dev_indices + n - 1, sizeof(int), cudaMemcpyDeviceToHost); + + cudaFree(dev_idata); + cudaFree(dev_odata); + cudaFree(dev_bools); + cudaFree(dev_indices); + + return lastBool + lastIdx; } } } diff --git a/stream_compaction/naive.cu b/stream_compaction/naive.cu index 4308876..f4a201b 100644 --- a/stream_compaction/naive.cu +++ b/stream_compaction/naive.cu @@ -11,15 +11,47 @@ namespace StreamCompaction { static PerformanceTimer timer; return timer; } - // TODO: __global__ + + __global__ void kernScanStep(int n, const int *dev_in, int *dev_out, int offset) { + int index = blockIdx.x * blockDim.x + threadIdx.x; + if (index >= n) { + return; + } + if (index >= offset) { + dev_out[index] = dev_in[index] + dev_in[index - offset]; + } else { + dev_out[index] = dev_in[index]; + } + } /** * Performs prefix-sum (aka scan) on idata, storing the result into odata. */ void scan(int n, int *odata, const int *idata) { + + dim3 blocks((n + blockSize - 1) / blockSize); + + int *dev_temp1, *dev_temp2; + cudaMalloc((void**) &dev_temp1, n * sizeof(int)); + cudaMalloc((void**) &dev_temp2, n * sizeof(int)); + + cudaMemcpy(dev_temp1, idata, n * sizeof(int), cudaMemcpyHostToDevice); + timer().startGpuTimer(); - // TODO + int steps = ilog2ceil(n); + int offset = 1; + for (int i = 0; i < steps; ++i) { + kernScanStep<<>>(n, dev_temp1, dev_temp2, offset); + std::swap(dev_temp1, dev_temp2); + offset *= 2; + } timer().endGpuTimer(); + + odata[0] = 0; + cudaMemcpy(odata + 1, dev_temp2, (n - 1) * sizeof(int), cudaMemcpyDeviceToHost); + + cudaFree(dev_temp1); + cudaFree(dev_temp2); } } } diff --git a/stream_compaction/thrust.cu b/stream_compaction/thrust.cu index 1def45e..1b2e22d 100644 --- a/stream_compaction/thrust.cu +++ b/stream_compaction/thrust.cu @@ -18,11 +18,15 @@ namespace StreamCompaction { * Performs prefix-sum (aka scan) on idata, storing the result into odata. */ void scan(int n, int *odata, const int *idata) { + + thrust::device_vector dev_idata(dev_idata, dev_idata + n); + 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 24a966483b0fecd71669aadd96065316234dc08b Mon Sep 17 00:00:00 2001 From: Jiyu Huang <47465661+JiyuHuang@users.noreply.github.com> Date: Sun, 19 Sep 2021 15:27:03 -0400 Subject: [PATCH 02/13] bug fixes --- stream_compaction/common.cu | 2 +- stream_compaction/cpu.cu | 24 ++++++++++++++---------- stream_compaction/thrust.cu | 2 +- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/stream_compaction/common.cu b/stream_compaction/common.cu index e7563d0..bad955b 100644 --- a/stream_compaction/common.cu +++ b/stream_compaction/common.cu @@ -41,7 +41,7 @@ namespace StreamCompaction { return; } if (bools[index] == 1) { - odata[[indices[index]]] = idata[index]; + odata[indices[index]] = idata[index]; } } diff --git a/stream_compaction/cpu.cu b/stream_compaction/cpu.cu index 3e54416..cb95a42 100644 --- a/stream_compaction/cpu.cu +++ b/stream_compaction/cpu.cu @@ -12,6 +12,13 @@ namespace StreamCompaction { return timer; } + void scanHelper(int n, int* odata, const int* idata) { + odata[0] = 0; + for (int i = 1; i < n; ++i) { + odata[i] = idata[i - 1] + odata[i - 1]; + } + } + /** * CPU scan (prefix sum). * For performance analysis, this is supposed to be a simple for loop. @@ -19,10 +26,7 @@ namespace StreamCompaction { */ void scan(int n, int *odata, const int *idata) { timer().startCpuTimer(); - odata[0] = 0; - for (int i = 1; i < n; ++i) { - odata[i] = idata[i - 1] + odata[i - 1]; - } + scanHelper(n, odata, idata); timer().endCpuTimer(); } @@ -50,16 +54,16 @@ namespace StreamCompaction { */ int compactWithScan(int n, int *odata, const int *idata) { timer().startCpuTimer(); - int *temp = new int[n]; - int *scan = new int[n]; + int *boolArray = new int[n]; + int *scanArray = new int[n]; for (int i = 0; i < n; ++i) { - temp[i] = idata[i] == 0 ? 0 : 1; + boolArray[i] = idata[i] == 0 ? 0 : 1; } - scan(n, scan, temp); + scanHelper(n, scanArray, boolArray); int num = 0; for (int i = 0; i < n; ++i) { - if (temp[i] != 0) { - odata[scan[i]] = idata[i]; + if (boolArray[i] != 0) { + odata[scanArray[i]] = idata[i]; ++num; } } diff --git a/stream_compaction/thrust.cu b/stream_compaction/thrust.cu index 1b2e22d..172b221 100644 --- a/stream_compaction/thrust.cu +++ b/stream_compaction/thrust.cu @@ -19,7 +19,7 @@ namespace StreamCompaction { */ void scan(int n, int *odata, const int *idata) { - thrust::device_vector dev_idata(dev_idata, dev_idata + n); + thrust::device_vector dev_idata(idata, idata + n); thrust::device_vector dev_odata(n); timer().startGpuTimer(); From 094222a97558040330b4c354b71a2b5df197161b Mon Sep 17 00:00:00 2001 From: Jiyu Huang <47465661+JiyuHuang@users.noreply.github.com> Date: Mon, 20 Sep 2021 13:59:43 -0400 Subject: [PATCH 03/13] todo: radix sort --- stream_compaction/efficient.cu | 23 +++++++++++++++++++++++ stream_compaction/efficient.h | 2 ++ stream_compaction/naive.cu | 4 ++-- 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/stream_compaction/efficient.cu b/stream_compaction/efficient.cu index 3871c92..8bd7748 100644 --- a/stream_compaction/efficient.cu +++ b/stream_compaction/efficient.cu @@ -119,5 +119,28 @@ namespace StreamCompaction { return lastBool + lastIdx; } + + void radixSort(int n, int *odata, int *idata) { + + int *dev_idata, *dev_odata, *dev_bools, *dev_scan; + cudaMalloc((void**) &dev_idata, n * sizeof(int)); + cudaMalloc((void**) &dev_odata, n * sizeof(int)); + cudaMalloc((void**) &dev_bools, n * sizeof(int)); + cudaMalloc((void**) &dev_scan, n * sizeof(int)); + + cudaMemcpy(dev_idata, idata, n * sizeof(int), cudaMemcpyHostToDevice); + + int maxNum = 0; + for (int i = 0; i < n; ++i) { + maxNum = std::max(maxNum, idata[n]); + } + + cudaMemcpy(odata, dev_odata, n * sizeof(int), cudaMemcpyDeviceToHost); + + cudaFree(dev_idata); + cudaFree(dev_odata); + cudaFree(dev_bools); + cudaFree(dev_scan); + } } } diff --git a/stream_compaction/efficient.h b/stream_compaction/efficient.h index 803cb4f..a2b764f 100644 --- a/stream_compaction/efficient.h +++ b/stream_compaction/efficient.h @@ -9,5 +9,7 @@ namespace StreamCompaction { void scan(int n, int *odata, const int *idata); int compact(int n, int *odata, const int *idata); + + void radixSort(int n, int *odata, const int *idata); } } diff --git a/stream_compaction/naive.cu b/stream_compaction/naive.cu index f4a201b..a39f68c 100644 --- a/stream_compaction/naive.cu +++ b/stream_compaction/naive.cu @@ -30,6 +30,8 @@ namespace StreamCompaction { void scan(int n, int *odata, const int *idata) { dim3 blocks((n + blockSize - 1) / blockSize); + int steps = ilog2ceil(n); + int offset = 1; int *dev_temp1, *dev_temp2; cudaMalloc((void**) &dev_temp1, n * sizeof(int)); @@ -38,8 +40,6 @@ namespace StreamCompaction { cudaMemcpy(dev_temp1, idata, n * sizeof(int), cudaMemcpyHostToDevice); timer().startGpuTimer(); - int steps = ilog2ceil(n); - int offset = 1; for (int i = 0; i < steps; ++i) { kernScanStep<<>>(n, dev_temp1, dev_temp2, offset); std::swap(dev_temp1, dev_temp2); From 0125786fd5a34eacba9dfc9efae145792ab4a4b0 Mon Sep 17 00:00:00 2001 From: Jiyu Huang <47465661+JiyuHuang@users.noreply.github.com> Date: Mon, 20 Sep 2021 15:01:43 -0400 Subject: [PATCH 04/13] bug fix --- src/main.cpp | 2 +- stream_compaction/naive.cu | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 896ac2b..600c29f 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 << 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/naive.cu b/stream_compaction/naive.cu index a39f68c..dce771b 100644 --- a/stream_compaction/naive.cu +++ b/stream_compaction/naive.cu @@ -48,7 +48,7 @@ namespace StreamCompaction { timer().endGpuTimer(); odata[0] = 0; - cudaMemcpy(odata + 1, dev_temp2, (n - 1) * sizeof(int), cudaMemcpyDeviceToHost); + cudaMemcpy(odata + 1, dev_temp1, (n - 1) * sizeof(int), cudaMemcpyDeviceToHost); cudaFree(dev_temp1); cudaFree(dev_temp2); From 8aa4aedcd49fd463dadd83cfb98d28b811745a3d Mon Sep 17 00:00:00 2001 From: Jiyu Huang <47465661+JiyuHuang@users.noreply.github.com> Date: Tue, 21 Sep 2021 17:34:53 -0400 Subject: [PATCH 05/13] todo: test shared memory and radix sort --- src/main.cpp | 35 ++++++- stream_compaction/efficient.cu | 174 ++++++++++++++++++++++++++++----- stream_compaction/naive.cu | 5 +- 3 files changed, 187 insertions(+), 27 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 600c29f..eb91603 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -44,7 +44,7 @@ int main(int argc, char* argv[]) { printDesc("cpu scan, non-power-of-two"); StreamCompaction::CPU::scan(NPOT, c, a); printElapsedTime(StreamCompaction::CPU::timer().getCpuElapsedTimeForPreviousOperation(), "(std::chrono Measured)"); - printArray(NPOT, b, true); + //printArray(NPOT, b, true); printCmpResult(NPOT, b, c); zeroArray(SIZE, c); @@ -147,6 +147,39 @@ int main(int argc, char* argv[]) { //printArray(count, c, true); printCmpLenResult(count, expectedNPOT, b, c); + printf("\n"); + printf("*****************************\n"); + printf("** RADIX SORT TESTS **\n"); + printf("*****************************\n"); + + genArray(SIZE - 1, a, 100); // Leave a 0 at the end to test that edge case + a[SIZE - 1] = 0; + printArray(SIZE, a, true); + + zeroArray(SIZE, b); + printDesc("radix sort, power-of-two"); + StreamCompaction::Efficient::radixSort(SIZE, b, a); + for (int i = 0; i < SIZE - 1; ++i) { + if (b[i] > b[i + 1]) { + printf("FAILED\n"); + break; + } else if (i == SIZE - 2) { + printf("passed\n") + } + } + + zeroArray(SIZE, b); + printDesc("radix sort, non-power-of-two"); + StreamCompaction::Efficient::radixSort(NPOT, b, a); + for (int i = 0; i < NPOT - 1; ++i) { + if (b[i] > b[i + 1]) { + printf("FAILED\n"); + break; + } else if (i == NPOT - 2) { + printf("passed\n") + } + } + system("pause"); // stop Win32 console from closing on exit delete[] a; delete[] b; diff --git a/stream_compaction/efficient.cu b/stream_compaction/efficient.cu index 8bd7748..1627540 100644 --- a/stream_compaction/efficient.cu +++ b/stream_compaction/efficient.cu @@ -12,6 +12,102 @@ namespace StreamCompaction { return timer; } +#if USING_SHARED_MEMORY + + const int blockSize_sharedMemory = 128; + + constexpr int logNumBanks = 5; + + #define CONFLICT_FREE_OFFSET(n) ((n) >> logNumBanks); + + __global__ void kernScanPerBlock(int n, int *dev_data, int *dev_blockSum) { + + extern __shared__ int temp[]; + + int index = threadIdx.x; + dev_data += blockDim.x * blockIdx.x * 2; + n = n > blockDim.x * 2 ? blockDim.x * 2; + + int i = index; + int j = index + n / 2; + int ti = i + CONFLICT_FREE_OFFSET(i); + int tj = j + CONFLICT_FREE_OFFSET(j); + temp[ti] = dev_data[i]; + temp[tj] = dev_data[j]; + + int lastElement = 0; + if (dev_blockSum && index == blockDim.x - 1) { + lastElement = temp[tj]; + } + + int offset = 1; + for (int d = n >> 1; d > 0; d >>= 1) { + _syncthreads(); + if (index < d) { + int i = offset * (2 * index + 1) - 1; + int j = offset * (2 * index + 2) - 1; + i += CONFLICT_FREE_OFFSET(i); + j += CONFLICT_FREE_OFFSET(j); + temp[j] += temp[i]; + } + offset *= 2; + } + + if (index == n / 2 - 1) { + temp[tj] = 0; + } + + for (int d = 1; d < n; d *= 2) { + offset >>= 1; + _syncthreads(); + if (index < d) { + int i = offset * (2 * index + 1) - 1; + int j = offset * (2 * index + 2) - 1; + i += CONFLICT_FREE_OFFSET(i); + j += CONFLICT_FREE_OFFSET(j); + int t = temp[i]; + temp[i] = temp[j]; + temp[j] += t; + } + } + + _syncthreads(); + dev_data[i] = temp[ti]; + dev_data[j] = temp[tj]; + + if (dev_blockSum && index == blockDim.x - 1) { + dev_blockSum[blockIdx.x] = lastElement + temp[tj]; + } + } + + __global__ void kernAddPerBlock(int *dev_data, int *dev_add) { + int blockSum = dev_add[blockIdx.x]; + dev_data += blockIdx.x * blockDim.x * 2; + dev_data[threadIdx.x] += blockSum; + dev_data[threadIdx.x + blockDim.x] += blockSum; + } + + void scanHelper(int size, int *dev_data) { + + if (size > 2 * blockSize_sharedMemory) { + + int blocks = size / (2 * blockSize_sharedMemory); + int *dev_blockSum; + cudaMalloc((void**) &dev_blockSum, blocks * sizeof(int)); + + kernScanPerBlock<<>>(size, dev_data, dev_blockSum); + scanHelper(blocks, dev_blockSum); + kernAddPerBlock<<>>(dev_data, dev_blockSum); + + cudaFree(dev_blockSum); + + } else { + kernScanPerBlock<<<1, blockSize_sharedMemory>>>(size, dev_data, nullptr); + } + } + +#else + __global__ void kernScanUpSweepPhase(int threads, int *dev_temp, int offset) { int index = blockIdx.x * blockDim.x + threadIdx.x; if (index >= threads) { @@ -32,35 +128,32 @@ namespace StreamCompaction { dev_temp[index] += t; } - void scanHelper(int steps, int size, int *dev_temp) { + void scanHelper(int size, int *dev_temp) { int threads = size / 2; int offset = 1; - for (int i = 0; i < steps; ++i) { + for (; threads > 0; threads /= 2, offset *= 2) { dim3 blocks((threads + blockSize - 1) / blockSize); kernScanUpSweepPhase<<>>(threads, dev_temp, offset); - threads /= 2; - offset *= 2; } cudaMemset(dev_temp + size - 1, 0, sizeof(int)); threads = 1; offset = size / 2; - for (int i = 0; i < steps; ++i) { + for (; offset > 0; offset /= 2, threads *= 2) { dim3 blocks((threads + blockSize - 1) / blockSize); kernScanDownSweepPhase<<>>(threads, dev_temp, offset); - threads *= 2; - offset /= 2; } } +#endif + /** * Performs prefix-sum (aka scan) on idata, storing the result into odata. */ void scan(int n, int *odata, const int *idata) { - int steps = ilog2ceil(n); - int size = 1 << steps; + int size = 1 << ilog2ceil(n); int *dev_temp; cudaMalloc((void**) &dev_temp, size * sizeof(int)); @@ -68,7 +161,7 @@ namespace StreamCompaction { cudaMemset(dev_temp + n, 0, (size - n) * sizeof(int)); timer().startGpuTimer(); - scanHelper(steps, size, dev_temp); + scanHelper(size, dev_temp); timer().endGpuTimer(); cudaMemcpy(odata, dev_temp, n * sizeof(int), cudaMemcpyDeviceToHost); @@ -88,8 +181,7 @@ namespace StreamCompaction { int compact(int n, int *odata, const int *idata) { dim3 blocks((n + blockSize - 1) / blockSize); - int steps = ilog2ceil(n); - int size = 1 << steps; + int size = 1 << ilog2ceil(n); int *dev_idata, *dev_odata, *dev_bools, *dev_indices; cudaMalloc((void**) &dev_idata, n * sizeof(int)); @@ -103,7 +195,7 @@ namespace StreamCompaction { timer().startGpuTimer(); StreamCompaction::Common::kernMapToBoolean<<>>(n, dev_bools, dev_idata); cudaMemcpy(dev_indices, dev_bools, n * sizeof(int), cudaMemcpyDeviceToDevice); - scanHelper(steps, size, dev_indices); + scanHelper(size, dev_indices); StreamCompaction::Common::kernScatter<<>>(n, dev_odata, dev_idata, dev_bools, dev_indices); timer().endGpuTimer(); @@ -120,26 +212,60 @@ namespace StreamCompaction { return lastBool + lastIdx; } - void radixSort(int n, int *odata, int *idata) { + __global__ void kernBitKNegative(int n, int *dev_idata, int *dev_bools, int bitK) { + int index = blockIdx.x * blockDim.x + threadIdx.x; + if (index >= n) { + return; + } + dev_bools[index] = (idata[index] & (1 << bitK)) == 0; + } - int *dev_idata, *dev_odata, *dev_bools, *dev_scan; - cudaMalloc((void**) &dev_idata, n * sizeof(int)); - cudaMalloc((void**) &dev_odata, n * sizeof(int)); - cudaMalloc((void**) &dev_bools, n * sizeof(int)); - cudaMalloc((void**) &dev_scan, n * sizeof(int)); + __global void kernSplit(int n, int *dev_idata, int *dev_odata, int *dev_scan, int bitK, int totalFalses) { + int index = blockIdx.x * blockDim.x + threadIdx.x; + if (index >= n) { + return; + } + int data = dev_idata[index]; + int scanIdx = dev_scan[index]; + if ((data & (1 << bitK)) == 0) { + dev_odata[scanIdx] = data; + } else { + dev_odata[scanIdx + totalFalses] = data; + } + } - cudaMemcpy(dev_idata, idata, n * sizeof(int), cudaMemcpyHostToDevice); + void radixSort(int n, int *odata, int *idata) { + dim3 blocks((n + blockSize - 1) / blockSize); + int size = 1 << ilog2ceil(n); int maxNum = 0; for (int i = 0; i < n; ++i) { maxNum = std::max(maxNum, idata[n]); } + int maxBit = ilog2ceil(maxNum); - cudaMemcpy(odata, dev_odata, n * sizeof(int), cudaMemcpyDeviceToHost); + int *dev_data1, *dev_data2, *dev_scan; + cudaMalloc((void**) &dev_data1, n * sizeof(int)); + cudaMalloc((void**) &dev_data2, n * sizeof(int)); + cudaMalloc((void**) &dev_scan, size * sizeof(int)); - cudaFree(dev_idata); - cudaFree(dev_odata); - cudaFree(dev_bools); + cudaMemcpy(dev_data1, idata, n * sizeof(int), cudaMemcpyHostToDevice); + + for (int i = 0; i <= maxBit; ++i) { + int lastBool, lastScan; + kernBitKNegative<<>>(n, dev_data1, dev_scan, i); + cudaMemcpy(&lastBool, dev_scan + n - 1, sizeof(int), cudaMemcpyDeviceToHost); + cudaMemset(dev_scan + n, 0, (size - n) * sizeof(int)); + scanHelper(size, dev_scan); + cudaMemcpy(&totalFalses, dev_scan + n - 1, sizeof(int), cudaMemcpyDeviceToHost); + kernSplit<<>>(n, dev_data1, dev_data2, dev_scan, i, lastBool + lastScan); + std::swap(dev_data1, dev_data2); + } + + cudaMemcpy(odata, dev_data1, n * sizeof(int), cudaMemcpyDeviceToHost); + + cudaFree(dev_data1); + cudaFree(dev_data2); cudaFree(dev_scan); } } diff --git a/stream_compaction/naive.cu b/stream_compaction/naive.cu index dce771b..995bf42 100644 --- a/stream_compaction/naive.cu +++ b/stream_compaction/naive.cu @@ -17,10 +17,11 @@ namespace StreamCompaction { if (index >= n) { return; } + int data = dev_in[index]; if (index >= offset) { - dev_out[index] = dev_in[index] + dev_in[index - offset]; + dev_out[index] = data + dev_in[index - offset]; } else { - dev_out[index] = dev_in[index]; + dev_out[index] = data; } } From 5e64a46819d02c12b8af5fdf9eaf0e10db386c26 Mon Sep 17 00:00:00 2001 From: Jiyu Huang <47465661+JiyuHuang@users.noreply.github.com> Date: Tue, 21 Sep 2021 22:01:37 -0400 Subject: [PATCH 06/13] bug fixes --- src/main.cpp | 30 +++++++++---------------- stream_compaction/efficient.cu | 40 +++++++++++++++++++++------------- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index eb91603..6c76602 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -152,33 +152,23 @@ int main(int argc, char* argv[]) { printf("** RADIX SORT TESTS **\n"); printf("*****************************\n"); - genArray(SIZE - 1, a, 100); // Leave a 0 at the end to test that edge case + genArray(SIZE - 1, a, 20); // Leave a 0 at the end to test that edge case a[SIZE - 1] = 0; printArray(SIZE, a, true); - + zeroArray(SIZE, b); printDesc("radix sort, power-of-two"); StreamCompaction::Efficient::radixSort(SIZE, b, a); - for (int i = 0; i < SIZE - 1; ++i) { - if (b[i] > b[i + 1]) { - printf("FAILED\n"); - break; - } else if (i == SIZE - 2) { - printf("passed\n") - } - } - - zeroArray(SIZE, b); + std::memcpy(c, a, SIZE * sizeof(int)); + std::sort(c, c + SIZE); + printCmpResult(SIZE, b, c); + + zeroArray(NPOT, b); printDesc("radix sort, non-power-of-two"); StreamCompaction::Efficient::radixSort(NPOT, b, a); - for (int i = 0; i < NPOT - 1; ++i) { - if (b[i] > b[i + 1]) { - printf("FAILED\n"); - break; - } else if (i == NPOT - 2) { - printf("passed\n") - } - } + std::memcpy(c, a, NPOT * sizeof(int)); + std::sort(c, c + NPOT); + printCmpResult(NPOT, b, c); system("pause"); // stop Win32 console from closing on exit delete[] a; diff --git a/stream_compaction/efficient.cu b/stream_compaction/efficient.cu index 1627540..fcfa6d5 100644 --- a/stream_compaction/efficient.cu +++ b/stream_compaction/efficient.cu @@ -3,6 +3,8 @@ #include "common.h" #include "efficient.h" +#define USING_SHARED_MEMORY 1 + namespace StreamCompaction { namespace Efficient { using StreamCompaction::Common::PerformanceTimer; @@ -14,7 +16,7 @@ namespace StreamCompaction { #if USING_SHARED_MEMORY - const int blockSize_sharedMemory = 128; + const int blockSize_sharedMemory = 32; constexpr int logNumBanks = 5; @@ -22,19 +24,27 @@ namespace StreamCompaction { __global__ void kernScanPerBlock(int n, int *dev_data, int *dev_blockSum) { - extern __shared__ int temp[]; + if (n == 1) { + dev_data[0] = 0; + return; + } + + __shared__ int temp[2 * blockSize_sharedMemory + (2 * blockSize_sharedMemory >> logNumBanks)]; int index = threadIdx.x; dev_data += blockDim.x * blockIdx.x * 2; - n = n > blockDim.x * 2 ? blockDim.x * 2; + if (n > blockDim.x * 2) { + n = blockDim.x * 2; + } int i = index; int j = index + n / 2; int ti = i + CONFLICT_FREE_OFFSET(i); int tj = j + CONFLICT_FREE_OFFSET(j); + temp[ti] = dev_data[i]; temp[tj] = dev_data[j]; - + int lastElement = 0; if (dev_blockSum && index == blockDim.x - 1) { lastElement = temp[tj]; @@ -42,7 +52,7 @@ namespace StreamCompaction { int offset = 1; for (int d = n >> 1; d > 0; d >>= 1) { - _syncthreads(); + __syncthreads(); if (index < d) { int i = offset * (2 * index + 1) - 1; int j = offset * (2 * index + 2) - 1; @@ -59,7 +69,7 @@ namespace StreamCompaction { for (int d = 1; d < n; d *= 2) { offset >>= 1; - _syncthreads(); + __syncthreads(); if (index < d) { int i = offset * (2 * index + 1) - 1; int j = offset * (2 * index + 2) - 1; @@ -71,7 +81,7 @@ namespace StreamCompaction { } } - _syncthreads(); + __syncthreads(); dev_data[i] = temp[ti]; dev_data[j] = temp[tj]; @@ -90,7 +100,7 @@ namespace StreamCompaction { void scanHelper(int size, int *dev_data) { if (size > 2 * blockSize_sharedMemory) { - + int blocks = size / (2 * blockSize_sharedMemory); int *dev_blockSum; cudaMalloc((void**) &dev_blockSum, blocks * sizeof(int)); @@ -100,7 +110,7 @@ namespace StreamCompaction { kernAddPerBlock<<>>(dev_data, dev_blockSum); cudaFree(dev_blockSum); - + } else { kernScanPerBlock<<<1, blockSize_sharedMemory>>>(size, dev_data, nullptr); } @@ -217,10 +227,10 @@ namespace StreamCompaction { if (index >= n) { return; } - dev_bools[index] = (idata[index] & (1 << bitK)) == 0; + dev_bools[index] = (dev_idata[index] & (1 << bitK)) != 0 ? 0 : 1; } - __global void kernSplit(int n, int *dev_idata, int *dev_odata, int *dev_scan, int bitK, int totalFalses) { + __global__ void kernSplit(int n, int *dev_idata, int *dev_odata, int *dev_scan, int bitK, int totalFalses) { int index = blockIdx.x * blockDim.x + threadIdx.x; if (index >= n) { return; @@ -230,17 +240,17 @@ namespace StreamCompaction { if ((data & (1 << bitK)) == 0) { dev_odata[scanIdx] = data; } else { - dev_odata[scanIdx + totalFalses] = data; + dev_odata[index - scanIdx + totalFalses] = data; } } - void radixSort(int n, int *odata, int *idata) { + void radixSort(int n, int *odata, const int *idata) { dim3 blocks((n + blockSize - 1) / blockSize); int size = 1 << ilog2ceil(n); int maxNum = 0; for (int i = 0; i < n; ++i) { - maxNum = std::max(maxNum, idata[n]); + maxNum = std::max(maxNum, idata[i]); } int maxBit = ilog2ceil(maxNum); @@ -257,7 +267,7 @@ namespace StreamCompaction { cudaMemcpy(&lastBool, dev_scan + n - 1, sizeof(int), cudaMemcpyDeviceToHost); cudaMemset(dev_scan + n, 0, (size - n) * sizeof(int)); scanHelper(size, dev_scan); - cudaMemcpy(&totalFalses, dev_scan + n - 1, sizeof(int), cudaMemcpyDeviceToHost); + cudaMemcpy(&lastScan, dev_scan + n - 1, sizeof(int), cudaMemcpyDeviceToHost); kernSplit<<>>(n, dev_data1, dev_data2, dev_scan, i, lastBool + lastScan); std::swap(dev_data1, dev_data2); } From f8263613a2f39c3780146c397eabc4c9e7e3056c Mon Sep 17 00:00:00 2001 From: Jiyu Huang <47465661+JiyuHuang@users.noreply.github.com> Date: Tue, 21 Sep 2021 23:41:42 -0400 Subject: [PATCH 07/13] performance analysis --- img/performance_chart.png | Bin 0 -> 21754 bytes src/main.cpp | 2 +- stream_compaction/common.h | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 img/performance_chart.png diff --git a/img/performance_chart.png b/img/performance_chart.png new file mode 100644 index 0000000000000000000000000000000000000000..86556541eadbd4f2a00448aadfbd2f0fc5ab763c GIT binary patch literal 21754 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 #include "testing_helpers.hpp" -const int SIZE = 1 << 20; // 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/common.h b/stream_compaction/common.h index 3d6f518..bb50e41 100644 --- a/stream_compaction/common.h +++ b/stream_compaction/common.h @@ -13,7 +13,7 @@ #define FILENAME (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) #define checkCUDAError(msg) checkCUDAErrorFn(msg, FILENAME, __LINE__) -const int blockSize = 128; +const int blockSize = 512; /** * Check for CUDA errors; print and exit if there was a problem. From be00993a05b96f84b5a6449dcbd02901da3e5c81 Mon Sep 17 00:00:00 2001 From: Jiyu Huang <47465661+JiyuHuang@users.noreply.github.com> Date: Tue, 21 Sep 2021 23:43:53 -0400 Subject: [PATCH 08/13] graph update --- img/performance_chart.png | Bin 21754 -> 21629 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/img/performance_chart.png b/img/performance_chart.png index 86556541eadbd4f2a00448aadfbd2f0fc5ab763c..a984e61614ee02eb416a7ffffa7371a656130c34 100644 GIT binary patch literal 21629 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

h zaBP^>?4_jZd)S8oOPEZ=0tJ8nmZCd^SZZPqviO*GnNZ!T zUGlcHW#A@!*Zk+O$QO_OJtw-ZuKRVu8JKjebU#VE{0PhW* z{e|)7YLnoE6*pjwwENbLaVypJXmKOF{hY3_ykQo6sml(!Ds+FpAckY3qE(NY6gWu-$#>@Sl6xXmjL4=2gQ3@}&5d<#Lpj?9 z`^(B(8)+8p50OOjVYdN+Hs?~|L2nJ&&&?lKY%r)sPcZZGhCiAhsZH)`s4O3;t zvV%PZN-0>gP5MU=W@nRa#K29ZJ0il(=*uPRS+zIcu|G~%kWr|%15;J zfx|w+M`XvPk#>~hC>X)9d~>*j$@*yg;p{phL6}oe7;d>^{zCah(Ok;ha>HDQ40atj z`4~;iYNmA)jBJOTd+#P)l(6y;Kd==#9QvqksP+c^_+tUsQ#`Pz7VY;>szzc%kKx!x z?bDrL`5L8Q`3Z;f^u3HT{i`fF<8%tmnw3{P1!sTb?M_JXdCrc%X#F+(6LvK`BK6ts z#9In7WH4*FEIlBQZD^Jwok6l0Uz$Xq{3FH6p%WnW*tnHAnt$aC75IQ#Dxbhf+9JSc z0c^ouoS{EfcOyFn7-w2-R%Oe>KiE0MeN=VlAGN8Z?o7H#`5{c91`m6HIfl0zNEs~{U z;nT^&5z4M-G!YA90e&OPEb7IZy+xej_~cd2HVg*xrPP^~?Z!J64=J6ubp{Eg{z;f! z&(!ci9e}PQLZ(j|38SpR!=~};96yAy1n5l1#ky@u=}(>3EQ@hVV;wog&$c~jXNjy7 z_@3!{Bq=?25-iz9MIe1vnK~b~h1KpuR{YYHp-AarT$f4BKPhMPLNR?iO@oiZY?yWh zqOdbDDT%mh7L=H|xVWmg^BZAHiuOBk8aF)MT-VPk4-{CSxx9t3_58nJSMl&95Q_K{ zZBOpTg0=B&(nL7Gn%5^NNvR02ztl*defO6lM(x{p=4nYiV2)<$g|Wy^JRB}QB}aqp_19*(`_pO1Fy{|n013-K*v}Rn=( zz~sF354py+Pe^5GJn}SB5=NoZlA5vhxG8}4r>7g)+HRI4pIRz~r-?i^xz5(vB^U#C zSaRic?tu0u7jM~av#5I-Z=CmR?6UD;Ft+S*Xp0!J@UghCHrfs|()6fVJW8<=| zu<%W8ybcvJStBz1;-Y%AaT7Wx;w%0}9EHK-wi3(N9cBNSg_s~gUfMR4y^Z@p1NZQG z5{!1+Uq=Ix(DjfQw)#=R1Ozra7z+!3VB<}A?-dEqbzyyi6lTLGy&1<)pcUcs{2dOS zXyaQRw$wc*)qU-chtJt^+nyh%lJY&JAr>Zytt6PS4f&QCmi~e?iM1G&z5I<1JJ?Bw zAtR*sf6gT1141l0*lniyPL&wj;u0O@krX#gFd;EQ*zCwj#ClUfB?@1FEmHY^>MLRm-iuSY=>HOAqo~X%oM)b>*3{W_++xKPeC%dl zPO~RXg9Yf;&kxS9J%^dqO#F{cT5P(2rbrZ<#fTXcQt_~-d_EG1;fS2I!M?nnOS_gK z{iKL|6PGv!4-Ij36lcrzH!hvw1Q=VOr^gb#u&(EAAiz#j@+6tHO;$y&*De+@%{us> zSu*kkvFqCcjqf;L7I&UCx3tWCP5s?_x46hTpP!1xLs49Tk>s9NNfe6@Tm>$G^i^LzolLiNxu1UmRq%o1xsWWO@0|~7VH#;VgZarvq;IS zJf?`on|eMUhMTAoIp5vg-TwTM8-Jj`@bCj-*cmQ1O>k>fmZl@>gC6p#{3yl4@keU4T5MQ@ zYc={WrXK34RT`{G>@%B4s}T|sSKSI`8$+K}*N83otIx9FRPieTxh@g zFGbkJv$>_uPB4EnyNOE=N)BjpcmFa%p}H3$+o^pxUnD_~E*Y7&5$QX5sk6%nPK~*Tv z*1qOUDbnn4P4o5BWTM*VI;GEppEkf%9)i{kY3GWK($m_n|SKE5uUEmL4ZCd`~hmV zvcHM&w3bLp#5A?9C+BCANTi`u>Vh+vDgUX)2f|!YNo&Tyj2}2{jzJKmb~x7-sSUn z+z8rKvVMZ2|1NCvm8QdiWL-iC)5W3uOYrRaxyC86`qP9M1E!-kJkO(CVXhaM-MAsG z1T?mduGQ@kFe+|MRq}^SA7Z{J>XO*`e2s1UX&6jS9+inSWmfwj!TSNtCbifvMK)aN z7ry!km4E*}PT?ZVO`ynKI}WF86r|F1HXMQsJ&KR3M1<@BH5?8S%_Eaa%#*}tih^@A zJ(>6#!KADalt66W^<+D|Xl^9xV~X~!`|#VBq=%1me`MQpqo5DqlOORFKyN*xMJSbp z#^y>Lnhg~rPMazgCJqZudiM&QE_Gi9jp8QUvg6^cFy=IP|6*&jvwbK)Y5hm_$#`&cbSRy;0kKryNK^~;su%&gTfa84%t zW{Ti<;mXJdpK6W#+@m-XzrsYi8sDXnA&Ji?15Mt&>@_5Iz#&c+3tz15MlkE;e|WVB zONVj@e)&KkARFDuS4E-%lQwgq2p?*!B$pB7pifdus<<4TNyXA{=ixlyWZ@BxoSoTc z*pOpO0?C!L~(q zU7@MZc2wl~o*P9qk#+QSWxqn>64_#cW0=kHAI5ko8rh`_ z{?PHU2p1%LSXID!>1J=w{3E=!PwfxM1(kS|2p`jx=T+vpHNB-xWhnQ!MT&FI$WkL5 zmPng~cexMZOF!d*ErXh&CV?wtb%6$qrt{<6({rz;k>OjACqb+ZHyT;*Gm0@Zqn09X zlWEYPz<evlao+8N%Q%un`$p7M|Tm=iCb zQ~uvS(Umr!#j&a@ljU`MDMOj`%5e7^bauU%*~|Fo)Q(i5=En_5{;3@Xs#&$qHB&X! zsW2vHW9XWCfoC&PJx|y%sfC(BXGa|Mgsg-K%xX z@1KCW%qK3QTg?;P*P1+71HUd0;j3nyfEPApeXddmry(A1i8!fKqM^ac5gx1(F=CqA zdr^i(-$w#|m6k4q1VA<2G3PxrnmH;)W=L;e*A?{+%gt7zc=q?mN4r6N;KSgo<|%^1 zqv%k!Y8GO@b`flZ*yh0OOiA;0;M9hx1H}*Q$gtOS8bNHqQw~sX)=f*LWfk?B#%Sfe z9=V&>vFc>JepQ75F3yJShksja(*-(_{&SSy7TWV|%|q_(7^5!hp&RZ{=YbpM$gcu2 zZOoNQ`#1V3*5xTQH?PY<+1TO3<*}a=KbJJ!CVTc`zyX$x?3vt~T_8PKL@Pocq~0j= zSAd2NCUmYSBp=3Q$sC?7*u`vRrwXaaCzx(ImM^#{26~WX+w?9gZwSN=It8)>fAtQvBmpA%j>%S z^{$!BAlwzJM%|g8$ZcguO)!5f0)A>_-NnHPOiSy#R~K8CGM?DrJRsZYrh4e3=xaF_ zGtIei7P^^PbWp=6!k8IjK5hMk;hab9jDL@ZqGwR$hGBzVqrXCuuuL^q8Qou2fnZMVGbAx?Vu ztdZc`gdO*d4R#Jb|s1npLV-(`15DHEBaVwm*S zKxJ9fjrSwHg(kJp%lU(p!ULc5Fu!I%l|9vdFr6;>lH?7r8oI|B{ncZ4{6Eaxx|F=~ zJ&=I0RADbb70wOon+~7BFLPhHBINbh4az&%f1h$EONfQ-M@|a~!5UI)Sy#^v2PSE@ zksbM*owa}`H*3aDqJNj5ejGV94a+vbFW z5QoN;NsNW5MMVDn%sUrhb1044LW|(A$1sCXX6HnLcnD3)ikpEk(Qf6(2wKDet|Dhh z?bMf-vl*c4+looqmRiwUGWdb?z^#Rh`!M3=-=#20N6vF7Bo>^remT3b%x&x|COkv5 z?Vv}Jr6|t+)nCD!EM8~2T8T*vprSyp0SPrs(yr`Xi=|G1+uD0Sa8`Bn8)ds>U}e!> zR_SfJz&AgKRa`~|xth&CUVq8}6mcKABUE~Y;noTikLd~S4Fx2Oe0~Eb?S!~mCGw-`JCYQd4wB+Zu#d0JiDn~`AYs;{b_=1 z$>sWuBre~=QKD8qr*DMYFI;Cakp9MTdY4HA7V==7G`d_W1x8Q*rSf+&kI0}JpE06Q zsvBadYJ0dnXEjo8W&wFg_Co8&9~TL2h)OkDT4#2aL}CN${tjm> zx>mDu?sR%fgCf&}7%ejnwHgKyD0)Fxg`OWwyC=$Bjj~>|?olqf`4wQt8yNl`KwDpq zdlwFW{fdtTQpE{&cxDi?a>a|n(9Jp&5%ZqV=IkNLtz9YTK2^)E`Ha1psR1ChOxbNsmK32wNV4 zPK90g5jOBCPZXWcBhH9}aD4LZ-g_g}5N!+Bip!V&nhjVr z^&h(|3Sm{*TH%$6kW6Ili{C>(z?@?B*x=N=CKl6}Ae5b9((`Tz{fW6ENBj^KlFIvJ zR%sRi1J5{hNZYac40lGD#6V1^M8#0%*l!uuL^@`p5!tC?KUQPs6fc(d5xZJ;;)~i2 zZg7_M&a&Kj#}#IR3w|4F%l08$TMMMG=}jbQyo`E68re2-uM73mu-QaWKN$@mtaO$; z7A?Vfok1I#Ceb&OO8D6`yiCmTg&P$UBXZJh+S~OiC?e|etk>bXwqU}Q)G%Qa1^Y&2 z-`?lGaIoJkAW8!m^j}bai2_nbKw97iShKUl)^46KULovRVp)AlP{?J#uc22!Eqk)B zNGjn~Qqx#@Iz=8$7|KQ8{71ud(0H6dkF<;DQkI}%U3RTj&Og@7a%gJ~=u+glLbSzHokV-3 zMm2aN|2>R@_%`>Q8I0xMCKza?2*#NRNQtpWkzV*rG~GJynBG-4O%kfj*9~S{ zQJ~uEhO9pQW@>l@$nOd1B6f|g(myCMR(62#%Xs| z8nt-Rl)w%_A}hnc@63m{J`hFs)$B$*tbEdRVwN0nezakZf5x$%9{1-AHv|B7TQt<>9-uiBKle`x zVGa%s+#L50mQ_rfxb)I8YPu?M&O2VzU)6M3%@a4_$6CvyuueB@sVv08oUXR4zI)Jg z4J4#8BFLM==8GwZ6Z;Rp{q07t(0-<9*CMGw*zE!v-_e9Xhahhi59dYx_;^4c#e6l{ z;v4g(zIg~`jvb;WYRrJ@js@*Y*NQ%{M{Q&7fJxd8aL@96jKQgi6CG~NOeBxFNtStP z2oWl-Vb9=Av@svAt0dGeZYMY31f2liCap+f%k4JFJ6>)CX35l}-USI5V?#r=$ToZV zp+ht_H~3eB8Bs24NACM=t)A(D(fIb3Lr|<&fU9=c`R9!s@r&Vc7v|Xo5jdz-IMQ1*??QOlPJ5#Gm2D99E@1{`JG~EsLaCRWGxXdJ{to;`aLR zu8gqcT8)h6jH;ftPH9Sks&Ud^sxYSu7k`>Y6T=5~ZH3+?+fZj8YRo%%SDL2!$tgPY zG!QYOi)y?r24{lK@Nc%57njsp!8fRol`}q4U7!Y*8XW94!g4f~dZ-}%xmI$vS@ah( zqxF+12_cO{vihBYYG>e;pkXvtGc!LzQf*V*+3LF1qMJ2E*z-{Isu+P&f}qP>1R0A5 zb@H+mmT6cDKg0M5^ek9F1AkxLEPQ9!NDP4XemQ+~<-6PKetDHo!RZNafB8K4{um!{ zU|JWu8cVtJ=KlcB!$K|gvmkkJc6l|icqG&)eX2R5+88XfT}SE9+owK;JzXNE{sRVzgTDgdysH8 b9agHAxjz-+5}a#+MVnk4JucGhuiX15N`qY| literal 21754 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 Date: Tue, 21 Sep 2021 23:45:52 -0400 Subject: [PATCH 09/13] data saved --- img/Book1.xlsx | Bin 0 -> 15309 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 img/Book1.xlsx diff --git a/img/Book1.xlsx b/img/Book1.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..267453710b500db950719333f01afcfbf485b929 GIT binary patch literal 15309 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 Rs38M+zGVd5e^8u&{vS`* Date: Wed, 22 Sep 2021 11:57:05 -0400 Subject: [PATCH 10/13] bug fix --- img/Book1.xlsx | Bin 15309 -> 0 bytes img/data.xlsx | Bin 0 -> 20367 bytes src/main.cpp | 2 +- stream_compaction/efficient.cu | 37 +++++++++++++++++---------------- 4 files changed, 20 insertions(+), 19 deletions(-) delete mode 100644 img/Book1.xlsx create mode 100644 img/data.xlsx diff --git a/img/Book1.xlsx b/img/Book1.xlsx deleted file mode 100644 index 267453710b500db950719333f01afcfbf485b929..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15309 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 literal 0 HcmV?d00001 diff --git a/src/main.cpp b/src/main.cpp index a54ddf4..6c76602 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/efficient.cu b/stream_compaction/efficient.cu index fcfa6d5..1e53fdc 100644 --- a/stream_compaction/efficient.cu +++ b/stream_compaction/efficient.cu @@ -16,11 +16,11 @@ namespace StreamCompaction { #if USING_SHARED_MEMORY - const int blockSize_sharedMemory = 32; + const int blockSize_sharedMemory = 128; - constexpr int logNumBanks = 5; + const int logNumBanks = 5; - #define CONFLICT_FREE_OFFSET(n) ((n) >> logNumBanks); + #define CONFLICT_FREE_OFFSET(n) ((n) >> logNumBanks) __global__ void kernScanPerBlock(int n, int *dev_data, int *dev_blockSum) { @@ -28,34 +28,35 @@ namespace StreamCompaction { dev_data[0] = 0; return; } + if (threadIdx.x >= n / 2) { + return; + } - __shared__ int temp[2 * blockSize_sharedMemory + (2 * blockSize_sharedMemory >> logNumBanks)]; + __shared__ int temp[2 * blockSize_sharedMemory + CONFLICT_FREE_OFFSET(2 * blockSize_sharedMemory)]; - int index = threadIdx.x; dev_data += blockDim.x * blockIdx.x * 2; if (n > blockDim.x * 2) { n = blockDim.x * 2; } - int i = index; - int j = index + n / 2; + int i = threadIdx.x; + int j = threadIdx.x + n / 2; int ti = i + CONFLICT_FREE_OFFSET(i); int tj = j + CONFLICT_FREE_OFFSET(j); - temp[ti] = dev_data[i]; temp[tj] = dev_data[j]; int lastElement = 0; - if (dev_blockSum && index == blockDim.x - 1) { + if (dev_blockSum && threadIdx.x == blockDim.x - 1) { lastElement = temp[tj]; } int offset = 1; for (int d = n >> 1; d > 0; d >>= 1) { __syncthreads(); - if (index < d) { - int i = offset * (2 * index + 1) - 1; - int j = offset * (2 * index + 2) - 1; + if (threadIdx.x < d) { + int i = offset * (2 * threadIdx.x + 1) - 1; + int j = offset * (2 * threadIdx.x + 2) - 1; i += CONFLICT_FREE_OFFSET(i); j += CONFLICT_FREE_OFFSET(j); temp[j] += temp[i]; @@ -63,16 +64,16 @@ namespace StreamCompaction { offset *= 2; } - if (index == n / 2 - 1) { - temp[tj] = 0; + if (threadIdx.x == 0) { + temp[n - 1 + CONFLICT_FREE_OFFSET(n - 1)] = 0; } for (int d = 1; d < n; d *= 2) { offset >>= 1; __syncthreads(); - if (index < d) { - int i = offset * (2 * index + 1) - 1; - int j = offset * (2 * index + 2) - 1; + if (threadIdx.x < d) { + int i = offset * (2 * threadIdx.x + 1) - 1; + int j = offset * (2 * threadIdx.x + 2) - 1; i += CONFLICT_FREE_OFFSET(i); j += CONFLICT_FREE_OFFSET(j); int t = temp[i]; @@ -85,7 +86,7 @@ namespace StreamCompaction { dev_data[i] = temp[ti]; dev_data[j] = temp[tj]; - if (dev_blockSum && index == blockDim.x - 1) { + if (dev_blockSum && threadIdx.x == blockDim.x - 1) { dev_blockSum[blockIdx.x] = lastElement + temp[tj]; } } From 5f5432c6567d4ad24be399c38f28d94616d9cafe Mon Sep 17 00:00:00 2001 From: Jiyu Huang <47465661+JiyuHuang@users.noreply.github.com> Date: Wed, 22 Sep 2021 12:15:36 -0400 Subject: [PATCH 11/13] performance charts --- img/performance_chart.png | Bin 21629 -> 31017 bytes img/performance_chart_large.png | Bin 0 -> 25516 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 img/performance_chart_large.png diff --git a/img/performance_chart.png b/img/performance_chart.png index a984e61614ee02eb416a7ffffa7371a656130c34..e3092d501ff438d9d4635219b43aff58a2b37c09 100644 GIT binary patch literal 31017 zcmbrm2UJthwl0h!AOeD-ARVQtG^KY$M0ydFUX+gX8hQi-q)4xl&;%5LAe~U9_s~Hg zq4yFxp(VVHo^#K+_uMi5H~u$9V5gA1*IM&ibAI2Pa}%baCQo*Q_68mv9+{%T3r#${ ztMhnxSJtl+0`JJ^a`pp2IrQy;FeePbqS6oq1p;+TX3Or>FzYa_u?-?mY>lHk_5BHcq z;R7$9KBFK44yGTNZvux_JQ;f6@aamR2ymdlzvc`amfqPP-v(&S*XD z{(|F=g_CO^o(!pFNJ*ADpu{kD@a9!)gGNS#YdOrie_*<%A7}Vb5&PTxC`fFNlXQ8j z1NULDd;t!~w@>iEye7Et75FqRl+>AyU;ELD3h(OVl@M4f)sZ{{Z) zwd?2Gd|IS0FcE{W>S@zAspS1oNUF?tN8kitb@m&%{?VQvPO;C)z>wYl-( z*iuPLEZ2L+m>A(6;6g+L_Oqr@UOMKBAG{srm+LaXojY(l9Li9^mvzc^JQMP${yJGk zykup_;8+N4{&r!KU#;8`kz|w~scY@oNlPN~+6?UZwwV-2Ie2qwBVJbU&~%#?U6SV$ zl~*sRFuU;sQn_e;02lqK7^S*@*thBf-I3Ui4NvlR_UE|g7+%bPXR4~K^&&_FueE4l z=APGbDsS}pYPJtr4ENazveEd|1u036+)j;k{LRUOvtH~$oFoU~=GvJ2CXgJ+h2Yx8 zIVp7YhOBh5*^iE!zC{a-(sU)1yn}JZnA*3oixE83FFoOEO3!o>;*W67 zV6IE`_bWRyZaF9I!?qZb<2ftvi{w7SyB&oHUoW)LJG=>EALCUef^t5_MD}T({V)f) zr)Kyxd_$Rx0_pKw+yA;onQx7OR}3rh`(xbpS^6% z@Y9ssDm+EOo~b65$VB z5X)5IygIXCot!-R%j_f00}szRgv<}{M;{&zllcMu0C@WU=C=NS`@8?wFH&3qT%T=> zw$IpW4tG_54A+o;ZWgTTBoZ&?H(+3jyK0_=`LSOJ>E3+(R?&1)cM69pJBbL5%dc

9G18{UgMfd9_^HRGls#>tsmc=CI-(D}8< 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 H?>zrsh+JgH literal 21629 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

h zaBP^>?4_jZd)S8oOPEZ=0tJ8nmZCd^SZZPqviO*GnNZ!T zUGlcHW#A@!*Zk+O$QO_OJtw-ZuKRVu8JKjebU#VE{0PhW* z{e|)7YLnoE6*pjwwENbLaVypJXmKOF{hY3_ykQo6sml(!Ds+FpAckY3qE(NY6gWu-$#>@Sl6xXmjL4=2gQ3@}&5d<#Lpj?9 z`^(B(8)+8p50OOjVYdN+Hs?~|L2nJ&&&?lKY%r)sPcZZGhCiAhsZH)`s4O3;t zvV%PZN-0>gP5MU=W@nRa#K29ZJ0il(=*uPRS+zIcu|G~%kWr|%15;J zfx|w+M`XvPk#>~hC>X)9d~>*j$@*yg;p{phL6}oe7;d>^{zCah(Ok;ha>HDQ40atj z`4~;iYNmA)jBJOTd+#P)l(6y;Kd==#9QvqksP+c^_+tUsQ#`Pz7VY;>szzc%kKx!x z?bDrL`5L8Q`3Z;f^u3HT{i`fF<8%tmnw3{P1!sTb?M_JXdCrc%X#F+(6LvK`BK6ts z#9In7WH4*FEIlBQZD^Jwok6l0Uz$Xq{3FH6p%WnW*tnHAnt$aC75IQ#Dxbhf+9JSc z0c^ouoS{EfcOyFn7-w2-R%Oe>KiE0MeN=VlAGN8Z?o7H#`5{c91`m6HIfl0zNEs~{U z;nT^&5z4M-G!YA90e&OPEb7IZy+xej_~cd2HVg*xrPP^~?Z!J64=J6ubp{Eg{z;f! z&(!ci9e}PQLZ(j|38SpR!=~};96yAy1n5l1#ky@u=}(>3EQ@hVV;wog&$c~jXNjy7 z_@3!{Bq=?25-iz9MIe1vnK~b~h1KpuR{YYHp-AarT$f4BKPhMPLNR?iO@oiZY?yWh zqOdbDDT%mh7L=H|xVWmg^BZAHiuOBk8aF)MT-VPk4-{CSxx9t3_58nJSMl&95Q_K{ zZBOpTg0=B&(nL7Gn%5^NNvR02ztl*defO6lM(x{p=4nYiV2)<$g|Wy^JRB}QB}aqp_19*(`_pO1Fy{|n013-K*v}Rn=( zz~sF354py+Pe^5GJn}SB5=NoZlA5vhxG8}4r>7g)+HRI4pIRz~r-?i^xz5(vB^U#C zSaRic?tu0u7jM~av#5I-Z=CmR?6UD;Ft+S*Xp0!J@UghCHrfs|()6fVJW8<=| zu<%W8ybcvJStBz1;-Y%AaT7Wx;w%0}9EHK-wi3(N9cBNSg_s~gUfMR4y^Z@p1NZQG z5{!1+Uq=Ix(DjfQw)#=R1Ozra7z+!3VB<}A?-dEqbzyyi6lTLGy&1<)pcUcs{2dOS zXyaQRw$wc*)qU-chtJt^+nyh%lJY&JAr>Zytt6PS4f&QCmi~e?iM1G&z5I<1JJ?Bw zAtR*sf6gT1141l0*lniyPL&wj;u0O@krX#gFd;EQ*zCwj#ClUfB?@1FEmHY^>MLRm-iuSY=>HOAqo~X%oM)b>*3{W_++xKPeC%dl zPO~RXg9Yf;&kxS9J%^dqO#F{cT5P(2rbrZ<#fTXcQt_~-d_EG1;fS2I!M?nnOS_gK z{iKL|6PGv!4-Ij36lcrzH!hvw1Q=VOr^gb#u&(EAAiz#j@+6tHO;$y&*De+@%{us> zSu*kkvFqCcjqf;L7I&UCx3tWCP5s?_x46hTpP!1xLs49Tk>s9NNfe6@Tm>$G^i^LzolLiNxu1UmRq%o1xsWWO@0|~7VH#;VgZarvq;IS zJf?`on|eMUhMTAoIp5vg-TwTM8-Jj`@bCj-*cmQ1O>k>fmZl@>gC6p#{3yl4@keU4T5MQ@ zYc={WrXK34RT`{G>@%B4s}T|sSKSI`8$+K}*N83otIx9FRPieTxh@g zFGbkJv$>_uPB4EnyNOE=N)BjpcmFa%p}H3$+o^pxUnD_~E*Y7&5$QX5sk6%nPK~*Tv z*1qOUDbnn4P4o5BWTM*VI;GEppEkf%9)i{kY3GWK($m_n|SKE5uUEmL4ZCd`~hmV zvcHM&w3bLp#5A?9C+BCANTi`u>Vh+vDgUX)2f|!YNo&Tyj2}2{jzJKmb~x7-sSUn z+z8rKvVMZ2|1NCvm8QdiWL-iC)5W3uOYrRaxyC86`qP9M1E!-kJkO(CVXhaM-MAsG z1T?mduGQ@kFe+|MRq}^SA7Z{J>XO*`e2s1UX&6jS9+inSWmfwj!TSNtCbifvMK)aN z7ry!km4E*}PT?ZVO`ynKI}WF86r|F1HXMQsJ&KR3M1<@BH5?8S%_Eaa%#*}tih^@A zJ(>6#!KADalt66W^<+D|Xl^9xV~X~!`|#VBq=%1me`MQpqo5DqlOORFKyN*xMJSbp z#^y>Lnhg~rPMazgCJqZudiM&QE_Gi9jp8QUvg6^cFy=IP|6*&jvwbK)Y5hm_$#`&cbSRy;0kKryNK^~;su%&gTfa84%t zW{Ti<;mXJdpK6W#+@m-XzrsYi8sDXnA&Ji?15Mt&>@_5Iz#&c+3tz15MlkE;e|WVB zONVj@e)&KkARFDuS4E-%lQwgq2p?*!B$pB7pifdus<<4TNyXA{=ixlyWZ@BxoSoTc z*pOpO0?C!L~(q zU7@MZc2wl~o*P9qk#+QSWxqn>64_#cW0=kHAI5ko8rh`_ z{?PHU2p1%LSXID!>1J=w{3E=!PwfxM1(kS|2p`jx=T+vpHNB-xWhnQ!MT&FI$WkL5 zmPng~cexMZOF!d*ErXh&CV?wtb%6$qrt{<6({rz;k>OjACqb+ZHyT;*Gm0@Zqn09X zlWEYPz<evlao+8N%Q%un`$p7M|Tm=iCb zQ~uvS(Umr!#j&a@ljU`MDMOj`%5e7^bauU%*~|Fo)Q(i5=En_5{;3@Xs#&$qHB&X! zsW2vHW9XWCfoC&PJx|y%sfC(BXGa|Mgsg-K%xX z@1KCW%qK3QTg?;P*P1+71HUd0;j3nyfEPApeXddmry(A1i8!fKqM^ac5gx1(F=CqA zdr^i(-$w#|m6k4q1VA<2G3PxrnmH;)W=L;e*A?{+%gt7zc=q?mN4r6N;KSgo<|%^1 zqv%k!Y8GO@b`flZ*yh0OOiA;0;M9hx1H}*Q$gtOS8bNHqQw~sX)=f*LWfk?B#%Sfe z9=V&>vFc>JepQ75F3yJShksja(*-(_{&SSy7TWV|%|q_(7^5!hp&RZ{=YbpM$gcu2 zZOoNQ`#1V3*5xTQH?PY<+1TO3<*}a=KbJJ!CVTc`zyX$x?3vt~T_8PKL@Pocq~0j= zSAd2NCUmYSBp=3Q$sC?7*u`vRrwXaaCzx(ImM^#{26~WX+w?9gZwSN=It8)>fAtQvBmpA%j>%S z^{$!BAlwzJM%|g8$ZcguO)!5f0)A>_-NnHPOiSy#R~K8CGM?DrJRsZYrh4e3=xaF_ zGtIei7P^^PbWp=6!k8IjK5hMk;hab9jDL@ZqGwR$hGBzVqrXCuuuL^q8Qou2fnZMVGbAx?Vu ztdZc`gdO*d4R#Jb|s1npLV-(`15DHEBaVwm*S zKxJ9fjrSwHg(kJp%lU(p!ULc5Fu!I%l|9vdFr6;>lH?7r8oI|B{ncZ4{6Eaxx|F=~ zJ&=I0RADbb70wOon+~7BFLPhHBINbh4az&%f1h$EONfQ-M@|a~!5UI)Sy#^v2PSE@ zksbM*owa}`H*3aDqJNj5ejGV94a+vbFW z5QoN;NsNW5MMVDn%sUrhb1044LW|(A$1sCXX6HnLcnD3)ikpEk(Qf6(2wKDet|Dhh z?bMf-vl*c4+looqmRiwUGWdb?z^#Rh`!M3=-=#20N6vF7Bo>^remT3b%x&x|COkv5 z?Vv}Jr6|t+)nCD!EM8~2T8T*vprSyp0SPrs(yr`Xi=|G1+uD0Sa8`Bn8)ds>U}e!> zR_SfJz&AgKRa`~|xth&CUVq8}6mcKABUE~Y;noTikLd~S4Fx2Oe0~Eb?S!~mCGw-`JCYQd4wB+Zu#d0JiDn~`AYs;{b_=1 z$>sWuBre~=QKD8qr*DMYFI;Cakp9MTdY4HA7V==7G`d_W1x8Q*rSf+&kI0}JpE06Q zsvBadYJ0dnXEjo8W&wFg_Co8&9~TL2h)OkDT4#2aL}CN${tjm> zx>mDu?sR%fgCf&}7%ejnwHgKyD0)Fxg`OWwyC=$Bjj~>|?olqf`4wQt8yNl`KwDpq zdlwFW{fdtTQpE{&cxDi?a>a|n(9Jp&5%ZqV=IkNLtz9YTK2^)E`Ha1psR1ChOxbNsmK32wNV4 zPK90g5jOBCPZXWcBhH9}aD4LZ-g_g}5N!+Bip!V&nhjVr z^&h(|3Sm{*TH%$6kW6Ili{C>(z?@?B*x=N=CKl6}Ae5b9((`Tz{fW6ENBj^KlFIvJ zR%sRi1J5{hNZYac40lGD#6V1^M8#0%*l!uuL^@`p5!tC?KUQPs6fc(d5xZJ;;)~i2 zZg7_M&a&Kj#}#IR3w|4F%l08$TMMMG=}jbQyo`E68re2-uM73mu-QaWKN$@mtaO$; z7A?Vfok1I#Ceb&OO8D6`yiCmTg&P$UBXZJh+S~OiC?e|etk>bXwqU}Q)G%Qa1^Y&2 z-`?lGaIoJkAW8!m^j}bai2_nbKw97iShKUl)^46KULovRVp)AlP{?J#uc22!Eqk)B zNGjn~Qqx#@Iz=8$7|KQ8{71ud(0H6dkF<;DQkI}%U3RTj&Og@7a%gJ~=u+glLbSzHokV-3 zMm2aN|2>R@_%`>Q8I0xMCKza?2*#NRNQtpWkzV*rG~GJynBG-4O%kfj*9~S{ zQJ~uEhO9pQW@>l@$nOd1B6f|g(myCMR(62#%Xs| z8nt-Rl)w%_A}hnc@63m{J`hFs)$B$*tbEdRVwN0nezakZf5x$%9{1-AHv|B7TQt<>9-uiBKle`x zVGa%s+#L50mQ_rfxb)I8YPu?M&O2VzU)6M3%@a4_$6CvyuueB@sVv08oUXR4zI)Jg z4J4#8BFLM==8GwZ6Z;Rp{q07t(0-<9*CMGw*zE!v-_e9Xhahhi59dYx_;^4c#e6l{ z;v4g(zIg~`jvb;WYRrJ@js@*Y*NQ%{M{Q&7fJxd8aL@96jKQgi6CG~NOeBxFNtStP z2oWl-Vb9=Av@svAt0dGeZYMY31f2liCap+f%k4JFJ6>)CX35l}-USI5V?#r=$ToZV zp+ht_H~3eB8Bs24NACM=t)A(D(fIb3Lr|<&fU9=c`R9!s@r&Vc7v|Xo5jdz-IMQ1*??QOlPJ5#Gm2D99E@1{`JG~EsLaCRWGxXdJ{to;`aLR zu8gqcT8)h6jH;ftPH9Sks&Ud^sxYSu7k`>Y6T=5~ZH3+?+fZj8YRo%%SDL2!$tgPY zG!QYOi)y?r24{lK@Nc%57njsp!8fRol`}q4U7!Y*8XW94!g4f~dZ-}%xmI$vS@ah( zqxF+12_cO{vihBYYG>e;pkXvtGc!LzQf*V*+3LF1qMJ2E*z-{Isu+P&f}qP>1R0A5 zb@H+mmT6cDKg0M5^ek9F1AkxLEPQ9!NDP4XemQ+~<-6PKetDHo!RZNafB8K4{um!{ zU|JWu8cVtJ=KlcB!$K|gvmkkJc6l|icqG&)eX2R5+88XfT}SE9+owK;JzXNE{sRVzgTDgdysH8 b9agHAxjz-+5}a#+MVnk4JucGhuiX15N`qY| diff --git a/img/performance_chart_large.png b/img/performance_chart_large.png new file mode 100644 index 0000000000000000000000000000000000000000..304b3634e1f8190d6f79afcf233d7d1f80711d5d GIT binary patch literal 25516 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 literal 0 HcmV?d00001 From c61c89a529c3d06ec21627bc26ff08446a9a3aea Mon Sep 17 00:00:00 2001 From: Jiyu Huang <47465661+JiyuHuang@users.noreply.github.com> Date: Wed, 22 Sep 2021 15:46:15 -0400 Subject: [PATCH 12/13] readme --- README.md | 107 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 101 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 0e38ddb..130d21e 100644 --- a/README.md +++ b/README.md @@ -3,12 +3,107 @@ 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) +* Jiyu Huang + * [LinkedIn](https://www.linkedin.com/in/jiyu-huang-0123/) +* Tested on: Windows 10, i7-6700 @ 3.41GHz 16GB, Quadro P1000 4096MB (Towne 062 Lab) -### (TODO: Your README) +### Overview -Include analysis, etc. (Remember, this is public, so don't put -anything here that you don't want to share with the world.) +This project involves CUDA implementation of GPU parallel algorithms such as scan (prefix sum), stream compaction, and Radix sort. Specifically, the following are implemented: +* CPU scan (serialized, used as comparison) +* CPU stream compact (serialized, used as comparison) +* CPU stream compact with CPU scan (serialized, used as comparison) +* naive version of GPU scan +* work-efficient GPU scan +* work-efficient GPU scan with shared memory and no bank conflict +* GPU stream compaction using optimized work-efficient GPU scan +* GPU Radix sort using optimized work-efficient GPU scan + +Thrust library's version of exclusive scan is also used as comparison. + +### Performance Analysis + +The performance of various implementations of scan are illustrated below. + +[performance_chart](!/img/performance_chart.png) +[performance_chart_large](!/img/performance_chart_large.png) + +* As can be seen from the graph, starting from array size 2^15 (32768), GPU algorithms show performance advantages towards the CPU implementation, due to the advantages of parallelism. + +* The naive version of GPU scan performs sufficiently well until the array size reaches 2^17 (131072), after which point the performance drops significantly. As shown in the large array graph, it becomes the slowest implementation, even slower than CPU implementation. This is due to the fact that the naive version of GPU scan is not work efficient and in total performs the most amount of computations. + +* The work-efficient version of GPU scan initially performs worse than other implementations, but catches up and ends up reducing execution time compared to CPU implemetation and naive GPU implementation. The initial slowness likely results from the extra amount of kernel invocations. + +* The shared memory optimization has a significant impact on improving performance and is the fastest implementation in this project, as it should be; operating on shared memory efficiently does prove to be much faster than operating on global memory. + +* Thrust library's scan function is almost always the fastest version (except for when the array size is small, or when the array size goes from 2^17 (131072) to 2^18 (262144), where Thrust's scan function has a sudden performance drop). + +I would like to delve deeper into the execution timelines for each implementation (and understand why Thrust's scan is so fast), but since I am using the lab computer with no admin access to enable Nsight tracing, I'm temporarily unable to do that. + +### Test Results + +The following test output is generated with array size of one million (2^20). Extra Radix sort tests (one for power-of-two, one for non-power-of-two) testing the sorting correctness are also included at the end. + +``` +**************** +** SCAN TESTS ** +**************** + [ 19 18 31 41 16 17 6 12 41 4 7 45 31 ... 11 0 ] +==== cpu scan, power-of-two ==== + elapsed time: 1.6692ms (std::chrono Measured) + [ 0 19 37 68 109 125 142 148 160 201 205 212 257 ... 25680674 25680685 ] +==== cpu scan, non-power-of-two ==== + elapsed time: 1.6274ms (std::chrono Measured) + passed +==== naive scan, power-of-two ==== + elapsed time: 2.83338ms (CUDA Measured) + passed +==== naive scan, non-power-of-two ==== + elapsed time: 2.64646ms (CUDA Measured) + passed +==== work-efficient scan, power-of-two ==== + elapsed time: 1.11798ms (CUDA Measured) + passed +==== work-efficient scan, non-power-of-two ==== + elapsed time: 0.829952ms (CUDA Measured) + passed +==== thrust scan, power-of-two ==== + elapsed time: 0.434304ms (CUDA Measured) + passed +==== thrust scan, non-power-of-two ==== + elapsed time: 0.463584ms (CUDA Measured) + passed + +***************************** +** STREAM COMPACTION TESTS ** +***************************** + [ 2 2 1 2 1 2 2 1 2 0 3 1 1 ... 0 0 ] +==== cpu compact without scan, power-of-two ==== + elapsed time: 3.0586ms (std::chrono Measured) + [ 2 2 1 2 1 2 2 1 2 3 1 1 1 ... 1 1 ] + passed +==== cpu compact without scan, non-power-of-two ==== + elapsed time: 2.5556ms (std::chrono Measured) + [ 2 2 1 2 1 2 2 1 2 3 1 1 1 ... 2 1 ] + passed +==== cpu compact with scan ==== + elapsed time: 5.8896ms (std::chrono Measured) + [ 2 2 1 2 1 2 2 1 2 3 1 1 1 ... 1 1 ] + passed +==== work-efficient compact, power-of-two ==== + elapsed time: 1.22845ms (CUDA Measured) + passed +==== work-efficient compact, non-power-of-two ==== + elapsed time: 1.25226ms (CUDA Measured) + passed + +***************************** +** RADIX SORT TESTS ** +***************************** + [ 2 6 17 6 13 14 18 13 2 16 19 9 1 ... 4 0 ] +==== radix sort, power-of-two ==== + passed +==== radix sort, non-power-of-two ==== + passed +``` From a836028bcd6c77ea2992e7257e7c963b7329bdea Mon Sep 17 00:00:00 2001 From: Jiyu Huang <47465661+JiyuHuang@users.noreply.github.com> Date: Wed, 22 Sep 2021 15:47:16 -0400 Subject: [PATCH 13/13] readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 130d21e..ff4137e 100644 --- a/README.md +++ b/README.md @@ -26,8 +26,8 @@ Thrust library's version of exclusive scan is also used as comparison. The performance of various implementations of scan are illustrated below. -[performance_chart](!/img/performance_chart.png) -[performance_chart_large](!/img/performance_chart_large.png) +![performance_chart](/img/performance_chart.png) +![performance_chart_large](/img/performance_chart_large.png) * As can be seen from the graph, starting from array size 2^15 (32768), GPU algorithms show performance advantages towards the CPU implementation, due to the advantages of parallelism.