From cc983cbcffdb5f62827802f7959898731256fdfd Mon Sep 17 00:00:00 2001 From: zach Date: Mon, 17 Sep 2018 11:12:07 -0400 Subject: [PATCH 1/7] complete --- img/compactCompare.png | Bin 0 -> 16662 bytes img/scanCompare.png | Bin 0 -> 16579 bytes src/main.cpp | 40 +++++++--- src/testing_helpers.hpp | 3 +- stream_compaction/CMakeLists.txt | 2 +- stream_compaction/common.cu | 4 +- stream_compaction/common.h | 2 + stream_compaction/cpu.cu | 62 ++++++++++++--- stream_compaction/efficient.cu | 130 +++++++++++++++++++++++++++---- stream_compaction/efficient.h | 4 +- stream_compaction/naive.cu | 58 +++++++++++--- stream_compaction/naive.h | 3 +- stream_compaction/thrust.cu | 19 +++-- 13 files changed, 266 insertions(+), 61 deletions(-) create mode 100644 img/compactCompare.png create mode 100644 img/scanCompare.png diff --git a/img/compactCompare.png b/img/compactCompare.png new file mode 100644 index 0000000000000000000000000000000000000000..f3b525250c0273d1f033605402f475f749387fb4 GIT binary patch literal 16662 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≪ zfSGO3P5X`rH!V+)_QT%nYbX>S^y1^==e=-cWMs_E%{B0Fa#B&|+v#RL|F}tc^D|Ih zR#|!MR!o~H^Pj3xpzUb1qr_92Ry*;0S?JQG=;P(x-7puZGw(5qXwEykaZm8TfVK`i zyR=uxePLZq+C|0A!jd|%&Bn&2OfTfjEtw7Bl{#rV2Ee*uRnJqHaE7WpGHfm>T!AB)*%Hhfg|X*N;b!hfQp9!E#xS7c&{KdA~b z@BmkLW|Sv22bxu78&H{^oKRx<+gMtD2gUXZHcn3PmI8E@qThjLw%o$fP~1vHGYEJ$ zLiL$jG^bT|7%LJ|sABp4eVJ7JBl=tX1|Zg^4MU!Ubh~Kicf-L zz&(LZ>Jp2uFeN(BQUeNZb8~Y9Oj^xI#g!{zp`qZ>NNVb+8GD{o?2?bFCRnco9K1LP zF3GB^)8tcE)zu}%$M=t7VPv${*Y}2b6t1L6NJ>b&Iq=!EsDmT2MgJ$>^8=mq|F1J0 dljx3=QXK^u^VPNw`1cO(Ns7vf6bb41{11b(cL@Lh literal 0 HcmV?d00001 diff --git a/img/scanCompare.png b/img/scanCompare.png new file mode 100644 index 0000000000000000000000000000000000000000..d6b771b2ceef68946cd579093820a7e50f8d4a71 GIT binary patch literal 16579 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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 << 12; // 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]; int *c = new int[SIZE]; +int *cudaDataArrayA; // device array for read/write during scan +int *cudaDataArrayB; // device array for read/write during scan + int main(int argc, char* argv[]) { // Scan tests @@ -31,6 +34,14 @@ int main(int argc, char* argv[]) { a[SIZE - 1] = 0; printArray(SIZE, a, true); + // initialize CUDA arrays + cudaMalloc((void**)&cudaDataArrayA, SIZE * sizeof(int)); + checkCUDAErrorFn("cudaMalloc tempA failed!"); + cudaMalloc((void**)&cudaDataArrayB, SIZE * sizeof(int)); + checkCUDAErrorFn("cudaMalloc tempB failed!"); + + cudaDeviceSynchronize(); + // 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. @@ -47,9 +58,10 @@ int main(int argc, char* argv[]) { printArray(NPOT, b, true); printCmpResult(NPOT, b, c); + cudaMemcpy(cudaDataArrayA, a, SIZE * sizeof(int), cudaMemcpyHostToDevice); zeroArray(SIZE, c); printDesc("naive scan, power-of-two"); - StreamCompaction::Naive::scan(SIZE, c, a); + StreamCompaction::Naive::scan(SIZE, c, a, cudaDataArrayA, cudaDataArrayB); printElapsedTime(StreamCompaction::Naive::timer().getGpuElapsedTimeForPreviousOperation(), "(CUDA Measured)"); //printArray(SIZE, c, true); printCmpResult(SIZE, b, c); @@ -60,27 +72,30 @@ int main(int argc, char* argv[]) { StreamCompaction::Naive::scan(SIZE, c, a); printArray(SIZE, c, true); */ - zeroArray(SIZE, c); + cudaMemcpy(cudaDataArrayA, a, SIZE * sizeof(int), cudaMemcpyHostToDevice); + zeroArray(SIZE, c); printDesc("naive scan, non-power-of-two"); - StreamCompaction::Naive::scan(NPOT, c, a); + StreamCompaction::Naive::scan(NPOT, c, a, cudaDataArrayA, cudaDataArrayB); printElapsedTime(StreamCompaction::Naive::timer().getGpuElapsedTimeForPreviousOperation(), "(CUDA Measured)"); //printArray(SIZE, c, true); printCmpResult(NPOT, b, c); + cudaMemcpy(cudaDataArrayA, a, SIZE * sizeof(int), cudaMemcpyHostToDevice); zeroArray(SIZE, c); printDesc("work-efficient scan, power-of-two"); - StreamCompaction::Efficient::scan(SIZE, c, a); + StreamCompaction::Efficient::scan(SIZE, SIZE, c, a, cudaDataArrayA); printElapsedTime(StreamCompaction::Efficient::timer().getGpuElapsedTimeForPreviousOperation(), "(CUDA Measured)"); //printArray(SIZE, c, true); printCmpResult(SIZE, b, c); + cudaMemcpy(cudaDataArrayA, a, SIZE * sizeof(int), cudaMemcpyHostToDevice); zeroArray(SIZE, c); printDesc("work-efficient scan, non-power-of-two"); - StreamCompaction::Efficient::scan(NPOT, c, a); + StreamCompaction::Efficient::scan(SIZE, NPOT, c, a, cudaDataArrayA); printElapsedTime(StreamCompaction::Efficient::timer().getGpuElapsedTimeForPreviousOperation(), "(CUDA Measured)"); //printArray(NPOT, c, true); printCmpResult(NPOT, b, c); - + zeroArray(SIZE, c); printDesc("thrust scan, power-of-two"); StreamCompaction::Thrust::scan(SIZE, c, a); @@ -133,16 +148,20 @@ int main(int argc, char* argv[]) { printArray(count, c, true); printCmpLenResult(count, expectedCount, b, c); + cudaMemcpy(cudaDataArrayA, a, SIZE * sizeof(int), cudaMemcpyHostToDevice); + cudaMemcpy(cudaDataArrayB, a, SIZE * sizeof(int), cudaMemcpyHostToDevice); zeroArray(SIZE, c); printDesc("work-efficient compact, power-of-two"); - count = StreamCompaction::Efficient::compact(SIZE, c, a); + count = StreamCompaction::Efficient::compact(SIZE, SIZE, c, a, cudaDataArrayA, cudaDataArrayB); printElapsedTime(StreamCompaction::Efficient::timer().getGpuElapsedTimeForPreviousOperation(), "(CUDA Measured)"); //printArray(count, c, true); printCmpLenResult(count, expectedCount, b, c); + cudaMemcpy(cudaDataArrayA, a, SIZE * sizeof(int), cudaMemcpyHostToDevice); + cudaMemcpy(cudaDataArrayB, a, SIZE * sizeof(int), cudaMemcpyHostToDevice); zeroArray(SIZE, c); printDesc("work-efficient compact, non-power-of-two"); - count = StreamCompaction::Efficient::compact(NPOT, c, a); + count = StreamCompaction::Efficient::compact(SIZE, NPOT, c, a, cudaDataArrayA, cudaDataArrayB); printElapsedTime(StreamCompaction::Efficient::timer().getGpuElapsedTimeForPreviousOperation(), "(CUDA Measured)"); //printArray(count, c, true); printCmpLenResult(count, expectedNPOT, b, c); @@ -151,4 +170,7 @@ int main(int argc, char* argv[]) { delete[] a; delete[] b; delete[] c; + + cudaFree(cudaDataArrayA); + cudaFree(cudaDataArrayB); } diff --git a/src/testing_helpers.hpp b/src/testing_helpers.hpp index 46337ab..834bbe5 100644 --- a/src/testing_helpers.hpp +++ b/src/testing_helpers.hpp @@ -50,7 +50,8 @@ void onesArray(int n, int *a) { } void genArray(int n, int *a, int maxval) { - srand(time(nullptr)); + //srand(time(nullptr)); + srand(124); for (int i = 0; i < n; i++) { a[i] = rand() % maxval; diff --git a/stream_compaction/CMakeLists.txt b/stream_compaction/CMakeLists.txt index cdbef77..c8709e7 100644 --- a/stream_compaction/CMakeLists.txt +++ b/stream_compaction/CMakeLists.txt @@ -13,5 +13,5 @@ set(SOURCE_FILES cuda_add_library(stream_compaction ${SOURCE_FILES} - OPTIONS -arch=sm_20 + OPTIONS -arch=sm_50 ) diff --git a/stream_compaction/common.cu b/stream_compaction/common.cu index 8fc0211..d601e60 100644 --- a/stream_compaction/common.cu +++ b/stream_compaction/common.cu @@ -14,7 +14,6 @@ void checkCUDAErrorFn(const char *msg, const char *file, int line) { exit(EXIT_FAILURE); } - namespace StreamCompaction { namespace Common { @@ -24,6 +23,7 @@ namespace StreamCompaction { */ __global__ void kernMapToBoolean(int n, int *bools, const int *idata) { // TODO + // written in efficient.cu } /** @@ -33,7 +33,7 @@ namespace StreamCompaction { __global__ void kernScatter(int n, int *odata, const int *idata, const int *bools, const int *indices) { // TODO + // written in efficient.cu } - } } diff --git a/stream_compaction/common.h b/stream_compaction/common.h index 99a1b04..24a3e19 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__) +#define blocksize 512 + /** * 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 05ce667..7f1ab6a 100644 --- a/stream_compaction/cpu.cu +++ b/stream_compaction/cpu.cu @@ -1,15 +1,15 @@ #include #include "cpu.h" -#include "common.h" +#include "common.h" namespace StreamCompaction { namespace CPU { - using StreamCompaction::Common::PerformanceTimer; - PerformanceTimer& timer() - { - static PerformanceTimer timer; - return timer; + using StreamCompaction::Common::PerformanceTimer; + PerformanceTimer& timer() + { + static PerformanceTimer timer; + return timer; } /** @@ -20,6 +20,10 @@ namespace StreamCompaction { void scan(int n, int *odata, const int *idata) { timer().startCpuTimer(); // TODO + odata[0] = 0; + for (int k = 1; k < n; ++k) { + odata[k] = idata[k - 1] + odata[k - 1]; + } timer().endCpuTimer(); } @@ -31,8 +35,15 @@ namespace StreamCompaction { int compactWithoutScan(int n, int *odata, const int *idata) { timer().startCpuTimer(); // TODO + int counter = 0; + for (int k = 0; k < n; ++k) { + if (idata[k] != 0) { + odata[counter] = idata[k]; + counter++; + } + } timer().endCpuTimer(); - return -1; + return counter; } /** @@ -41,10 +52,39 @@ namespace StreamCompaction { * @returns the number of elements remaining after compaction. */ int compactWithScan(int n, int *odata, const int *idata) { - timer().startCpuTimer(); - // TODO - timer().endCpuTimer(); - return -1; + + int *binaryArray = new int[n]; + int *scanResult = new int[n]; + + timer().startCpuTimer(); + // prepare temporary binary array + for (int k = 0; k < n; ++k) { + if (idata[k] != 0) { + binaryArray[k] = 1; + } + else { + binaryArray[k] = 0; + } + } + // scan + scanResult[0] = 0; + for (int k = 1; k < n; ++k) { + scanResult[k] = binaryArray[k - 1] + scanResult[k - 1]; + } + // scatter + int counter = 0; + for (int k = 0; k < n; ++k) { + if (binaryArray[k] == 1) { + odata[scanResult[k]] = idata[k]; + counter++; + } + } + timer().endCpuTimer(); + + delete binaryArray; + delete scanResult; + + return counter; } } } diff --git a/stream_compaction/efficient.cu b/stream_compaction/efficient.cu index 36c5ef2..58c6668 100644 --- a/stream_compaction/efficient.cu +++ b/stream_compaction/efficient.cu @@ -1,3 +1,5 @@ +#define GLM_FORCE_CUDA + #include #include #include "common.h" @@ -5,20 +7,58 @@ namespace StreamCompaction { namespace Efficient { - using StreamCompaction::Common::PerformanceTimer; - PerformanceTimer& timer() - { - static PerformanceTimer timer; - return timer; + using StreamCompaction::Common::PerformanceTimer; + PerformanceTimer& timer() + { + static PerformanceTimer timer; + return timer; } + __global__ void kernUpSweep(int n, int d, int* a) { + int idx = threadIdx.x + (blockIdx.x * blockDim.x); + int offset = (int)powf(2, d + 1); + if (idx % offset != 0 || idx >= n - 1) { + return; + } + a[idx + (int)powf(2, d + 1) - 1] += a[idx + (int)powf(2, d) - 1]; + } + + __global__ void kernDownSweep(int n, int d, int* a) { + int idx = threadIdx.x + (blockIdx.x * blockDim.x); + int offset = (int)powf(2, d + 1); + if (idx % offset != 0 || idx >= n - 1) { + return; + } + int t = a[idx + (int)powf(2, d) - 1]; + a[idx + (int)powf(2, d) - 1] = a[idx + (int)powf(2, d + 1) - 1]; + a[idx + (int)powf(2, d + 1) - 1] += t; + } + /** * Performs prefix-sum (aka scan) on idata, storing the result into odata. */ - void scan(int n, int *odata, const int *idata) { - timer().startGpuTimer(); - // TODO - timer().endGpuTimer(); + void scan(int n, int npot, int *odata, const int *idata, int *cudaA) { + + dim3 fullBlocksPerGrid((n + blocksize - 1) / blocksize); + + // UPSWEEP + int dmax = ilog2ceil(n) - 1; + + timer().startGpuTimer(); + + for (int d = 0; d <= dmax; ++d) { + kernUpSweep<<>>(n, d, cudaA); + } + int temp[1] = { 0 }; + cudaMemcpy(cudaA + (n - 1), temp, 1 * sizeof(int), cudaMemcpyHostToDevice); + // DOWNSWEEP + for (int d = dmax; d >= 0; d--) { + kernDownSweep<<>>(n, d, cudaA); + } + + timer().endGpuTimer(); + + cudaMemcpy(odata, cudaA, npot * sizeof(int), cudaMemcpyDeviceToHost); } /** @@ -30,11 +70,75 @@ namespace StreamCompaction { * @param idata The array of elements to compact. * @returns The number of elements remaining after compaction. */ - int compact(int n, int *odata, const int *idata) { + __global__ void kernConvertToBinary(int n, int *devBinary) { + int idx = threadIdx.x + (blockIdx.x * blockDim.x); + if (idx >= n) { + return; + } + if (devBinary[idx] != 0) { + devBinary[idx] = 1; + } + else { + devBinary[idx] = 0; + } + } + + __global__ void kernScatter(int n, int *devBinary, int *devBinaryCopy, int *devCopy, int *devResult) { + int idx = threadIdx.x + (blockIdx.x * blockDim.x); + if (idx >= n) { + return; + } + if (devBinary[idx] == 1) { + devResult[devBinaryCopy[idx]] = devCopy[idx]; + } + } + + int compact(int n, int npot, int *odata, const int *idata, int *devCopy, int *devBinary) { + + dim3 fullBlocksPerGrid((n + blocksize - 1) / blocksize); + + int *devBinaryCopy; + int *devResult; + + cudaMalloc((void**)&devBinaryCopy, n * sizeof(int)); + checkCUDAErrorFn("cudaMalloc devBinaryCopy"); + cudaMalloc((void**)&devResult, n * sizeof(int)); + checkCUDAErrorFn("cudaMalloc devResult"); + timer().startGpuTimer(); - // TODO - timer().endGpuTimer(); - return -1; + + // preparing binary array + kernConvertToBinary<<>>(n, devBinary); + cudaMemcpy(devBinaryCopy, devBinary, n * sizeof(int), cudaMemcpyDeviceToDevice); + + // running scan + // UPSWEEP + int dmax = ilog2ceil(n) - 1; + + for (int d = 0; d <= dmax; ++d) { + kernUpSweep<<>>(n, d, devBinaryCopy); + } + int temp[1] = { 0 }; + cudaMemcpy(devBinaryCopy + (n - 1), temp, 1 * sizeof(int), cudaMemcpyHostToDevice); + // DOWNSWEEP + for (int d = dmax; d >= 0; d--) { + kernDownSweep<<>>(n, d, devBinaryCopy); + } + + // populating compact return array + kernScatter<<>>(n, devBinary, devBinaryCopy, devCopy, devResult); + + timer().endGpuTimer(); + + int tempResult[1] = { 0 }; + int offset = npot == n ? n - 1 : npot; + cudaMemcpy(&tempResult, devBinaryCopy + offset, 1 * sizeof(int), cudaMemcpyDeviceToHost); + cudaMemcpy(odata, devResult, npot * sizeof(int), cudaMemcpyDeviceToHost); + + cudaFree(devBinaryCopy); + cudaFree(devResult); + + return tempResult[0]; } } } diff --git a/stream_compaction/efficient.h b/stream_compaction/efficient.h index 803cb4f..fc5a4a6 100644 --- a/stream_compaction/efficient.h +++ b/stream_compaction/efficient.h @@ -6,8 +6,8 @@ namespace StreamCompaction { namespace Efficient { StreamCompaction::Common::PerformanceTimer& timer(); - void scan(int n, int *odata, const int *idata); + void scan(int n, int npot, int *odata, const int *idata, int *cudaA); - int compact(int n, int *odata, const int *idata); + int compact(int n, int npot, int *odata, const int *idata, int *devCopy, int *devBinary); } } diff --git a/stream_compaction/naive.cu b/stream_compaction/naive.cu index 9218f8e..b78ef5b 100644 --- a/stream_compaction/naive.cu +++ b/stream_compaction/naive.cu @@ -1,3 +1,5 @@ +#define GLM_FORCE_CUDA + #include #include #include "common.h" @@ -5,21 +7,53 @@ namespace StreamCompaction { namespace Naive { - using StreamCompaction::Common::PerformanceTimer; - PerformanceTimer& timer() - { - static PerformanceTimer timer; - return timer; + using StreamCompaction::Common::PerformanceTimer; + PerformanceTimer& timer() + { + static PerformanceTimer timer; + return timer; } - // TODO: __global__ - + // TODO: /** * Performs prefix-sum (aka scan) on idata, storing the result into odata. - */ - void scan(int n, int *odata, const int *idata) { - timer().startGpuTimer(); - // TODO - timer().endGpuTimer(); + */ + + __global__ void kernAdvanceScan(int n, int offset, int* a, int* b) { + int idx = threadIdx.x + (blockIdx.x * blockDim.x); + if (idx >= n) { + return; + } + + if (idx >= offset) { + b[idx] = a[idx - offset] + a[idx]; + } + else { + b[idx] = a[idx]; + } + } + + void scan(int n, int *odata, const int *idata, int *cudaA, int *cudaB) { + + dim3 fullBlocksPerGrid((n + blocksize - 1) / blocksize); + + int kmax = ilog2ceil(n); + + timer().startGpuTimer(); + + for (int k = 1; k <= kmax; ++k) { + // invoke kernel + int offset = (int)pow(2, k - 1); + kernAdvanceScan<<>>(n - 1, offset, cudaA, cudaB); + // pointer swap + int *temp = cudaA; + cudaA = cudaB; + cudaB = temp; + } + + timer().endGpuTimer(); + + cudaMemcpy(odata + 1, cudaA, (n - 1) * sizeof(int), cudaMemcpyDeviceToHost); + odata[0] = 0; } } } diff --git a/stream_compaction/naive.h b/stream_compaction/naive.h index 37dcb06..53b75cc 100644 --- a/stream_compaction/naive.h +++ b/stream_compaction/naive.h @@ -5,7 +5,6 @@ namespace StreamCompaction { namespace Naive { StreamCompaction::Common::PerformanceTimer& timer(); - - void scan(int n, int *odata, const int *idata); + void scan(int n, int *odata, const int *idata, int *cudaA, int *cudaB); } } diff --git a/stream_compaction/thrust.cu b/stream_compaction/thrust.cu index 36b732d..3b9a7e2 100644 --- a/stream_compaction/thrust.cu +++ b/stream_compaction/thrust.cu @@ -8,21 +8,24 @@ namespace StreamCompaction { namespace Thrust { - using StreamCompaction::Common::PerformanceTimer; - PerformanceTimer& timer() - { - static PerformanceTimer timer; - return timer; + using StreamCompaction::Common::PerformanceTimer; + PerformanceTimer& timer() + { + static PerformanceTimer timer; + return timer; } /** * Performs prefix-sum (aka scan) on idata, storing the result into odata. */ void scan(int n, int *odata, const int *idata) { - timer().startGpuTimer(); // TODO use `thrust::exclusive_scan` // example: for device_vectors dv_in and dv_out: - // thrust::exclusive_scan(dv_in.begin(), dv_in.end(), dv_out.begin()); - timer().endGpuTimer(); + thrust::device_vector dv_in(idata, idata + n); + thrust::device_vector dv_out = thrust::device_vector(n, 0); + timer().startGpuTimer(); + thrust::exclusive_scan(dv_in.begin(), dv_in.end(), dv_out.begin()); + timer().endGpuTimer(); + thrust::copy(dv_out.begin(), dv_out.end(), odata); } } } From a85a0dd45998df8b9f2ae371ea14facc19a036ee Mon Sep 17 00:00:00 2001 From: zach Date: Mon, 17 Sep 2018 11:28:29 -0400 Subject: [PATCH 2/7] fixed figure --- img/compactCompare.png | Bin 16662 -> 15818 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/img/compactCompare.png b/img/compactCompare.png index f3b525250c0273d1f033605402f475f749387fb4..b1b372346da5e2a4443fd8e3d768f008407556f7 100644 GIT binary patch literal 15818 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 literal 16662 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≪ zfSGO3P5X`rH!V+)_QT%nYbX>S^y1^==e=-cWMs_E%{B0Fa#B&|+v#RL|F}tc^D|Ih zR#|!MR!o~H^Pj3xpzUb1qr_92Ry*;0S?JQG=;P(x-7puZGw(5qXwEykaZm8TfVK`i zyR=uxePLZq+C|0A!jd|%&Bn&2OfTfjEtw7Bl{#rV2Ee*uRnJqHaE7WpGHfm>T!AB)*%Hhfg|X*N;b!hfQp9!E#xS7c&{KdA~b z@BmkLW|Sv22bxu78&H{^oKRx<+gMtD2gUXZHcn3PmI8E@qThjLw%o$fP~1vHGYEJ$ zLiL$jG^bT|7%LJ|sABp4eVJ7JBl=tX1|Zg^4MU!Ubh~Kicf-L zz&(LZ>Jp2uFeN(BQUeNZb8~Y9Oj^xI#g!{zp`qZ>NNVb+8GD{o?2?bFCRnco9K1LP zF3GB^)8tcE)zu}%$M=t7VPv${*Y}2b6t1L6NJ>b&Iq=!EsDmT2MgJ$>^8=mq|F1J0 dljx3=QXK^u^VPNw`1cO(Ns7vf6bb41{11b(cL@Lh From 11537ce9f9cf19951690467f39527eb001c44d33 Mon Sep 17 00:00:00 2001 From: Zach Corse Date: Mon, 17 Sep 2018 11:29:12 -0400 Subject: [PATCH 3/7] Update README.md --- README.md | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 0e38ddb..fb76336 100644 --- a/README.md +++ b/README.md @@ -3,12 +3,29 @@ 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) +* Zach Corse + * LinkedIn: https://www.linkedin.com/in/wzcorse/ + * Personal Website: https://wzcorse.com + * Twitter: @ZachCorse +* Tested on: Windows 10, i7-6700HQ @ 2.60GHz 32GB, NVIDIA GeForce GTX 970M (personal computer) -### (TODO: Your README) +## README -Include analysis, etc. (Remember, this is public, so don't put -anything here that you don't want to share with the world.) +Introduction +------------ +In this project I take a stab at implementing a scan algorithm on the GPU and compare the performance with a straightforward implementation on the CPU. For those not familiar with scan, this algorithm accepts an array idata of numbers then returns an array odata such that each element in odata is the sum of all elements in idata that precede the index in consideration (specifically, this is known as an exclusive scan, because the value stored at the index in consideration in idata is not included in the sum). + +I also include a parallel implementation of the compact algorithm, which uses scan as part of its implementation. This algorithm accepts an array of data, converts this array into an array of booleans according to a predefined rule (in this case, is the value zero or not), then "removes" the values that are false, thereby compacting the original array into one which preserves the array's remaining useful information. + +Scan Performance Analysis +------------ + + + +![graph1](img/scanCompare.png) + +Compact Performance Analysis +------------ + +![graph1](img/compactCompare.png) From bdf940e42768364b325302778e5a3939cc24614b Mon Sep 17 00:00:00 2001 From: Zach Corse Date: Mon, 17 Sep 2018 11:31:11 -0400 Subject: [PATCH 4/7] Delete compactCompare.png --- img/compactCompare.png | Bin 15818 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 img/compactCompare.png diff --git a/img/compactCompare.png b/img/compactCompare.png deleted file mode 100644 index b1b372346da5e2a4443fd8e3d768f008407556f7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15818 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 From fd1c34e7709ea053e7029ab9704681f0466ec6f2 Mon Sep 17 00:00:00 2001 From: zach Date: Mon, 17 Sep 2018 11:31:56 -0400 Subject: [PATCH 5/7] fixed figure --- img/compactCompare.png | Bin 0 -> 15818 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 img/compactCompare.png diff --git a/img/compactCompare.png b/img/compactCompare.png new file mode 100644 index 0000000000000000000000000000000000000000..b1b372346da5e2a4443fd8e3d768f008407556f7 GIT binary patch literal 15818 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 literal 0 HcmV?d00001 From 7ca26ccd5444bc431eaee492829075a61fb97d77 Mon Sep 17 00:00:00 2001 From: Zach Corse Date: Mon, 17 Sep 2018 12:20:15 -0400 Subject: [PATCH 6/7] Update README.md --- README.md | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/README.md b/README.md index fb76336..b726cae 100644 --- a/README.md +++ b/README.md @@ -18,14 +18,89 @@ In this project I take a stab at implementing a scan algorithm on the GPU and co I also include a parallel implementation of the compact algorithm, which uses scan as part of its implementation. This algorithm accepts an array of data, converts this array into an array of booleans according to a predefined rule (in this case, is the value zero or not), then "removes" the values that are false, thereby compacting the original array into one which preserves the array's remaining useful information. +As shown here, my implementations pass the tests designed to check accuracy and measure timing for each algorithm. Speed comparisons are discussed in detail below. + +``` +**************** +** SCAN TESTS ** +**************** + [ 43 1 21 0 10 41 43 1 21 16 31 44 19 ... 6 0 ] +==== cpu scan, power-of-two ==== + elapsed time: 0.00079ms (std::chrono Measured) + [ 0 43 44 65 65 75 116 159 160 181 197 228 272 ... 6155 6161 ] +==== cpu scan, non-power-of-two ==== + elapsed time: 0.000395ms (std::chrono Measured) + [ 0 43 44 65 65 75 116 159 160 181 197 228 272 ... 6077 6104 ] + passed +==== naive scan, power-of-two ==== + elapsed time: 0.04864ms (CUDA Measured) + passed +==== naive scan, non-power-of-two ==== + elapsed time: 0.047904ms (CUDA Measured) + passed +==== work-efficient scan, power-of-two ==== + elapsed time: 0.20016ms (CUDA Measured) + passed +==== work-efficient scan, non-power-of-two ==== + elapsed time: 0.183488ms (CUDA Measured) + passed +==== thrust scan, power-of-two ==== + elapsed time: 0.022336ms (CUDA Measured) + passed +==== thrust scan, non-power-of-two ==== + elapsed time: 0.02464ms (CUDA Measured) + passed + +***************************** +** STREAM COMPACTION TESTS ** +***************************** + [ 3 1 3 0 2 3 1 3 3 0 1 2 3 ... 2 0 ] +==== cpu compact without scan, power-of-two ==== + elapsed time: 0.001185ms (std::chrono Measured) + [ 3 1 3 2 3 1 3 3 1 2 3 1 1 ... 2 2 ] + passed +==== cpu compact without scan, non-power-of-two ==== + elapsed time: 0.001185ms (std::chrono Measured) + [ 3 1 3 2 3 1 3 3 1 2 3 1 1 ... 1 3 ] + passed +==== cpu compact with scan ==== + elapsed time: 0.00158ms (std::chrono Measured) + [ 3 1 3 2 3 1 3 3 1 2 3 1 1 ... 2 2 ] + passed +==== work-efficient compact, power-of-two ==== + elapsed time: 0.237024ms (CUDA Measured) + passed +==== work-efficient compact, non-power-of-two ==== + elapsed time: 0.276832ms (CUDA Measured) + passed +``` + Scan Performance Analysis ------------ +Here I compare four different implementations of the scan algorithm. The first is on the CPU, and is straightforward: + +``` +void scan(int n, int *odata, const int *idata) { +timer().startCpuTimer(); +odata[0] = 0; +for (int k = 1; k < n; ++k) { + odata[k] = idata[k - 1] + odata[k - 1]; + } +timer().endCpuTimer(); +} +``` + +On the GPU, I compare three scan implemenations. The first is "naive." In parallel, it loops over an array log(n) times, keeping track of an offset for each loop that scales as 2^(d-1) where d is the loop index. The "work-efficient" scan uses an algorithm developed in 1978 that first applies an "upsweep" to the array (which is equivalent to parallel reduction) then follows with a downsweep, which cleverly computes the scanned array. In theory, this should be faster than the naive implementation. Finally, I call the thrust library's scan algorithm as a gold standard to which I can compare my implementation. ![graph1](img/scanCompare.png) +The results are counterintuitive but informative. The CPU implementation is actually faster for sufficiently small arrays. Thrust outperforms the CPU for sufficiently large arrays, however. My naive implementation is slightly less efficient than thrust, but scales similarly. My supposedly "work efficient" implementation, however is much less efficient than all three. I can attribute this to several factors: first and foremost, my implemenation does not use blocking and shared memory. Instead, each thread must access and write values stored in global memory, which is far slower than reading and writing to shared memory per block. Additionally, for each loop d in upsweep and downsweep, I launch a thread for each index in the array. This is highly inefficient, as the greater d is, the fewer the indices that actually affect up/downsweep, which both sum and modify indices in intervals of 2^(d+1) (a binary tree structure). These indices are also distributed across multiple blocks, further indicating that executing scan recursively per block would lead to a significant speedup. + Compact Performance Analysis ------------ +As shown below, my CPU compact implementation outpeforms my parallel compact implementation which relies on my "work-efficient" scan implementation (and is therefore consistently slower). My CPU implementation that uses my CPU scan implementation rather than a more straightforward naive approach (CPU - no scan) is in fact slower, which is most likely due to the fact that scan requires that I generate two additional heap-based arrays to manage intermediate boolean and scanned boolean arrays. + ![graph1](img/compactCompare.png) From a2adce68c61beeccdace461e1837675a0c4ba152 Mon Sep 17 00:00:00 2001 From: zach Date: Mon, 17 Sep 2018 14:12:53 -0400 Subject: [PATCH 7/7] updated figures --- img/compactCompare.png | Bin 15818 -> 13090 bytes img/scanCompare.png | Bin 16579 -> 19582 bytes src/main.cpp | 2 +- src/testing_helpers.hpp | 3 +-- stream_compaction/cpu.cu | 1 - stream_compaction/efficient.cu | 34 +++++++++++++++++++-------------- 6 files changed, 22 insertions(+), 18 deletions(-) diff --git a/img/compactCompare.png b/img/compactCompare.png index b1b372346da5e2a4443fd8e3d768f008407556f7..8dabd7415a278358985ae9bcdd2b2d18fb505d5e 100644 GIT binary patch literal 13090 zcmch7by$?$*Y2Pw2oe@3tq4di)v{`&dS~RA z0hjaY<08dJIu_w&g2TTM!zkMUi^&ngmI3OG>lZR^$#D>JUY8@>Y4&+~AyumzBm9*- z2w!emn!ymC5P#a{_QW2%w`6H8@qO$y?#8RzSanK)ogIaJ5$w+PN_KY5!VP)|guFpa z;u-`(DEWW=lWOYd=$OQOnNX6UyHNZ4^jgKF;bzls6w09Z^>aPF(XslqNOr9Ogb_Uj zg*oms7o&2bP^ONSrsmO>zp?-id5Xm1+s7gF;zcNi<4+M05kZvPN=iz)H}sKkuhteT z-{Eh@jh}9?G&eWvmygcRn@8^t-VUCug0Vn@(AWAUwDt7z)7UvUN<$N3Vm^k17*;uC zdeJ_2cHZMKUA6MIxcoQGIHxzro7NFFj#B%OtvyW@eL^VbASbw*=y4+{`5` z5x1lA^4R!LMFuteifU@%*bizUSC4ORnwj1uydt@wZZP4tK&wH!)~CaW{%ZdEwdL$^ ziNzZA&`)22H3a&#j*bqMk*@BGc3~E6T0AgCM=wa>^MzrjljeHQNlN=cQW9No#fZ<+qi;n; zTqOiT2u_5XUKz*8_u$}Q0fMK?%gg%ZfqWL7mRh15930HdG8q53iAE1+XXp9Z*%%jj zC8c`DrLSI;s_}i_Of!Lt)*()B;U>HrXa2sdAo_)Cv$R&p3}dsh?XCPC28ej zek~KMFy<(9`~oLlHi*~7E-h+>*?O>MUh>_Q(+!)yv@wZ9WcHTmzJeNa8PykPSt&?L z`n*8HCBGaJj*yo^vgR$jlS}-nF&0k-dA2 zj2hcJsXLwSHgI^oo3p24ew5fhcss4HYL~?yZAB6Eohsx1+lYDYu&}vJkYJ zcteY~U0wRKUg$#JjycYsjeq-^dTmovlU{|bMtauzxTA!&fkEM?Lc776N^!2Z6Rfnf zw7W%qrrP@oRn4JekM(*b`CBy9!Tw zMMp&tn$e$jjmwfVcw%E?*&dXr@&|>5T{F^rAuauZLMoGrk}{j1&&v=f6oSdlM}-Ly zniZKtl;Rz!5U1+B`^MNf)klS~EXrV>j+VC2q$i)BnDbj!#Unw4K%Z3Dk@kg{5(agr zmrGF7q~W{KKz^&*ybyXH71&EOk7;STjre8DJ?pY$q&bAwu4<)eR$4om+Piai-Co zV~!Fz&M!2eeOZe0lUrL3eKbL$SNmnv87-Y6>NH=LB#f5Zz&4z7S_d$TtfB*$j;-#T zID`H4lyydfV$`;^Q>?a4#fb(>N1S%3fK2#deN-LY?Hzm5zq(t@gc@N5R}><Coo8;^db?FH2N&0X5xcZV0jnC1s+t-Ze5?#QXnw=bP%GcdB=S>U zOT;b=fI3(ky>It+^R#?x4MFOYp4QB^DusDtFCENBI_oU%)8!x;`s?EdP`(6Yo>p#! zB&*%2QY|60eFJmF8+^V|TB4tBJmNB9#uN|l?C-Dq1=yr-n2A!IDN9g;-zzf#+N9V<;Mow@{swg7q^je;H zU6q8zHMfSdP*YLuq}0}qP1HI&I$CB9A!If)1KlmP(I$C38djs+T`>-dsJ_&fh)Ir=#;sFxcs;VZ5a&uMY?wqPV-3W;s=8n0WkJ~5eJkUdKV#20v zcA?8FD+bNb&&^Q7k|+E^(*A zn~BPP+rhsR@QqapPK`{be#%kwa+gGorUGK5-m6CQJ)L%d2I??yIf7lQpHSE6 z-Jzq3WY=)g`-L)I>eC6u&=C#t8+ZXHKaMVW}(_Bu~xxZbP zvJkgaCU->=c{z31VGQBSyU<`I-oH*I4qZk@W~56eCLy7}A>J1=(iHURlf8HcFN?(OgEN)dv&&J{n7czo zwIxcZ7}j}lc}kxz(hTaze2)N*Lj6m)nQ{ol*}Tyxv9x2VEp9LP-miWp)ZD6e3gYcCe*Go zM$ke;k07@c6{AC2);nQO*x@!X1UfannmeI8xqSYJD| z9cpuZA1`64eHfj9K>(>|D;({I&%$QOjH2R|%Xl~zD z`M#o2S64^p(~W(72Xn~FM=aTuk2W*O3=_+0e(FRX;iEzyb!BH?rvDP zeq&`OUo(a*-~DT3q?veMOLSz~7y0MUUmCMnnfE!novwNc6)z|dYaKFBiAgjQ83ec< z7|QhS39;mX+UwUD?FR99_|dE``cKIlSmrx0!!)t@Psx24uA1DsbqkP$fls?2ydd|3 zoL7NU}9?8*w`2z9?lI&z{aZkiKRIO z73|Lsylv6}*^3Z-FY*y@y)kKL=W>(Qi8aoQ>s!(X@s~peu2!ZdsoQh%Z^;g4H;EnZHf+)i##fjIPou zMl-0-FHHhM7vn9E+1gNFFJ>eb7yBV?yjpE;WPt35@mzaK(pEmeUtEF zh_3BuZS8@g!W8!DE8f`H2p}1(y+1AW>Ugp4QxCFvD7+vcB~_}r&&;|zctXmByuN2b zv>V^}#f!j^9Re~s*O)9M(x~svaB-7a?N^zZP+H+f6{k))E$jt{sI9H7=1Ji>7Z^ks zA)QtE>-}YHC_q zH2=yV!X@?(@YYlaB#he6Jgj5i2b}Z3Oh^dn$}k8)x)=yayvU_<4bu!?NlG>>-Y^uW zk|2ZxJt0J+RO6E(Bjdn?5A__%O4AlnkBJ0P z4^14!&70bNV>K>a*n%Dqi$1wvrLL)It>ErneJBW`9b9>7srJSlN=gf4?_@Mxs^N8z zA3%;6FLWjj41W2aqKdmZe0O{Err=3qNoA$>T76Yh9F_Y@9!&J%e%x@WRk#BbMB0H- zIXMqj^xFW2ZOygso_i7Avf%e)y6Pw?qyBQTgKH;;A3v z0>Cv-L=q1B=Y1lCX0Kj7y%zK3ONv6^$B!y<5z)~#V~%=WvX?r*Ht`4hsQrC7muLop zCJ>MHziy+OO8{Vb)3mj)GO{W` zczG)Qnjhrbf$!T?g*&DoE#vpUJ{%+eg3SL8q5u2eU?OrMIuX~0zS&R7P3)=Y>BZmv z`Oy~1o}QKl!pDix(fqI*H*UzEC_a1k3(!>|AtC+~03cyuVFS>bni>H-GdZ`TX+U!? zUAi=H4})z7-eLzimky*I5)0s=XY&~;~DU*A4afkc-Hx9qGe|2SpxdmrZ(7Lb_r8f#`6QrYLvpF21dqYHL+cbC5R zEs41AEXD~y2_@TLk0K}lwm%GQZ)*ecX=-U@w*UlnFPM(=B9FW7tV4_0-_VK$K5~S< zYii*hNSAQbuJWZD1}qs0^YfQ_9&UkjM!vxF7cY8|T&TbiQf&Swi|)58OjoX40T%f= zBxK;k)x)Fa&SZaohB=RjhzKKNqhtIkh|+FSQ|G(Q&ClnBX7ViaEY8Kj0j7Y?^eHoCVdab@zL%| zcK$8hH(o>IH7@ysmX}hC)sj1R4ndZ~(9AucjSXY!geh=vW);GyEtgyWRPS^zr0{6F z&`*pSO?qtaQ~r_Rm(QW@SjwPr_246Z{+!6awcdryPpJ<<&~7o{+au490udDOam2*L zE~`VvRJ>)Uc&J~iJUMZPQ^d+p(=Nr=R591w`h)3Q6}#81CKMWp`(-gp|!JfU^R>V zskGCUS$W~ITm)YhtkHY5GLy}UCZR)t($iMui&yaA~0Tie8&&|;8a?j%f@i{35b@U zP$*hiBgraTf+t^$?qAN**eZKf1RqU59-vHjAYyo)nq1uxr76Z2QhZOzgheUg;Ut|^EdTlL3TVwOf#eTH&rM$L{=qhoC!rx=&>*RZdU*3S1&IHzN8}v2UfgFD#2$<5?;Y zO5(FSaRb*$edo@dTemXDVHhVnwMS#th9$+t{B=|x8X6>5;`6eJB(1EhUS9V5@F6yc zMBmWq_`qI3YHDg~85N_$&(FVD7H3!(NyRD%DwA_4YBpIl%S^VaO}{%OI;vwlP*^N;fz#kd=|KC?PK| z@99~4XBdqJtY5n7B80+%T-V><-+6V&9srPNQEu*YMMZ%bw3>eL>(<4^2hwB^?NW)D zu&~rpy%KZO=H|DP!n`~X$nzq3l&;u+1^2cSC#vTHu!x;|mpB`guto5|>@YzYBTw z^8qfxr%jUS9$;5B!%8Sm<{<{*jd?}G_{Oj7I-xdZC=?ZO<}V_C;C&K~MpE`kP0qdonbLaC7cHxDU3G&b~OR`=0^vp@i!PK`+2e^D$LE z^;1n27q`#Edl(94@F0fyn!E?H{Lm*lJ`?JP=l7x%`p2+)+EKAUj*i%r$}@pFf?iem zz$bh+N6Mhkfr2Y!WXj6Q8%s+(jDWv~fBkyz-aTGkUOG|tXHIzUnpm`pQj#Yb-q_iN z0?V|ubw0C9K(Nri0W%*O8Uma`?dU`tylBZM*PluZjgC^&(|ZG^w2klxRk8m~dK1DyD&cYoTT1|}S?U5**yzS~Bxop1j8KmYxUri#&- zewZ^Y!hKIQrX%we^8DtpUz=~g{WDs`!ooOou0VYJ6Mc1rMQ6&ySNiy$FFdN=H78Zv zTt4TguTaYA1+&*bn>uNvJ2EI}{!Cjo9qEiDa7PuCs52Azvm_#ghqCcsgYyR( z0<^ku2W3r1c&92=Dpu`tBExiQrBCP$C6Lxl_f9^a+K)B(g&U)!96p=t2g~W8RpazY z%;_Le(81Xo|1`;JbuVn_?$Aep)m0uX61iuGpKeN2XiesZbUZNQ3n}m7~AOM2N@G|KUL5B``rrgOBGIGVE0% zz&gFj)Am4!8ag{Wi;IhAXJ@Oc zg`nr&RrT4kXEHL)pe*^o1rno2bCV>6K|w)*gOgKHPOcKR5ORm_zj)WJ?QNHh3B{_5 zKWL^?EMHwp{SKTDJE5Mg?)1zIY6fsI5X-jFLgtAGqF`*)s8Q{P5MtnKWOL=e{(gJi1+Opv%Hx#s-ZmA`2!A^2*i zb}2>(d8@*&?(TQ)Js*V%3k#>Grvtb6C&iNR($ueC&pbUn-Q21gr+&A$x1&&*V|cHk z9;@-k_^1Hf;BP)RI3V~NFc0A=rg}eq0D$u8;&jS`Q!NPcO#`~($4?q~9fYIR{~Ej- zd_KtauR*z)e?9rnVDG=4{NGQjc-V5TC}CeWPV#QO_zB$S@AE6|uF5X$dUg*kQ5znx zvC08KE$t_u*6>|3*faG|R`OGo{5T_H2An7Br48)jtzF{ti@$%g_8u)D3vRv%HEbK^ zIbBm!K-H7Lr?AXlvj>kaNrI_zR92ms5CSY8_vSa!o&x7Vc=twQqYXRUT8W(YG0f)z zaGMN%pZ6W(sayE#k85XSn9iOfw4W$MVhSlEd{Bsbk&yw(_otz|q(6HbkTOs8+gzbw zGsgWNInt%1=LwkBTcqFmoNaI>3;Vb}b5CNdjMdpw{f8@ZA0I_O&ry>*pN9GJQ6a{X z%%JL@-!j4@D6dj#BRD+ckQ{@v(-q-#=SfU>!mHqnP&lbn9*ZYprWN@+hR-3; z*u}$B;vV2e?{w}w)&ZMu1J2CsdJ^k`(qIVZ=#0u%ETKBZdey@`ef>={Mnb$-%wQF~ zEFZ;&O5eBN7qMJVlH-P*7;FU?{6Ojp->$Kn9T|tP@qmRSg_t1tSUVFKMzkW7-z&5} z+56d(_dR*`vBF6#%$TT67UR!+(dH#UI#;68vzMRb-|wLl@s7J-?*i(LjZO!NL%A>UH`FNm(_(fqyqx2K97<9C(@CYlpyjh>CXOAqUq^D`4ihFFy zcjz+LpV^@wu3f`D&9h;F-ZWx|hajZa_B}bp;in`9RhH}N<68sNfCJwAxN}tr?90)M zBUytVW*#&w`7H^i^vSjjJUuRErB*X?bL@@T-d#zap#f6J*~(3p&gF_`T^FStZUvTj zun=xKS~lM3=Z^QE!F(1Eei2ZvG&63CSCdcEtNIsreV*8eFBS5-5n2>F-tIWz2|MVZ zyi5k)GHrF)3}gfo-ittoEqFoq+ChH6+fYI-`C@| zj8kEcHC~u~ANbvDn6q7VKg&WUat%HO|BI+%p8Vb=)P{54QR;BDXwBG`Q1LeOA%7|- zGaeppByyF6TP&YdYF2Dy<_3MK)2_OPk+64tO6hCVU(v}N_G6pDS>YqAmQ0Jpvi26? zJ!K(XioO;=hw)*=6Mdqp-X4o>($WUXN-|noBYTUPoZ@g)OAZw9Xb@O@+g{TckF~1% zPz9_yfE^L+T&gUT7*zHP=mmepqp=(j=fjS9;(#dq3EV#VgLij5j;}~dAi^^Z*9~g+ z;#J>LIbBS?*k8krP@G;&12uW@P4Oa)=X&PncUSyRY+)3+a1VsYx&O?#tJ^L2>ks zw{Tk#@3T|O76q+?>Rqwj`|Qev1$9w<6OQmV2k-}jLO$d_e3~aRVReT!H%<59JAqYB zLD2rX7A`8B6m7WI{ zT3xkj+b5}e<;#%T&!G(XPglJ-_C_?1cYbD(co7hR5JYm{+&tX$#>FyGRKSL2#+VQm zQ4w;iIP)I&ck@SfR(ox8g9RHA(m8bzO)RTrSoJMGYyQ|bi7Ssrg7DQ{03QI~hxB4b zBWue6%nH;Vypd~~$VfMHI4JV}yB;1IKwxIdIZ984T1s~@Im%C`*Q`a`PbH6ZVb$9I zA}S)WOXG~*Bk)CJ1-)z5+7CV~Q*r{=rce;Ps`}49H-+bnuKz;wI@MLFeeZ}5P;vR<6hRERH!knD9k7+=Tr^aQ2G0tEbp6!P`Dl27G zjiv z6h7zNI+I$x$UeA_Z5nRah@6LSXs^2nthT!~Jqr=Yu#kyewZBvFz~N4PNzt<2N}hp# z^uQzSir_u7gQMzHJ3AV8Mwn~$n|xcZ1RF1@*VGb=AmMkKR&pCVODhR1l&EY!lWHVa zX?)s>Ai3%7oU+KrC55ZU4H+FQkpAKy%pPo0z2ymmadi9K{{LPo31P) z?=M?=^rWpwt7?-H8X>Kdg^-VvJNA|ISOt%)g_Shwe^rr zDL2!8j{j~=$gkIQhfVYBc5e#4t1f&Huu+Z_QX_4<3w33}+A|CX#Q3yKy(alYARD2& zXN*I)q?(a1*R4vK4$j;!@h^hk3uZ{57Z4E0&CQ($#f|d}7)|m_f60c{#S#He^_>lO zA~93{01p6W-po?U6BfcQ@bz4O%y6<@>wyn< zqvR}_B5BVREEBiuZCfmod-AX++7`DcUncHIYwL*{)|))q8n=TRH%m26p1dEVjytfZ z+!^$Sl4MREocu(3G+xEvrcenRGCGY%-p@ehPxGB1cEW!J9w4LF&JQXKCyhFl zJI8|;Gi*$>@f* z;nEij`!HSsX;e6^1s~nbuf6mldNojdEr2JhpyRr7n!XF2BeTQkcY$#tQMf?6b9nn zBjnfko1cS%@{5WzzhjU;3QJYO3mf*k3b4bKRbAuaYoig=TRz8fQf~=T2L?ciwy-W6 zG-(~#41n_YWU*P0TfsAd^xADCL1o96Ere|gK?_$-tMbC3)=9^YRszWA zbwfcSNKIAs`0!^i=y(9#eDdSzXR;K|AAp%p5VP}>ZlcS7>U;(?+O_7M~(XuDU1 z$@YqOklKUAm*4hL!H|NwX8Ev{s(=RNgk6bdsNuNt_5o7ysE;|+C{Iwt-+V7*^86NAwr-)D~19sp@1aXe7mb4@+r_Aqc05V_a7W9KrMaV8Y&MsA-X_W z0+cr4MOG(vV_BnL7RtO}lmB@csB?n`{nJtbn**p^dM9z1+N5a}>4OaG%lJwLiNyu# zI089xYcd1j-zQJh2F(WY-Vh=8_MFQUnV6WiT^jNRr+2+WRmOe5*;(n;cperDv7d+H@-8x!g@Rb$w+eZ`0`vE`<)_R&=*}?8Cj5b;;Rx@REsMkRTqT1xfw`h0xQ}`?tU$ z^drbY8sF)7BWa{*!j{(Pb@+|kSQ_a^+)zW0^~?OHU$?eS7H@BS{!Cr(-`?s(JfcNJ z-dWdCK!ZbQ3212%P&h8$|FJ`QJI>_6VGA=j6CCc9dVP6DE$Cp<@0wLw%E`p^&f0q5 zTS24enXR$0v7KG%AqgeJk6QbC$+lKjM{a^Evr7F~RVo=>vJ< zubb=3H0!fOv(rd0#AUqc^Uh3t5rw~ac(b%cxvu~2&L##_&@lS=EGF}tu#s(hy5I9A{q7d z+FDX{pX{ah%E`C!GsL^>XxQ1Yj+oATs)9s|@8WO}1xLbk$q!`ipZQ~tnVXw)={?0% z|JIU|o7+jb>B#QhX{l2Fvx|>~MNZ_-*6yxzEsO1sh9I4jpFe;0^vKMU8+-F_rz9oy z4Gt1o$;-)ET3f&V0bh2gciAPg zSEay`>>phkd1G5pRXPvxbzi(lQCkV*iD7`Ap57tBmzL2i>6MigH8r)hAlLmLoz_)V zRa9mwW$Dvp(TqyQ+nE%u(51FEG;I8k5~BJ#Vk2>J|MsKro@aY?DagbUgXVC1!Eh%Z z?NoCqics+vNytPn)g=S>_LMMS7Hc;xF64EZlM3!vAs37c45b?9gu-n51F6EUr35)0 zot?CAVhQ~^Xn(?6u#qYL6=DJco9;tS(O;-??i?gRI=6QA_FQh585tkusy8@l@1pS} zD)fn*=}IDtXu5T4?Un`-d1^IgYu+zrNk^;}DBc(Ej-W2d%WJthC#-X<(%CvVu-El8 zGBWCM7hozR?I$884lyWFw~~{?$niSpBtCyrcrlV8zU)ofZ`?XEqRbWop%E4)Zw52d zuhcR*uTzzdoi5iHRss!Tz`on-x%E9;Emtu^S=_BA39`S~9Fe$IV-<;S*5r0ztffUd z$rkS<16^zX{;VQzT8W?|0eRor($WD+dy3!H$$-e4q=m=KGR<_hPEJj|r3m`zkXbP| zpROL5XkS{Z)D|-^pg{mN~uzu-`V4xpq`t^d5n0RqaI-@z(ynG+*07Nirf;N5*`ZN1ad^3N4(b%DsQXJ_iWx$U{C?^jqm*adGS?RTI9MGWv5 zwH|Fo#T4kRB`=u@#l7suP`XTL%)K>~O@_yEm4pK+naazkt`PQx=Kc{&7A#MIar zo>&(_Dg5!9NT57?K3YhmQ2EC_7N53V9dq+sCP*X$(k;fqub(iv#kO|-x&7UrC4rs= zr(;VM`87?1aJL>2q12&vSHzu8d^2c(KsjkR@d{E5Di_>Cl6z0G$V+-zmdN>sQZ@uP z#PCab`SSQx%0Zpbg_ILI0Ro43a8i(pGTK@-PTYeLtO?qzkz2<>My z9CF95Fv4)C=y}fMw7jxH8CUPTJ!!c=;OXi4skg1I4f@c@*;#N^vT*QP`~u);R1_XP zC5EW`aG9Xe=_HOK3Ld1?nOvr4-1j7r0x!nPBtCz10yvI4X4C|5_TnI);*NWiWa^n( zn%ma*Cs(oT%j*h3p`W6n9M+O~`T5^VAgt2V*9K(pO>p8bjsSM2rJLeNZr&Il(66OR zGO-8=ng|BY%%^Z%P>sGCI$o8_fbc6JYF*pw5)%^-zO2S_lon`|=znv;(ui+w92Sz( zm%y7M6lhR5T`kwr)$KU%jb@~bYcmbnnpTRJMyT!_Z;f#Vx>4Jm?r7N$Bk5E{^_(7b z`&obZ+2YI5e1+G0__Ot%ilVzD;;R6qXsidhE^V-PX#cR_bApV#92GUSvBkv*+%Tyu zHho4$Mtjv513I@T$pwN!CN7l(SXFGgd98@r@Yd*NQtpKN`tJ@*eHKz$2tWqv?tpZ47ImmkkTLz_K!4=JkS>obbK5Jzj9Q13W*0o?B|GY0 z7w8YZe*LN|8s_BoXh#nhO+|i=mI|-rZVGEsmRr63ij=&3$Pt4CR%Lr2PGCR)^EMlJ z|F;{R?H}fsC8eZ<#LNzYAs=p?&b5Epcm>G3F`;VI->-v6#>$mPlzrA|ZiH zCWCChq!5|jbTIEwVnRZjCdiaX5jIb|Ur#XzKErPiRQhaIISn-u$osfTR%=Q$^BW3h zvU0e#Um+^Rv{^KZOd)*pMJa-gQtm?{Z0`1#{%OgaKQ9$#na{$}u|* zbgG6XCLOPeMuGRvm$DlCv$nR@i*ptr7DI^Nu@pjfl-X5r6^Stw`tG~r>0pwlu$Wl$ zEPps~7M)}dY-_Io<3J9*`c=D)J$*!WcJ}4zuDHsYaw_3AE(aopddf3Oa*AMSrFmjX z*}J&L0tRBA-cZV?PqZ3IlJ*hr@|&9%k19eT(;qaq{vsDxAAS5BHa0eW8g*9d;FW&K zVn9oRejh}ageY?dYpd3V+WSeaiLc%Z|$cn zYm`p~56GlU&LShBx+DTZ6wG|9^JyW;jf4|T8dhk1XG-0ZIcfLE4KjL7@c0j5txBmv zRkx4!B$=wWb%)iwBxuE`qqTTKJYeut#Xys8RYylhTZv6wEh#Mnvk#~@r!@N*W|xqF zHuL#2F*Tl{_XSrDqd6%#p4z-fJ>XYbJZt4_6jI};{p0ou zv5Y1TLe9e>t9S31FrI};t<3jTFg__^!c}GX);*I(9!hB0aq5)$@4cA3rfZ{Y-dOiQ@+&%-H?JSSx#11@oTZpi%uf5-mHe z{KQeIJdY6;6QZ&6rT=OrXVgaU5<#3*+&3Q>mu0nOFu!a_FFSJX<181(TeDWM$* z3qDnsqQpaT+$BJ2ImsQchiV=Bx%V8`lQe<@nO`(8z{?X60Re)QvkDLu;-~L*A(Ap8 z@bQMlo@iM(IA($qj(z_gm6*t^QOJl;Mj&AH2xxF5IDn$QhR~wU{f|TPmot9UAm<3? zEUg90R9RVBM~6Jx4+`>uh)DXaf|4dV1%=1%a0dqmz}b@dTD(%`YTiG5_;9h=Cq2<0 z{I0Fa{rvaRQhOjS9B}yg-l06zv8ohrVEZ@Od2#B)&J(G z;etAE5TwleAu&CDpHuI+s>u2u>b?f|BQrQ0Zf9o)CJjwGp1qcsh-i5Vq$L1k&rVN8 zy}y4Wk86vmt*vF%C}t`sLr#+Bs5PuyYa_^gM-r2(vgu7@bq85EJwL4JUr^JZd42J z&UFqMK6&-()ex*h&XA+b6Wr1}%VFVu>Sv}u4_m;iwhTaoA=jP`p?hj^697@#z6e*ZWA~pmWsTiJ} zLS7u;iO7zNi__f;;(T($GWByrzSbYo*~FY)3zOQ}*&$enEOvi4l-zGM}yBL@fWzT?D1QvM^hr%#R3Kqexj5NN@9JzHh< zgV4y`U67$olZ2Xz>fbv2K&+so)P26X z`sHdHAOC}|#zfP5QrV2%qLfG4KK#;$Qv-o>}JKi*V1c<<#U zg2o(p=CUkF=Eu$vZ0GK-B~nB<2WKN8CC%6McYC*02PsnD4kPE+*4B>UVcPBkc0>sW z=M|8pM+tX^)V`vr{7mFAk7>#;C}?kf^5n^(2*{p?s-&hE;Rp9sMt$ihwhlhu&Q;}9wWGKkEVQcGF66K2s4#s;X0>T=? z!GO2yqo4lGFABQMqC^l2Z3^zFPoKd~t5#$`yK2nxaP3k!|wWRbcu zn(<=HYMLYS^PyW{x}_tiu&}YEX8TsOi%x&G`X5flvaz!losL<0-CS&>rlt;D5YVb{ zb#`_dcpabzKa!2=>+g?_idr=`_4cM(VTjHzFFyo%EdlMABV7wocQ_^f%EZJ3$k}&y zcROoa4O;}FtX5zI%sX~MPxTv+yaxLE>fxRTwM_8fNbBc<{DW551?(VyI@_J&7ZzrX zXL$7J(fL${=F1shAFd-I3|lrlVq({|zF5HQ<|Dvg$d&@-i&v`1tq*V?tDH?33WpiRu%DfpxP&L2DxqTbtzs zPuLX2JUuod@NKui9wGE8ofLs^dO||2c5#dCiXkQj2J~4U3?Xdq1Y38cgR?!!6t9&N z3@=quG!Oiuc7rG-6#nScRX%MK{|JfRX$jPcp>iaQUZ+c}ldVXwu-e+kM@)|N>}-k& z&F+)5v~&rFUn?wOW3ouctfH>6@)*bonKa@S-^a%h-1V86Xr|GJ43u-7>Oc=rVad+P z;p62sysrjUMqny4D{H%)lMrR!XJ6mDg)CrtzJ9f=vU$$hIOkKgUK+4fR~1z8`ljgF zL;jFsu^ruKuhBOfeYFg{5cTs-FxJ2y8?1cNP%e`*s<^(ew|`P}=hlysyQ@*x3o~Lx#q>y7NC8JA_0( zT9}lCL}++;Ux;Rrdc_P|T5NW1F5SnLtHV^`^={i2f=I@$t}ad}ZqUT=&;kS9@@x_0};S;u^z@U)DnVZLdc29KZL90d)(< zNFDRx{f7@jBCsgp{`@K^QGOhNK+q)NCU5TVcb6wnXA@#D6MUN91w0CfwL2f(Qdcld zua6X!-Y{afw6d7`fFP>%`l(}pJT6)S9zVt5b@Y)d0t*YvDtJV2D@H6x!yj@`CJ!mm zkbjXtzOmdDM$V@2zs7+75)S@%9gfqt`TF`I5C|~v(9qC;0Q5kE_lr%GUs}v6xVAtM z;5-6CTb-Ae7Z*n)8xR#T=@D>Pl>vO(%gYPE)>!Ia+Fjb%`25+k zH=#OgM92|CPA2nO*L{5h1L_@34Gq`dKVJ^4l%mKg1Biva&L=FsZ07nVFewZE!BV zf{EZyAa?Wd@g)te3%l+~NJ`cP5rdb8o&-T3V_`jEU zu8fV1EiEk#9r5zVGXD;%t`gnwzy2AUjXILlAywy!1$Pw-& z^JFVczQOYk81B*tz-5r99ksnC7#JAb$%B2Q#~#lW>Pkyr0PFTR!e1MfDWve9QF>_m zX8#GaWhs@xnhQ19?-nu06Uw>)LZofaB@}B*`nvr#& zD?es)E06EiQ3Hc-D&IJ}xe3tHwgQ5Hi>mNV*{6N^0ET0gUUm^y#aha{tC`EHaeGx}QRn%}IM> zfD>9LqJN@JcSOzq@`fh$Bnj}%W}|M%P1VpOm+9XmXB(>g9rjbLT8aIcb;Y}i3rGw~ zl4$N?(2_a=zTUS#^9l!k;ePLLreWJs_W-N`^`@(De<`L>sse8R9j=&&5S?|Vi~tThxF)0)YAH}I+gE_E7F|O1LD8e>HI?^;fcIh ztsa45ChhG?>Go8jpr<3fY^z>D@ACMKSR}QtT#^PG{s#3NJp=aN!IR(ZTX@+M@#6he z$Gj`j=HGO2L(}|gSkZp52wKs3aoLJeb=`xU(A+bqdV#4&FlrE0go__vIx4O5*ZaSl zw=1H1d$zmqlFfzFrXhTM zbn^BWpQhb<2N&TkA29HN(q|!v(hTK&CkXQ)?xtfK?=&NuKrA$?_<1gv8*hS}#En9p ziBsv%&SD1c_q8w86qe{lHmq7dW%%+Nd_-r&-ZQAyW(JF4hV4jJ`@Z{nnv8@ez*_qL z;TuThq37uy&H6rfY#RUMOkkVe+soTbAxxBK#~SJEl!mS+3W-IN)s&21#jI%@=ymx0 zPB$%#w8^kZLJzz>XJE^_Hi;iJR9>M)32fBZ=WIdu;}tw#C6k^3k;k3p3p7JWY-rP+z#$fL9LbLDcZ{q32pIHY>-Kr8nENUCuKp zo!q{en~gXfs0y$x5^A5}frH{D))q$f<5sB+?>wLYvQ=D8Xf#fE=dTjWc0T~GcCNOa z5$Ps4iq#=1a6TT3Fl^0mb;+Cc1HNJs9ikLslQD4Q;}jG2lJ2HtsMAu2XRNPIK8<~@7alBhfA@+AqWr7yA=v4=ECWgM%+)^e$;&r z-}N?p36{>X%~7HmSYoH~UW4yRY_B>x<(tUGT9wwD#KEbnYNdd@%n;NM%iU>0#W@2b zhi;;jY`Y|>ioLtRXtq-!7`q%7(>0&^5(nk*Q&}&<(7}xLRCp+rH?NZSRyMDGyKa-? zuMQrb&i-g~K9MPZk%GiuJh+PH@0o zsSw(X9EVTRsVnTN6r8y73i1yt#E+674<9_h!@~oqHxD;A5fKrEw~eJGAYkn!T3T8{ zl<8s6<@WXHN-+G!QSdL=doK2sE18b>`@M)7MUCH24F36C|MWr4i~k-U*Gi(Jqj7O@ zv$C>4KAo2b;o;Q4EnlmyV6SX5A+q61F|WUUG;emB*19I&RG&^Rd}Bf-WSiXFAMbUy zNA>Ns8Ya=k<=R~jX@qtW;7;nHN=izBfq|?(9{|Z!Qi>qi|Mg2wQPHdmwtC?)Bw`L& z2KA(-)X@MS8pR*Fs?V~!c0VSIjcHuB2=+L7^-k3;0OPz%YaHcnTE(<}J=^({2w7TD z!Sm#aWGLMZV4NU+4pnTO`~gzq>({S2-{Y3kT7NG$8WGYl2X02H$a5w^*8GDRIOX{5 z_aFEC$E-w%s|)_z{Q_Bf89734@@V&Eigjfu6PJrC2Buw=qg z8vYne9#gEhrX*0y8)+5Z-EN&NRV+;Gl^4la>t|gkxqd*jbG4nG#7nlgX!LK?9g#$` z%Yp(3s8==&!@Qj8_N3!8(G?}Uk+)FvS+UXWAPHUM=EzM(no;Cn)TgkwX%xhIkHe*R zRNfN%>(I0T4!gPej!X2}GeTV4j4ML5KY#vo07-Av_uj*VUSxB~Q2F7e1HGEJlf~`t zp(E+|Dlt8Grx|JvDm{35VV-OnzxN2rz9VK8on2j^klo=WDJdBcptL<@S#^4b1%%8< zYCrY+A)vIBtC(smN_UebG8TGXKR^Aac)WB_`RMJ&$1i$r#mnDL_Yh>&q`>3qYv$(3 zZAQ&>a#?pKxG&-j#Gqd%13Sm`R?`1Iw>*99di_z?LLh#RYS%g{-kGlJPJ;^+X@OYnZK=kz#EAw)a zy`uF@MjJt42KgcNYB$b6P`Zi3?Jwg<82xJ9#eQGS0S7~Qfzz`2{X*12)x8+b6whO{ z(6?U_6FsHBLEbuZ0c0z`d;4rxb4xPyhR)Zp^?3C~HOTcL*V1TYKD($}A#UgpL}2wE zjQFfAM%gwSQpsfA%Dh-w)O!p7SimT{Fq$}Q@O|xXY8?mKN^mVQ{j%gl??b2SmKgj# ztKQ?B3RlhG$Ppg|7{+x?mLBT(n3}ejE9`O5P%H!Xp1GwmqGR~l957MGMcO|R(0HQb z%cl>R;dmr$Iv{F1y_zDo(fMV12+fq3>HJyV+i^bw67=U9K;CuL{+JI!qyYWi7Zh@V zheYO1emb9jqvG`}e^~==aJ|-=F8-T6$aW1%0`N92|3(Yj@L~B8OrJqE{3^ z-ja83+d8%4RsjcYgIT0N0Z;4o^PTSmFZ6m#Z0uU;ctQ+y8O9j%L8jxAx2}w8{xEv* z9=?+QxK@H}A-$`5+$vGv{jVzp%lV;=bc6`bdhn6thMauZi2@dQcESZqGh?#SVL&AjU5kx5)UTS1M2|-70T;sJ_o)V%R``0sb$;229*Bx3Mp%Q= z$gk`@sL4MJT~993soqR5M4aF4SKpq#X$Euk?q#r@o_h}`6Aqsqu`%87!+y;S$>iOo zJ7S5Ru;CfL*j*dq_U;(A3@87?h znVZ-AdPg~svA5J$16~^~I3GbWS{G$e8j-)`2&EKZ-pa{eMe1Lfk6X1GKSs9lq>Q@dfez1;C z`0(fv#dPOqP#7+l076sn!PeH+@85Q7CD+&2HMR>;_oNXY6Kra3K{07V_`UMLL3UIl(7Xa%O2lo?_nEvr2Eh&kmi=7Bl;+@@zML&Q?C#U19 ztA-;bASk{9!a$R(=O6pg&Yx4H2At34??s%eOF$@vevA(c0th@kKK{K2N`@l7dXK*{ zqZ9;y6kWMFIiOZXVHQcxOiPG{&7vVaWNFC!zPqeJ3=GDFe) zHs@8zl(dwTaYI1?>t?c(iwif9TspY&I7xEUmOJHJKjAF$xK!l5js*+?VU5V{@)#|O zdJY{JG#o}TAjEwInmnBpAqVCAezOr`hV5K`PpZvV_6mj(v!2vETPdwIY``7+@CGs5vc*OGd# z2=V{Dyp@-m7c3PRG)16xQAn(t7nQcq(`Z&}Zv-D*>fbyg5ICPK*m;El-N+V<`&HCF zG-$2)bPsLnC3ApG2ch-FgAA>kA*o_M{+KBn*Bt)$4$7RT&Jlp9Lhk3PE!#Y}_wQ(1 zAomINwqQ`+$}(z)f8Q#<;_Z3F`(Ub=&*CzlEKt3yGA|5yNI2t|r_645m%>ZR33GUe*ucW!B*a?+QJfexw+fwX6Xm4yJF#LVK9F2fR%Hr8!Nk&U+jlQWy{ok|D z(_*LFt0rKrpzCVG%1`Q393UQ*>4*c!sIvI|Z`nzxx$q*N- zOEuCb>4Y*C6AT4mV6LImkM6(9NjzSSEp`%~O<)-AYpEfcy;2$mq>%$~p>%*zoB9MhW_W=w(682^}3Bo2R3@8}!72PbbI6 zobtGEKI-IF8rN*$h9jH&3V(~`2P(Z!N`6pyDSGTCWL^9aEskL0B6^lI_p$K7Soti@ zq%dsUvI@{NrS}v$xuC?ZHx!qUfLeik)Z#ivBf`QGGy=)VtyD3(^q(y1-mY3ai9b^m zX!_-kY1A>suTV-yv1Q)9M~Um=;sW}Nk@nHO1Ox;oe95SUWAdS2aIurLsMqlH)F3ae zrcHz=<8G|(atVp6X?@=6VS3%E+|w$(xKU}oTSbh02xt%>}sP{GW(*s``3?G-XtDUmy%l4W@;;= zttjOc9vBMzRflMAVE9!BBKU>!<-x<8PZ=!=fm@wxe{5ONz71o1@sYIyrm7y$%v7;@ zLcACZ>F(;v%*yKgk38Ey1D0IRk=cx;USYAaOE$Uk`}@w{%X1d>d!H0)deHTbdNs>W zzV%Sas&gL+7EIJ>V1|QgBsqD(=sS8@P!j+ZID)Y%ZFaEOW+<%dJHbj*VSdoQnM;ih z*Qn2o!pyAC!+gq8M}sl%IjP@^ZojmHH+Uu@2?34LKLt?;oZC*&s83Bw%G`xA?oqo) zPO6~2f>XN5dyZY4l1qEkUHT{ZLV#tH1&CvHRSOiJon1Fag$$!)-)b5v%h6Ej9o(CC z&T8`-gHy`8*>7AmKl95BWjrB9LWK|n8B&n%a%Z4GANj&?#sogV4^X7d=L5&B2McW% z@AL+>w0ZgTTTz4)>6D&^gZqBRnA}rJy2%&Ct0;&8wrR2Eed-Z!@rGJuhp2 zXn8gVRrpLpwTuVqEQHMafhp}c`Oi@ZF%NUDd_^(O;W1cM6MouToUK#UG{QUZr?ML#rR^Z4 zUpUt%ef=cX%+U-<2@RQUOM^LmKfG3%TO+#6_4FK;YnIPBMGeSZRC8m4ig3Ny3;W-YSZ2K#^!3Q)y?F(eWv8F7v?)l2YzVM&*|a{fEYE{ zgNOA=EF9k0qrw*b1^oMcyiCoSw}meU}1^d4%Cy)U`SyxO_0dVAW(fz?=eg=%R)Ml2QC zE@LrxuEpnJhQ}JH7VN|tOzTmj9@P|3Qy-MBuiG?lY@c^k&N`-lk?k6Fin35@i zgRBVf;~O3ghrBD*Ortg8zvGAv`#X;3A6Cd?4fKT?2-Ml$RO_st9RIziXzzn>LHt@& z{5Z3$%}ke$KD!AwzfIqM;Ad?{MO8*HnS;?Q$@9eSpN+=YVV`Ju09pgJ!`Sq~OX~oH zKn0J(@lXDcODVBy831?U`#X`%0dAAv9dg!^F5{-l-@o>DzpNFl>Ko_l=;Hv+w)%)E z1dBcCqCcC-2jp9!fT+wvgz&*NR38YvNQihGm(+P^a+1;i^5x2QKk>ovx1e-A?WS3?8BcYO zt>64C%_&Vr#wc?{wV2LWJ_~zHdziq=E-@PF;11|dUrCZ~k`gJh(6fbM2k(P$__Da*hdo%@Hy^+ zG2Ll1<0QGP&mfYZACnF2KHZ`6+MbvQetY%h+JqRxHKtkf{@pOgHF4bSgzn0=H?a9u z8ifbhk7|s>Pd{%A?_(YnDd7w=lXQM)>7%WDR|>h$rVdkQn&de3qWi|;C1!8etarNA zdz#9|-mxBMJ7GK~z%h-r@8WrBIR8<3|C+bEj{}0CbrGP9pua=aJvf|kEPecsp(*!M zIG9h{Hi6Ci_K(}J&+o03Ta>=XPt!NXhbt~8W<*9_ml;`Pd>3DiP0*9{aa;J-x>>Ig zpmU!s{M-ECh!-mQ0e)H2 zqe`kS8pfPncx&-utH1)ieZpmiwfRcEE%^2)oCH_jD(x+6KwgF%8Qi*#MiHj*2ph(32*3X6mZwF$7$^P13-_+2M zhL)C=mzS50?yZrL@77I9=@yYBdYp7Xo!TR=taR_yCba(Pv;|f|%|G@0x9l;uvfDSC zV}|l$&9I`1nshnVl|R9iqO5K#MG=Z6GV(Lnxl@5YvLpTEQL&_2hfY3#6O{#`pr&m@ zO=#|a$z?eg_`~yB{65?8@vm5*$4wdN7V&lere5>{<*!P>4lPW2Tt%CCw5a-tRIvNH zUHdZJ?Iby|{VC`LA2ThRUNZ$Ynm?I&*tg^#&1$BK-REjH?0LRB8oLV<#TO>>QjBuF zImU_>5JK2GVD#jV3tL31MG<4t>Ttk|)XB-oS-Wy`QPdFR-;x37{QmEpR!Bb<@~Y4p zGtG_}{l49$I`5ZW_;!2zQ%r=c{&u%0ZE<(g+fjPz_Cmvb^J14TerVf$Ys#ScWHNOj zeE}l>RX|-g?u~1)^++oQ(+fTAR-?!jLjLwki{gL`ikPCX^X<$v1h4E+wnK%6IMPU<;y-62a*RlHc$OuyTmF& zBx@CC=jK2wd}nQ^u`dKmaX%C63b?(wh`zL~y65?)3+D9x+3PddJclN&rjTEl86!7{g}FWK3k!}ROfi=?C^P~48JDlab|SCt-q^^}Z~asjmC zfNf9Xo-|;W7`Ccm#f4mm-(A`^pxlDqTF~;GppXMvkJr}MzaGE%`AB!7N3-@Tm<20K z%kipP&`R;<%^Rj#S@QAyIvvZX;8Z-aH%&G0+6hVJYiN=S1;w{%O+s%omZ~_CSV4!m2axBM%fW1d{ z++f$GzXPfnvF8oTbBy^_7|zMb3F+gHDJc|U&NsQ`^SN2*ms{u`4l4bk6LX=RLuC_MNhT0yz7*KihrfQF< z@59|NDMzrKCm|sLXmFtWny1t%Dr>=qT^#aYe=3wahi{82U`NCg?0Xqm! z11?d}uQ?|jOC_VPuRp@yT2<0@&$nMkM~AtMeFb#JgEmfJhTn^W$@~*KT2k0{`#&1V zNlAnHb#`H|o&9&bnc)Q!Noi@t^%%WF5B;_&56P*iyBpuTyVq%dZLS}dw2Xj6P(25| zKS+o3p;Td@7JBj4&j7!p5OQJ?5I8?SSgGX$-U)yXKt0n}kaoTHfM8F3^dL7mlgD)d zo3}csL7N22b2?yfup6*epPPqgV||^eja>{H(+E2Lm;$*La^*Oy*~B5V^q*1G@PFPY n0G8hW$M%8$jc3o4LOK5e;CE_8nPCMTug#q2K=lfAHy5 diff --git a/img/scanCompare.png b/img/scanCompare.png index d6b771b2ceef68946cd579093820a7e50f8d4a71..abb5d9267e98cd6de42e8f2b80254eb2d37d52e0 100644 GIT binary patch literal 19582 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 E07dIs@c;k- literal 16579 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 #include "testing_helpers.hpp" -const int SIZE = 1 << 12; // feel free to change the size of array +const int SIZE = 1 << 19; // 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/src/testing_helpers.hpp b/src/testing_helpers.hpp index 834bbe5..46337ab 100644 --- a/src/testing_helpers.hpp +++ b/src/testing_helpers.hpp @@ -50,8 +50,7 @@ void onesArray(int n, int *a) { } void genArray(int n, int *a, int maxval) { - //srand(time(nullptr)); - srand(124); + srand(time(nullptr)); for (int i = 0; i < n; i++) { a[i] = rand() % maxval; diff --git a/stream_compaction/cpu.cu b/stream_compaction/cpu.cu index 7f1ab6a..d37864f 100644 --- a/stream_compaction/cpu.cu +++ b/stream_compaction/cpu.cu @@ -19,7 +19,6 @@ namespace StreamCompaction { */ void scan(int n, int *odata, const int *idata) { timer().startCpuTimer(); - // TODO odata[0] = 0; for (int k = 1; k < n; ++k) { odata[k] = idata[k - 1] + odata[k - 1]; diff --git a/stream_compaction/efficient.cu b/stream_compaction/efficient.cu index 58c6668..0acb3cf 100644 --- a/stream_compaction/efficient.cu +++ b/stream_compaction/efficient.cu @@ -14,24 +14,22 @@ namespace StreamCompaction { return timer; } - __global__ void kernUpSweep(int n, int d, int* a) { + __global__ void kernUpSweep(int n, int off1, int off2, int* a) { int idx = threadIdx.x + (blockIdx.x * blockDim.x); - int offset = (int)powf(2, d + 1); - if (idx % offset != 0 || idx >= n - 1) { + if (idx % off1 != 0 || idx >= n - 1) { return; } - a[idx + (int)powf(2, d + 1) - 1] += a[idx + (int)powf(2, d) - 1]; + a[idx + off1 - 1] += a[idx + off2 - 1]; } - __global__ void kernDownSweep(int n, int d, int* a) { + __global__ void kernDownSweep(int n, int off1, int off2, int* a) { int idx = threadIdx.x + (blockIdx.x * blockDim.x); - int offset = (int)powf(2, d + 1); - if (idx % offset != 0 || idx >= n - 1) { + if (idx % off1 != 0 || idx >= n - 1) { return; } - int t = a[idx + (int)powf(2, d) - 1]; - a[idx + (int)powf(2, d) - 1] = a[idx + (int)powf(2, d + 1) - 1]; - a[idx + (int)powf(2, d + 1) - 1] += t; + int t = a[idx + off2 - 1]; + a[idx + off2 - 1] = a[idx + off1 - 1]; + a[idx + off1 - 1] += t; } /** @@ -47,13 +45,17 @@ namespace StreamCompaction { timer().startGpuTimer(); for (int d = 0; d <= dmax; ++d) { - kernUpSweep<<>>(n, d, cudaA); + int off1 = (int)pow(2, d + 1); + int off2 = (int)pow(2, d); + kernUpSweep<<>>(n, off1, off2, cudaA); } int temp[1] = { 0 }; cudaMemcpy(cudaA + (n - 1), temp, 1 * sizeof(int), cudaMemcpyHostToDevice); // DOWNSWEEP for (int d = dmax; d >= 0; d--) { - kernDownSweep<<>>(n, d, cudaA); + int off1 = (int)pow(2, d + 1); + int off2 = (int)pow(2, d); + kernDownSweep<<>>(n, off1, off2, cudaA); } timer().endGpuTimer(); @@ -116,13 +118,17 @@ namespace StreamCompaction { int dmax = ilog2ceil(n) - 1; for (int d = 0; d <= dmax; ++d) { - kernUpSweep<<>>(n, d, devBinaryCopy); + int off1 = (int)pow(2, d + 1); + int off2 = (int)pow(2, d); + kernUpSweep<<>>(n, off1, off2, devBinaryCopy); } int temp[1] = { 0 }; cudaMemcpy(devBinaryCopy + (n - 1), temp, 1 * sizeof(int), cudaMemcpyHostToDevice); // DOWNSWEEP for (int d = dmax; d >= 0; d--) { - kernDownSweep<<>>(n, d, devBinaryCopy); + int off1 = (int)pow(2, d + 1); + int off2 = (int)pow(2, d); + kernDownSweep<<>>(n, off1, off2, devBinaryCopy); } // populating compact return array