From c8298eb619b4a23164724bfcf663f03dd34a732c Mon Sep 17 00:00:00 2001 From: YOUR NAME Date: Sun, 25 Sep 2016 16:01:17 -0400 Subject: [PATCH 01/21] coding almost done --- stream_compaction/common.cu | 10 +++- stream_compaction/cpu.cu | 46 +++++++++++++-- stream_compaction/efficient.cu | 100 +++++++++++++++++++++++++++++++-- stream_compaction/naive.cu | 63 ++++++++++++++++++++- 4 files changed, 207 insertions(+), 12 deletions(-) diff --git a/stream_compaction/common.cu b/stream_compaction/common.cu index fe872d4..99aef01 100644 --- a/stream_compaction/common.cu +++ b/stream_compaction/common.cu @@ -24,6 +24,10 @@ namespace Common { */ __global__ void kernMapToBoolean(int n, int *bools, const int *idata) { // TODO + int index = threadIdx.x + blockIdx.x * blockDim.x; + if (index < n){ + bools[index] = idata[index] != 0; + } } /** @@ -32,7 +36,11 @@ __global__ void kernMapToBoolean(int n, int *bools, const int *idata) { */ __global__ void kernScatter(int n, int *odata, const int *idata, const int *bools, const int *indices) { - // TODO + int index = threadIdx.x + blockIdx.x * blockDim.x; + if (index >= n) return; + if (bools[index]){ + odata[indices[index]] = idata[index]; + } } } diff --git a/stream_compaction/cpu.cu b/stream_compaction/cpu.cu index e600c29..68c1499 100644 --- a/stream_compaction/cpu.cu +++ b/stream_compaction/cpu.cu @@ -9,7 +9,15 @@ namespace CPU { */ void scan(int n, int *odata, const int *idata) { // TODO - printf("TODO\n"); + //printf("TODO\n"); + if (n<=0) return; + for (int i=0; i=n) return; + int tmp=(offset << 1); + if (index % tmp==0){ + if (index + tmp = offset){ + idata[index+(tmp << 1)-1] = idata[index+offset-1] + idata[index+tmp-1]; + } + } + } +} +__global__ void downSweep(int offset, int n, int *idata){ + int index = threadIdx.x + blockIdx.x*blockDim.x; + if (index >=n) return; + int tmp=(offset << 1); + if (index % tmp==0){ + if (index + tmp GPU + cudaMemcpy(idata_buff,idata,n*sizeof(int),cudaMemcpyHostToDevice); + checkCUDAError("cudaMemcpy-idata_buff-failed"); + //upsweep + for (int level=0; level <= levels_max-1; level++){ + upSweep<<>>(1<=0 ; level--){ + downSweep<<>>(1< CPU + cudaMemcpy(odata, idata_buff, n*sizeof(int),cudaMemcpyDeviceToHost); + checkCUDAError("cudaMemcpy-odata-failed"); + cudaFree(idata_buff); } /** @@ -26,8 +80,46 @@ void scan(int n, int *odata, const int *idata) { * @returns The number of elements remaining after compaction. */ int compact(int n, int *odata, const int *idata) { - // TODO - return -1; + int n_remaing=0; + int * idata_buff; + int * odata_buff; + int * bool_buff; + int * indices_buff; + + dim3 numblocks(std::ceil((double) n/blockSize)); + // + cudaMalloc((void**)&idata_buff,n * sizeof(int)); + checkCUDAError("cudaMalloc-idata_buff-failed"); + cudaMalloc((void**)&odata_buff,n * sizeof(int)); + checkCUDAError("cudaMalloc-odata_buff-failed"); + cudaMalloc((void**)&bool_buff,n * sizeof(int)); + checkCUDAError("cudaMalloc-odata_buff-failed"); + cudaMalloc((void**)&indices_buff,n * sizeof(int)); + checkCUDAError("cudaMalloc-odata_buff-failed"); + + cudaMemcpy(idata_buff, idata, n* sizeof(int), cudaMemcpyHostToDevice); + checkCUDAError("cudaMemcpy-idata_buff-failed"); + cudaMemcpy(odata_buff, odata, n* sizeof(int), cudaMemcpyHostToDevice); + checkCUDAError("cudaMemcpy-odata_buff-failed"); + + //produce the indices + Common::kernMapToBoolean<<>> ( n, bool_buff, idata_buff); + scan<<>>(n, indices_buff, bool_buff); + Common::kernScatter<<>>( n, odata_buff, idata_buff, bool_buff, indices_buff); + + + //GPU-->CPU + cudaMemcpy(odata,odata_buff,n*sizeof(int),cudaMemcpyDeviceToHost); + + for (int i =0; i< n; i++){ + n_remaing+=bool_buff[i]; + } + + cudaFree(idata_buff); + cudaFree(odata_buff); + cudaFree(bool_buff); + cudaFree(indices_buff); + return n_remaing; } } diff --git a/stream_compaction/naive.cu b/stream_compaction/naive.cu index 3d86b60..77ef054 100644 --- a/stream_compaction/naive.cu +++ b/stream_compaction/naive.cu @@ -1,19 +1,76 @@ #include #include +#include + #include "common.h" #include "naive.h" namespace StreamCompaction { namespace Naive { -// TODO: __global__ +#define blockSize 128 +//__global__ +__global__ void scan(int offset, int n, int *odata, const int *idata) { + int index = threadIdx.x + blockIdx.x*blockDim.x; + if (index >=n) return; + if (index >= offset){ + odata[index] = idata[index] + idata[index-offset]; + } + else{ + odata[index] = idata[index]; + } +} +__global__ void excludesiveShift(int n, int *odata, int *idata){ + int index = threadIdx.x + blockIdx.x* blockDim.x; + if (index>=n) return; + if (index>=1){ + odata[index]= idata[index-1] ; + } + else { + odata[index]= 0; + } +} /** * Performs prefix-sum (aka scan) on idata, storing the result into odata. */ void scan(int n, int *odata, const int *idata) { - // TODO - printf("TODO\n"); + + //dim3 numblocks(std::ceil((double) n / blockSize)); + dim3 numblocks((n + blockSize - 1) / blockSize); + int* idata_buff; + int* odata_buff; + + cudaMalloc((void**)&idata_buff, n*sizeof(int)); + checkCUDAError("cudaMalloc-idata_buff- failed!"); + cudaMalloc((void**)&odata_buff, n*sizeof(int)); + checkCUDAError("cudaMalloc-odata_buff-failed!"); + + /// CPU -->GPU + cudaMemcpy(idata_buff,idata,sizeof(int)*n,cudaMemcpyHostToDevice); + cudaMemcpy(odata_buff,idata,sizeof(int)*n,cudaMemcpyHostToDevice); + + + for (int level= 1; level <= ilog2ceil(n); level++) { + int offset; + if (level==1){ + offset = 1; + } + else { + offset = 2 << (level -2); + } + // for the given level, all threads read from idata_buff + scan <<>>(offset, n, odata_buff, idata_buff); + //std::swap(idata_buff, odata_buff); + // odata_buff --> idata_buff for next iteration + cudaMemcpy(idata_buff, odata_buff, sizeof(int)*n, cudaMemcpyDeviceToDevice); + } + excludesiveShift<<>>(n, odata_buff, idata_buff); + + //GPU --> CPU + cudaMemcpy(odata, odata_buff, sizeof(int)*n, cudaMemcpyDeviceToHost); + cudaFree(idata_buff); + cudaFree(odata_buff); } } From b3c815cceabf5818f0bb1ddf52e1167313df3223 Mon Sep 17 00:00:00 2001 From: YOUR NAME Date: Sun, 25 Sep 2016 17:13:15 -0400 Subject: [PATCH 02/21] debug --- stream_compaction/efficient.cu | 38 ++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/stream_compaction/efficient.cu b/stream_compaction/efficient.cu index 8d65814..7b365af 100644 --- a/stream_compaction/efficient.cu +++ b/stream_compaction/efficient.cu @@ -1,4 +1,4 @@ -#include +#include #include #include "common.h" #include "efficient.h" @@ -12,10 +12,8 @@ __global__ void upSweep(int offset, int n, int *idata){ if (index >=n) return; int tmp=(offset << 1); if (index % tmp==0){ - if (index + tmp = offset){ - idata[index+(tmp << 1)-1] = idata[index+offset-1] + idata[index+tmp-1]; - } + if (index + tmp <=n){ + idata[index+tmp-1] += idata[index+offset-1] ; } } } @@ -25,7 +23,8 @@ __global__ void downSweep(int offset, int n, int *idata){ if (index >=n) return; int tmp=(offset << 1); if (index % tmp==0){ - if (index + tmp >>(1<>>(1<=0 ; level--){ - downSweep<<>>(1<>>(1< CPU cudaMemcpy(odata, idata_buff, n*sizeof(int),cudaMemcpyDeviceToHost); checkCUDAError("cudaMemcpy-odata-failed"); @@ -103,17 +107,19 @@ int compact(int n, int *odata, const int *idata) { checkCUDAError("cudaMemcpy-odata_buff-failed"); //produce the indices - Common::kernMapToBoolean<<>> ( n, bool_buff, idata_buff); - scan<<>>(n, indices_buff, bool_buff); - Common::kernScatter<<>>( n, odata_buff, idata_buff, bool_buff, indices_buff); + StreamCompaction::Common::kernMapToBoolean<<>> ( n, bool_buff, idata_buff); + + scan (n, indices_buff, bool_buff); + + StreamCompaction::Common::kernScatter<<>>( n, odata_buff, idata_buff, bool_buff, indices_buff); //GPU-->CPU cudaMemcpy(odata,odata_buff,n*sizeof(int),cudaMemcpyDeviceToHost); - for (int i =0; i< n; i++){ - n_remaing+=bool_buff[i]; - } + //for (int i =0; i< n; i++){ + // n_remaing+=bool_buff[i]; + //} cudaFree(idata_buff); cudaFree(odata_buff); From c107d6ffc840f55b54d049f84ee015fcf012d4a5 Mon Sep 17 00:00:00 2001 From: dengxianga Date: Sun, 25 Sep 2016 18:00:37 -0400 Subject: [PATCH 03/21] my name not shown up --- stream_compaction/efficient.cu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stream_compaction/efficient.cu b/stream_compaction/efficient.cu index 7b365af..e6014fd 100644 --- a/stream_compaction/efficient.cu +++ b/stream_compaction/efficient.cu @@ -120,7 +120,7 @@ int compact(int n, int *odata, const int *idata) { //for (int i =0; i< n; i++){ // n_remaing+=bool_buff[i]; //} - + cudaMemcpy(&n_remaing,indices_buff+n-1,sizeof(int),cudaMemcpyDeviceToHost); cudaFree(idata_buff); cudaFree(odata_buff); cudaFree(bool_buff); From 238d72d30965be74cf1803b4921a68864206861a Mon Sep 17 00:00:00 2001 From: dengxianga Date: Sun, 25 Sep 2016 18:10:15 -0400 Subject: [PATCH 04/21] de another bug --- stream_compaction/cpu.cu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stream_compaction/cpu.cu b/stream_compaction/cpu.cu index 68c1499..7c326a2 100644 --- a/stream_compaction/cpu.cu +++ b/stream_compaction/cpu.cu @@ -58,7 +58,7 @@ int compactWithScan(int n, int *odata, const int *idata) { //scatter int cnt=0; for (int i=0;i Date: Sun, 25 Sep 2016 18:15:46 -0400 Subject: [PATCH 05/21] so easy --- stream_compaction/thrust.cu | 1 + 1 file changed, 1 insertion(+) diff --git a/stream_compaction/thrust.cu b/stream_compaction/thrust.cu index d8dbb32..58be000 100644 --- a/stream_compaction/thrust.cu +++ b/stream_compaction/thrust.cu @@ -16,6 +16,7 @@ void scan(int n, int *odata, const int *idata) { // 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(idata , idata +n , odata); } } From f16ca254f2ab4f8d96bb4dcbfd32761a5d8dcb63 Mon Sep 17 00:00:00 2001 From: dengxianga Date: Tue, 27 Sep 2016 14:01:38 -0400 Subject: [PATCH 06/21] radixsort implementation --- src/main.cpp | 1 + stream_compaction/radixSort.cu | 122 +++++++++++++++++++++++++++++++++ stream_compaction/radixSort.h | 8 +++ 3 files changed, 131 insertions(+) create mode 100644 stream_compaction/radixSort.cu create mode 100644 stream_compaction/radixSort.h diff --git a/src/main.cpp b/src/main.cpp index 675da35..a34a498 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include "testing_helpers.hpp" int main(int argc, char* argv[]) { diff --git a/stream_compaction/radixSort.cu b/stream_compaction/radixSort.cu new file mode 100644 index 0000000..6e1c4a2 --- /dev/null +++ b/stream_compaction/radixSort.cu @@ -0,0 +1,122 @@ +#include +#include +#include "common.h" +#include "radixSort.h" +#include "efficient.h" + +namespace StreamCompaction { +namespace RadixSort { +#define blockSize 128 + +int getNbit(int input, int nth){ + return (input >> nth) & 1; +} + +// assume the input and output are bits +__global__ void computeE(int n, int * edata, const int * bdata){ + int index = threadIdx.x + blockIdx.x * blockDim.x; + if (index < n) { + edata[index] = ~(bdata[index]); + //if (index ==0){ + // odata[index] = ~(0|idata[index]); + //} + //else { + // odata[index] = ~(idata[index-1]|idata[index]); + //} + } +} +__global__ void computeT(int n, int * tdata, const int * fdata, const int totalFalses){ + int index = threadIdx.x + blockIdx.x * blockDim.x; + if (index < n){ + tdata[index] = index - fdata[index] + totalFalses; + } +} + +__global__ void computeB(int n, int *bdata, const int *idata, int ith){ + int index = threadIdx.x + blockIdx.x * blockDim.x; + if (index < n){ + bdata[index] = getNbit(idata[index], ith); + } +} + +__global__ void computeD(int n, int *ddata, const int * bdata, const int *tdata, const int * fdata){ + int index = threadIdx.x + blockIdx.x * blockDim.x; + if (index < n){ + ddata[index] = bdata[index] ? tdata[index] : fdata[index]; + } +} + +__global__ void scatter(int n, int *odata, int *idata, const int * ddata){ + int index = threadIdx.x + blockIdx.x * blockDim.x; + if (index < n){ + odata[index]= idata[ddata[index]]; + } +} +/** + * radix sort + */ +void sort(int n, int *odata, const int *idata, int msb) { + dim3 numblocks(std::ceil((double) n / blockSize)); + + int * idata_buff; + int * idata_buff2; + int * bdata_buff; + int * edata_buff; + int * fdata_buff; + int * tdata_buff; + int * ddata_buff; + + + cudaMalloc((void**)&idata_buff,n*sizeof(int)); + checkCUDAError("cudaMalloc-idata_buff- failed!"); + cudaMalloc((void**)&idata_buff2,n*sizeof(int)); + checkCUDAError("cudaMalloc-idata_buff2- failed!"); + cudaMalloc((void**)&bdata_buff,n*sizeof(int)); + checkCUDAError("cudaMalloc-bdata_buff- failed!"); + cudaMalloc((void**)&edata_buff,n*sizeof(int)); + checkCUDAError("cudaMalloc-edata_buff- failed!"); + cudaMalloc((void**)&fdata_buff,n*sizeof(int)); + checkCUDAError("cudaMalloc-fdata_buff- failed!"); + cudaMalloc((void**)&tdata_buff,n*sizeof(int)); + checkCUDAError("cudaMalloc-tdata_buff- failed!"); + cudaMalloc((void**)&ddata_buff,n*sizeof(int)); + checkCUDAError("cudaMalloc-ddata_buff- failed!"); + + /// CPU -->GPU + cudaMemcpy(idata_buff,idata,n*sizeof(int),cudaMemcpyHostToDevice); + checkCUDAError("cudaMemcpy-idata_buff-failed"); + + for (int i=0; i< msb; i++){ + int totalFalses = 0; + + //find b array for each bit + computeB<<>>(n, bdata_buff, idata_buff, i); + computeE<<>>(n, edata_buff, bdata_buff); + StreamCompaction::Efficient::scan(n, fdata_buff, edata_buff); + + totalFalses = edata_buff[n-1] + fdata_buff[n-1]; + + computeT<<>>(n, tdata_buff, fdata_buff, totalFalses); + computeD<<>>(n, ddata_buff, bdata_buff, tdata_buff, fdata_buff); + + + //scatter darray for this bit + scatter<<>>(n, idata_buff2, ddata_buff, idata_buff); + cudaMemcpy(idata_buff,idata_buff2,n*sizeof(int),cudaMemcpyDeviceToDevice); + checkCUDAError("cudaMemcpy-idata_buff-failed"); + } + + //GPU --> CPU + + //free + cudaFree(idata_buff); + cudaFree(idata_buff2); + cudaFree(bdata_buff); + cudaFree(tdata_buff); + cudaFree(fdata_buff); + cudaFree(edata_buff); + cudaFree(ddata_buff); +} + +} +} diff --git a/stream_compaction/radixSort.h b/stream_compaction/radixSort.h new file mode 100644 index 0000000..f4a7a8a --- /dev/null +++ b/stream_compaction/radixSort.h @@ -0,0 +1,8 @@ +#pragma once + +namespace StreamCompaction { +namespace RadixSort { + void sort(int n, int *odata, const int *idata); + +} +} From 54488cdf53a2d87843d3f9a9f38f8048b63ca7e9 Mon Sep 17 00:00:00 2001 From: dengxianga Date: Tue, 27 Sep 2016 14:24:56 -0400 Subject: [PATCH 07/21] cmakelist changed --- src/main.cpp | 10 ++++++++++ stream_compaction/CMakeLists.txt | 2 ++ stream_compaction/radixSort.cu | 11 ++++++----- stream_compaction/radixSort.h | 2 +- 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index a34a498..a330f63 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -121,4 +121,14 @@ int main(int argc, char* argv[]) { count = StreamCompaction::Efficient::compact(NPOT, c, a); //printArray(count, c, true); printCmpLenResult(count, expectedNPOT, b, c); + + printf("\n"); + printf("*****************************\n"); + printf("** RADIX SORT TESTS **\n"); + printf("*****************************\n"); + zeroArray(SIZE, b); + printDesc("RADIX SORT power-of-two"); + int msb=31; + StreamCompaction::RadixSort::sort(SIZE, b, a, msb); + printArray(SIZE, b, true); } diff --git a/stream_compaction/CMakeLists.txt b/stream_compaction/CMakeLists.txt index cdbef77..3dfb146 100644 --- a/stream_compaction/CMakeLists.txt +++ b/stream_compaction/CMakeLists.txt @@ -9,6 +9,8 @@ set(SOURCE_FILES "efficient.cu" "thrust.h" "thrust.cu" + "radixSort.h" + "radixSort.cu" ) cuda_add_library(stream_compaction diff --git a/stream_compaction/radixSort.cu b/stream_compaction/radixSort.cu index 6e1c4a2..66dc2b0 100644 --- a/stream_compaction/radixSort.cu +++ b/stream_compaction/radixSort.cu @@ -8,9 +8,9 @@ namespace StreamCompaction { namespace RadixSort { #define blockSize 128 -int getNbit(int input, int nth){ - return (input >> nth) & 1; -} +//int getNbit(int input, int nth){ +// return (input >> nth) & 1; +//} // assume the input and output are bits __global__ void computeE(int n, int * edata, const int * bdata){ @@ -35,7 +35,7 @@ __global__ void computeT(int n, int * tdata, const int * fdata, const int totalF __global__ void computeB(int n, int *bdata, const int *idata, int ith){ int index = threadIdx.x + blockIdx.x * blockDim.x; if (index < n){ - bdata[index] = getNbit(idata[index], ith); + bdata[index] = (idata[index]>> ith) & 1; } } @@ -107,7 +107,8 @@ void sort(int n, int *odata, const int *idata, int msb) { } //GPU --> CPU - + cudaMemcpy(odata,idata_buff,n*sizeof(int),cudaMemcpyDeviceToHost); + checkCUDAError("cudaMemcpy-odata-failed"); //free cudaFree(idata_buff); cudaFree(idata_buff2); diff --git a/stream_compaction/radixSort.h b/stream_compaction/radixSort.h index f4a7a8a..4aa4817 100644 --- a/stream_compaction/radixSort.h +++ b/stream_compaction/radixSort.h @@ -2,7 +2,7 @@ namespace StreamCompaction { namespace RadixSort { - void sort(int n, int *odata, const int *idata); + void sort(int n, int *odata, const int *idata, int msb); } } From 2afe62a5422d05d0d35d10f461956dcbea475893 Mon Sep 17 00:00:00 2001 From: dengxianga Date: Tue, 27 Sep 2016 15:55:04 -0400 Subject: [PATCH 08/21] debug --- src/main.cpp | 6 +- stream_compaction/radixSort.cu | 227 +++++++++++++++++---------------- stream_compaction/radixSort.h | 2 +- 3 files changed, 123 insertions(+), 112 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index a330f63..3828fc8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -126,9 +126,13 @@ int main(int argc, char* argv[]) { printf("*****************************\n"); printf("** RADIX SORT TESTS **\n"); printf("*****************************\n"); + genArray(SIZE - 1, a, 50); // Leave a 0 at the end to test that edge case + a[SIZE - 1] = 0; + printArray(SIZE, a, true); zeroArray(SIZE, b); printDesc("RADIX SORT power-of-two"); int msb=31; - StreamCompaction::RadixSort::sort(SIZE, b, a, msb); + printf("size of int is %d bits\n", sizeof(int)*CHAR_BIT); + StreamCompaction::RadixSort::sort(SIZE, b, a, msb); printArray(SIZE, b, true); } diff --git a/stream_compaction/radixSort.cu b/stream_compaction/radixSort.cu index 66dc2b0..99cf51f 100644 --- a/stream_compaction/radixSort.cu +++ b/stream_compaction/radixSort.cu @@ -1,123 +1,130 @@ -#include +#include #include #include "common.h" #include "radixSort.h" #include "efficient.h" namespace StreamCompaction { -namespace RadixSort { + namespace RadixSort { #define blockSize 128 -//int getNbit(int input, int nth){ -// return (input >> nth) & 1; -//} - -// assume the input and output are bits -__global__ void computeE(int n, int * edata, const int * bdata){ - int index = threadIdx.x + blockIdx.x * blockDim.x; - if (index < n) { - edata[index] = ~(bdata[index]); - //if (index ==0){ - // odata[index] = ~(0|idata[index]); + //int getNbit(int input, int nth){ + // return (input >> nth) & 1; //} - //else { - // odata[index] = ~(idata[index-1]|idata[index]); - //} - } -} -__global__ void computeT(int n, int * tdata, const int * fdata, const int totalFalses){ - int index = threadIdx.x + blockIdx.x * blockDim.x; - if (index < n){ - tdata[index] = index - fdata[index] + totalFalses; - } -} -__global__ void computeB(int n, int *bdata, const int *idata, int ith){ - int index = threadIdx.x + blockIdx.x * blockDim.x; - if (index < n){ - bdata[index] = (idata[index]>> ith) & 1; - } -} + // assume the input and output are bits + __global__ void computeE(int n, int * edata, const int * bdata){ + int index = threadIdx.x + blockIdx.x * blockDim.x; + if (index < n) { + //edata[index] = ~(bdata[index]); + edata[index] = 1 - (bdata[index]); + //if (index ==0){ + // odata[index] = ~(0|idata[index]); + //} + //else { + // odata[index] = ~(idata[index-1]|idata[index]); + //} + } + } + __global__ void computeT(int n, int * tdata, const int * fdata, const int totalFalses){ + int index = threadIdx.x + blockIdx.x * blockDim.x; + if (index < n){ + tdata[index] = index - fdata[index] + totalFalses; + } + } + + __global__ void computeB(int n, int *bdata, const int *idata, int ith){ + int index = threadIdx.x + blockIdx.x * blockDim.x; + if (index < n){ + //bdata[index] = (idata[index]>> ith) & 1; + bdata[index] = (idata[index] & (1<>ith; + } + } + + __global__ void computeD(int n, int *ddata, const int * bdata, const int *tdata, const int * fdata){ + int index = threadIdx.x + blockIdx.x * blockDim.x; + if (index < n){ + //printf(" %d \n",bdata[index]); + ddata[index] = bdata[index] ? tdata[index] : fdata[index]; + } + } + + __global__ void scatter(int n, int *odata, const int *idata, const int * ddata){ + int index = threadIdx.x + blockIdx.x * blockDim.x; + if (index < n){ + //odata[index]= idata[ddata[index]]; + odata[ddata[index]]= idata[index]; + } + } + /** + * radix sort + */ + void sort(int n, int *odata, const int *idata, int msb) { + dim3 numblocks(std::ceil((double) n / blockSize)); + + int * idata_buff; + int * idata_buff2; + int * bdata_buff; + int * edata_buff; + int * fdata_buff; + int * tdata_buff; + int * ddata_buff; + + + cudaMalloc((void**)&idata_buff,n*sizeof(int)); + checkCUDAError("cudaMalloc-idata_buff- failed!"); + cudaMalloc((void**)&idata_buff2,n*sizeof(int)); + checkCUDAError("cudaMalloc-idata_buff2- failed!"); + cudaMalloc((void**)&bdata_buff,n*sizeof(int)); + checkCUDAError("cudaMalloc-bdata_buff- failed!"); + cudaMalloc((void**)&edata_buff,n*sizeof(int)); + checkCUDAError("cudaMalloc-edata_buff- failed!"); + cudaMalloc((void**)&fdata_buff,n*sizeof(int)); + checkCUDAError("cudaMalloc-fdata_buff- failed!"); + cudaMalloc((void**)&tdata_buff,n*sizeof(int)); + checkCUDAError("cudaMalloc-tdata_buff- failed!"); + cudaMalloc((void**)&ddata_buff,n*sizeof(int)); + checkCUDAError("cudaMalloc-ddata_buff- failed!"); + + /// CPU -->GPU + cudaMemcpy(idata_buff,idata,n*sizeof(int),cudaMemcpyHostToDevice); + checkCUDAError("cudaMemcpy-idata_buff-failed"); + + for (int i=0; i<= msb; i++){ + int totalFalses; + int totalFalses1 = 0; + int totalFalses2 = 0; + //find b array for each bit + computeB<<>>(n, bdata_buff, idata_buff, i); + computeE<<>>(n, edata_buff, bdata_buff); + StreamCompaction::Efficient::scan(n, fdata_buff, edata_buff); + + cudaMemcpy(&totalFalses1,edata_buff+n-1,sizeof(int),cudaMemcpyDeviceToHost); + cudaMemcpy(&totalFalses2,fdata_buff+n-1,sizeof(int),cudaMemcpyDeviceToHost); + totalFalses = totalFalses1 + totalFalses2; + + computeT<<>>(n, tdata_buff, fdata_buff, totalFalses); + computeD<<>>(n, ddata_buff, bdata_buff, tdata_buff, fdata_buff); + + + //scatter darray for this bit + scatter<<>>(n, idata_buff2, idata_buff, ddata_buff); + cudaMemcpy(idata_buff,idata_buff2,n*sizeof(int),cudaMemcpyDeviceToDevice); + checkCUDAError("cudaMemcpy-idata_buff-failed"); + } + + //GPU --> CPU + cudaMemcpy(odata,idata_buff,n*sizeof(int),cudaMemcpyDeviceToHost); + checkCUDAError("cudaMemcpy-odata-failed"); + //free + cudaFree(idata_buff); + cudaFree(idata_buff2); + cudaFree(bdata_buff); + cudaFree(tdata_buff); + cudaFree(fdata_buff); + cudaFree(edata_buff); + cudaFree(ddata_buff); + } -__global__ void computeD(int n, int *ddata, const int * bdata, const int *tdata, const int * fdata){ - int index = threadIdx.x + blockIdx.x * blockDim.x; - if (index < n){ - ddata[index] = bdata[index] ? tdata[index] : fdata[index]; } } - -__global__ void scatter(int n, int *odata, int *idata, const int * ddata){ - int index = threadIdx.x + blockIdx.x * blockDim.x; - if (index < n){ - odata[index]= idata[ddata[index]]; - } -} -/** - * radix sort - */ -void sort(int n, int *odata, const int *idata, int msb) { - dim3 numblocks(std::ceil((double) n / blockSize)); - - int * idata_buff; - int * idata_buff2; - int * bdata_buff; - int * edata_buff; - int * fdata_buff; - int * tdata_buff; - int * ddata_buff; - - - cudaMalloc((void**)&idata_buff,n*sizeof(int)); - checkCUDAError("cudaMalloc-idata_buff- failed!"); - cudaMalloc((void**)&idata_buff2,n*sizeof(int)); - checkCUDAError("cudaMalloc-idata_buff2- failed!"); - cudaMalloc((void**)&bdata_buff,n*sizeof(int)); - checkCUDAError("cudaMalloc-bdata_buff- failed!"); - cudaMalloc((void**)&edata_buff,n*sizeof(int)); - checkCUDAError("cudaMalloc-edata_buff- failed!"); - cudaMalloc((void**)&fdata_buff,n*sizeof(int)); - checkCUDAError("cudaMalloc-fdata_buff- failed!"); - cudaMalloc((void**)&tdata_buff,n*sizeof(int)); - checkCUDAError("cudaMalloc-tdata_buff- failed!"); - cudaMalloc((void**)&ddata_buff,n*sizeof(int)); - checkCUDAError("cudaMalloc-ddata_buff- failed!"); - - /// CPU -->GPU - cudaMemcpy(idata_buff,idata,n*sizeof(int),cudaMemcpyHostToDevice); - checkCUDAError("cudaMemcpy-idata_buff-failed"); - - for (int i=0; i< msb; i++){ - int totalFalses = 0; - - //find b array for each bit - computeB<<>>(n, bdata_buff, idata_buff, i); - computeE<<>>(n, edata_buff, bdata_buff); - StreamCompaction::Efficient::scan(n, fdata_buff, edata_buff); - - totalFalses = edata_buff[n-1] + fdata_buff[n-1]; - - computeT<<>>(n, tdata_buff, fdata_buff, totalFalses); - computeD<<>>(n, ddata_buff, bdata_buff, tdata_buff, fdata_buff); - - - //scatter darray for this bit - scatter<<>>(n, idata_buff2, ddata_buff, idata_buff); - cudaMemcpy(idata_buff,idata_buff2,n*sizeof(int),cudaMemcpyDeviceToDevice); - checkCUDAError("cudaMemcpy-idata_buff-failed"); - } - - //GPU --> CPU - cudaMemcpy(odata,idata_buff,n*sizeof(int),cudaMemcpyDeviceToHost); - checkCUDAError("cudaMemcpy-odata-failed"); - //free - cudaFree(idata_buff); - cudaFree(idata_buff2); - cudaFree(bdata_buff); - cudaFree(tdata_buff); - cudaFree(fdata_buff); - cudaFree(edata_buff); - cudaFree(ddata_buff); -} - -} -} diff --git a/stream_compaction/radixSort.h b/stream_compaction/radixSort.h index 4aa4817..fd69bbe 100644 --- a/stream_compaction/radixSort.h +++ b/stream_compaction/radixSort.h @@ -3,6 +3,6 @@ namespace StreamCompaction { namespace RadixSort { void sort(int n, int *odata, const int *idata, int msb); - + } } From 6ddae5faf2b558f0571ddd4989ec067c9ee08ba6 Mon Sep 17 00:00:00 2001 From: dengxianga Date: Tue, 27 Sep 2016 16:16:43 -0400 Subject: [PATCH 09/21] sort tested --- src/main.cpp | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 3828fc8..71a9dbc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -13,7 +13,7 @@ #include #include #include "testing_helpers.hpp" - +#include int main(int argc, char* argv[]) { const int SIZE = 1 << 8; const int NPOT = SIZE - 3; @@ -126,13 +126,38 @@ int main(int argc, char* argv[]) { printf("*****************************\n"); printf("** RADIX SORT TESTS **\n"); printf("*****************************\n"); - genArray(SIZE - 1, a, 50); // Leave a 0 at the end to test that edge case + genArray(SIZE - 1, a, 200); // Leave a 0 at the end to test that edge case a[SIZE - 1] = 0; + printDesc("Array to be sorted power of 2"); printArray(SIZE, a, true); zeroArray(SIZE, b); - printDesc("RADIX SORT power-of-two"); + printDesc("RADIX SORT POT"); int msb=31; - printf("size of int is %d bits\n", sizeof(int)*CHAR_BIT); + //printf("size of int is %d bits\n", sizeof(int)*CHAR_BIT); StreamCompaction::RadixSort::sort(SIZE, b, a, msb); printArray(SIZE, b, true); + + printDesc("C++ SORT POT"); + memcpy(c, a, SIZE*sizeof(int)); + std::sort(c,c+SIZE); + printArray(SIZE, c, true); + printCmpResult(SIZE, b, c); + + genArray(SIZE - 1, a, 2000); // Leave a 0 at the end to test that edge case + a[SIZE - 1] = 0; + printDesc("Array to be sorted not power of 2"); + printArray(SIZE, a, true); + zeroArray(SIZE, b); + zeroArray(SIZE, c); + printDesc("RADIX SORT NPOT"); + //printf("size of int is %d bits\n", sizeof(int)*CHAR_BIT); + StreamCompaction::RadixSort::sort(NPOT, b, a, msb); + printArray(NPOT, b, true); + + printDesc("C++ SORT NPOT"); + memcpy(c, a, SIZE*sizeof(int)); + std::sort(c,c+NPOT); + printArray(NPOT, c, true); + printCmpResult(NPOT, b, c); + } From b67f25c7a77acf4c6d008da85c186f207a5bff01 Mon Sep 17 00:00:00 2001 From: dengxianga Date: Tue, 27 Sep 2016 17:43:19 -0400 Subject: [PATCH 10/21] timer sucks --- src/main.cpp | 23 +++++++++++++++-------- stream_compaction/efficient.cu | 26 +++++++++++++++++++++++++- stream_compaction/efficient.h | 3 ++- stream_compaction/thrust.cu | 9 ++++++++- stream_compaction/thrust.h | 2 +- 5 files changed, 51 insertions(+), 12 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 71a9dbc..a44e753 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,8 +1,8 @@ /** * @file main.cpp * @brief Stream compaction test program - * @authors Kai Ninomiya - * @date 2015 + * @authors Kai Ninomiya, Xiang Deng + * @date 2015, 2016 * @copyright University of Pennsylvania */ @@ -18,7 +18,7 @@ int main(int argc, char* argv[]) { const int SIZE = 1 << 8; const int NPOT = SIZE - 3; int a[SIZE], b[SIZE], c[SIZE]; - + float milscs; // Scan tests printf("\n"); @@ -53,29 +53,36 @@ int main(int argc, char* argv[]) { //printArray(SIZE, c, true); printCmpResult(NPOT, b, c); + + zeroArray(SIZE, c); printDesc("work-efficient scan, power-of-two"); - StreamCompaction::Efficient::scan(SIZE, c, a); + milscs = StreamCompaction::Efficient::scan(SIZE, c, a); //printArray(SIZE, c, true); - printCmpResult(SIZE, b, c); + printCmpResult(SIZE, b, c); + printf("time lapsed %f ms\n", milscs); zeroArray(SIZE, c); printDesc("work-efficient scan, non-power-of-two"); - StreamCompaction::Efficient::scan(NPOT, c, a); + milscs = StreamCompaction::Efficient::scan(NPOT, c, a); //printArray(NPOT, c, true); printCmpResult(NPOT, b, c); + printf("time lapsed %f ms\n", milscs); + zeroArray(SIZE, c); printDesc("thrust scan, power-of-two"); - StreamCompaction::Thrust::scan(SIZE, c, a); + milscs = StreamCompaction::Thrust::scan(SIZE, c, a); //printArray(SIZE, c, true); printCmpResult(SIZE, b, c); + printf("time lapsed %f ms\n", milscs); zeroArray(SIZE, c); printDesc("thrust scan, non-power-of-two"); - StreamCompaction::Thrust::scan(NPOT, c, a); + milscs = StreamCompaction::Thrust::scan(NPOT, c, a); //printArray(NPOT, c, true); printCmpResult(NPOT, b, c); + printf("time lapsed %f ms\n", milscs); printf("\n"); printf("*****************************\n"); diff --git a/stream_compaction/efficient.cu b/stream_compaction/efficient.cu index e6014fd..891012d 100644 --- a/stream_compaction/efficient.cu +++ b/stream_compaction/efficient.cu @@ -37,7 +37,13 @@ __global__ void downSweep(int offset, int n, int *idata){ /** * Performs prefix-sum (aka scan) on idata, storing the result into odata. */ -void scan(int n, int *odata, const int *idata) { +float scan(int n, int *odata, const int *idata) { + cudaEvent_t t0, t2; + cudaEventCreate(&t0); + cudaEventCreate(&t2); + + float milliscs = 0.0f; + float tmpt; // TODO //printf("TODO\n"); int levels_max = ilog2ceil(n); @@ -55,23 +61,41 @@ void scan(int n, int *odata, const int *idata) { /// CPU -->GPU cudaMemcpy(idata_buff,idata,n*sizeof(int),cudaMemcpyHostToDevice); checkCUDAError("cudaMemcpy-idata_buff-failed"); + + cudaEventRecord(t0); + //upsweep for (int level=0; level <= levels_max-1; level++){ upSweep<<>>(1<=0 ; level--){ downSweep<<>>(1< CPU cudaMemcpy(odata, idata_buff, n*sizeof(int),cudaMemcpyDeviceToHost); checkCUDAError("cudaMemcpy-odata-failed"); cudaFree(idata_buff); + return milliscs; } /** diff --git a/stream_compaction/efficient.h b/stream_compaction/efficient.h index 395ba10..ac1968f 100644 --- a/stream_compaction/efficient.h +++ b/stream_compaction/efficient.h @@ -2,8 +2,9 @@ namespace StreamCompaction { namespace Efficient { - void scan(int n, int *odata, const int *idata); + float scan(int n, int *odata, const int *idata); int compact(int n, int *odata, const int *idata); + } } diff --git a/stream_compaction/thrust.cu b/stream_compaction/thrust.cu index 58be000..f3225e4 100644 --- a/stream_compaction/thrust.cu +++ b/stream_compaction/thrust.cu @@ -5,6 +5,7 @@ #include #include "common.h" #include "thrust.h" +#include namespace StreamCompaction { namespace Thrust { @@ -12,11 +13,17 @@ namespace Thrust { /** * Performs prefix-sum (aka scan) on idata, storing the result into odata. */ -void scan(int n, int *odata, const int *idata) { +float scan(int n, int *odata, const int *idata) { // 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()); + + + for (int i=0;i <10000 ; i++){ thrust::exclusive_scan(idata , idata +n , odata); + } + + return 0; } } diff --git a/stream_compaction/thrust.h b/stream_compaction/thrust.h index 06707f3..d6182b2 100644 --- a/stream_compaction/thrust.h +++ b/stream_compaction/thrust.h @@ -2,6 +2,6 @@ namespace StreamCompaction { namespace Thrust { - void scan(int n, int *odata, const int *idata); + float scan(int n, int *odata, const int *idata); } } From f8ccf31af63b10b9b21af1549238d4f97fd805b2 Mon Sep 17 00:00:00 2001 From: XIANG DENG Date: Tue, 27 Sep 2016 19:09:38 -0400 Subject: [PATCH 11/21] timing --- src/main.cpp | 61 ++++++++++++++++++++++++++++------ stream_compaction/efficient.cu | 25 +++++++++++--- stream_compaction/efficient.h | 2 +- stream_compaction/naive.cu | 24 +++++++++++-- stream_compaction/naive.h | 2 +- stream_compaction/thrust.cu | 13 +++++--- 6 files changed, 102 insertions(+), 25 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index a44e753..2f54e7e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -14,11 +14,15 @@ #include #include "testing_helpers.hpp" #include +#include + int main(int argc, char* argv[]) { - const int SIZE = 1 << 8; + const int SIZE = 1 << 12; const int NPOT = SIZE - 3; int a[SIZE], b[SIZE], c[SIZE]; float milscs; + int nitercpu = 10000; + // Scan tests printf("\n"); @@ -32,27 +36,42 @@ int main(int argc, char* argv[]) { zeroArray(SIZE, b); printDesc("cpu scan, power-of-two"); - StreamCompaction::CPU::scan(SIZE, b, a); + auto begin = std::chrono::high_resolution_clock::now(); + for (int i = 1; i < nitercpu; i++){ + StreamCompaction::CPU::scan(SIZE, b, a); + } + auto end = std::chrono::high_resolution_clock::now(); + milscs = std::chrono::duration_cast(end - begin).count() / nitercpu; printArray(SIZE, b, true); + printf("time lapsed %f ms\n", milscs); + zeroArray(SIZE, c); printDesc("cpu scan, non-power-of-two"); - StreamCompaction::CPU::scan(NPOT, c, a); + begin = std::chrono::high_resolution_clock::now(); + for (int i = 1; i < nitercpu; i++){ + StreamCompaction::CPU::scan(NPOT, c, a); + } + end = std::chrono::high_resolution_clock::now(); + milscs = (float)std::chrono::duration_cast(end - begin).count() / nitercpu; printArray(NPOT, b, true); printCmpResult(NPOT, b, c); + printf("time lapsed %f ms\n", milscs); + zeroArray(SIZE, c); printDesc("naive scan, power-of-two"); - StreamCompaction::Naive::scan(SIZE, c, a); + milscs = StreamCompaction::Naive::scan(SIZE, c, a); //printArray(SIZE, c, true); printCmpResult(SIZE, b, c); + printf("time lapsed %f ms\n", milscs); zeroArray(SIZE, c); printDesc("naive scan, non-power-of-two"); - StreamCompaction::Naive::scan(NPOT, c, a); + milscs = StreamCompaction::Naive::scan(NPOT, c, a); //printArray(SIZE, c, true); printCmpResult(NPOT, b, c); - + printf("time lapsed %f ms\n", milscs); zeroArray(SIZE, c); @@ -99,35 +118,55 @@ int main(int argc, char* argv[]) { zeroArray(SIZE, b); printDesc("cpu compact without scan, power-of-two"); - count = StreamCompaction::CPU::compactWithoutScan(SIZE, b, a); + begin = std::chrono::high_resolution_clock::now(); + for (int i = 1; i < nitercpu; i++){ + count = StreamCompaction::CPU::compactWithoutScan(SIZE, b, a); + } + end = std::chrono::high_resolution_clock::now(); + milscs = (float)std::chrono::duration_cast(end - begin).count() / nitercpu; expectedCount = count; printArray(count, b, true); printCmpLenResult(count, expectedCount, b, b); + printf("time lapsed %f ms\n", milscs); zeroArray(SIZE, c); printDesc("cpu compact without scan, non-power-of-two"); - count = StreamCompaction::CPU::compactWithoutScan(NPOT, c, a); + begin = std::chrono::high_resolution_clock::now(); + for (int i = 1; i < nitercpu; i++){ + count = StreamCompaction::CPU::compactWithoutScan(NPOT, c, a); + } + end = std::chrono::high_resolution_clock::now(); expectedNPOT = count; printArray(count, c, true); printCmpLenResult(count, expectedNPOT, b, c); + milscs = (float)std::chrono::duration_cast(end - begin).count() / nitercpu; + printf("time lapsed %f ms\n", milscs); zeroArray(SIZE, c); printDesc("cpu compact with scan"); - count = StreamCompaction::CPU::compactWithScan(SIZE, c, a); + begin = std::chrono::high_resolution_clock::now(); + for (int i = 1; i < nitercpu; i++){ + count = StreamCompaction::CPU::compactWithScan(SIZE, c, a); + } + end = std::chrono::high_resolution_clock::now(); printArray(count, c, true); printCmpLenResult(count, expectedCount, b, c); + milscs = (float)std::chrono::duration_cast(end - begin).count() / nitercpu; + printf("time lapsed %f ms\n", milscs); zeroArray(SIZE, c); printDesc("work-efficient compact, power-of-two"); - count = StreamCompaction::Efficient::compact(SIZE, c, a); + count = StreamCompaction::Efficient::compact(SIZE, c, a, milscs); //printArray(count, c, true); printCmpLenResult(count, expectedCount, b, c); + printf("time lapsed %f ms\n", milscs); zeroArray(SIZE, c); printDesc("work-efficient compact, non-power-of-two"); - count = StreamCompaction::Efficient::compact(NPOT, c, a); + count = StreamCompaction::Efficient::compact(NPOT, c, a, milscs); //printArray(count, c, true); printCmpLenResult(count, expectedNPOT, b, c); + printf("time lapsed %f ms\n", milscs); printf("\n"); printf("*****************************\n"); diff --git a/stream_compaction/efficient.cu b/stream_compaction/efficient.cu index 891012d..9ff1a07 100644 --- a/stream_compaction/efficient.cu +++ b/stream_compaction/efficient.cu @@ -86,8 +86,8 @@ float scan(int n, int *odata, const int *idata) { downSweep<<>>(1<>> ( n, bool_buff, idata_buff); scan (n, indices_buff, bool_buff); StreamCompaction::Common::kernScatter<<>>( n, odata_buff, idata_buff, bool_buff, indices_buff); - + + cudaEventRecord(t2); + cudaEventSynchronize(t2); + cudaEventElapsedTime(&tmpt, t0, t2); + milliscs += tmpt; //GPU-->CPU cudaMemcpy(odata,odata_buff,n*sizeof(int),cudaMemcpyDeviceToHost); @@ -145,11 +158,13 @@ int compact(int n, int *odata, const int *idata) { // n_remaing+=bool_buff[i]; //} cudaMemcpy(&n_remaing,indices_buff+n-1,sizeof(int),cudaMemcpyDeviceToHost); + int extra; + cudaMemcpy(&extra, bool_buff + n - 1, sizeof(int), cudaMemcpyDeviceToHost); cudaFree(idata_buff); cudaFree(odata_buff); cudaFree(bool_buff); cudaFree(indices_buff); - return n_remaing; + return n_remaing + extra; } } diff --git a/stream_compaction/efficient.h b/stream_compaction/efficient.h index ac1968f..bd669eb 100644 --- a/stream_compaction/efficient.h +++ b/stream_compaction/efficient.h @@ -4,7 +4,7 @@ namespace StreamCompaction { namespace Efficient { float scan(int n, int *odata, const int *idata); - int compact(int n, int *odata, const int *idata); + int compact(int n, int *odata, const int *idata, float & milliscs); } } diff --git a/stream_compaction/naive.cu b/stream_compaction/naive.cu index 77ef054..c23bd44 100644 --- a/stream_compaction/naive.cu +++ b/stream_compaction/naive.cu @@ -34,8 +34,13 @@ __global__ void excludesiveShift(int n, int *odata, int *idata){ /** * Performs prefix-sum (aka scan) on idata, storing the result into odata. */ -void scan(int n, int *odata, const int *idata) { - +float scan(int n, int *odata, const int *idata) { + cudaEvent_t t0, t2; + cudaEventCreate(&t0); + cudaEventCreate(&t2); + + float milliscs = 0.0f; + float tmpt; //dim3 numblocks(std::ceil((double) n / blockSize)); dim3 numblocks((n + blockSize - 1) / blockSize); int* idata_buff; @@ -59,18 +64,33 @@ void scan(int n, int *odata, const int *idata) { else { offset = 2 << (level -2); } + + cudaEventRecord(t0); // for the given level, all threads read from idata_buff scan <<>>(offset, n, odata_buff, idata_buff); + cudaEventRecord(t2); + cudaEventSynchronize(t2); + cudaEventElapsedTime(&tmpt, t0, t2); + milliscs += tmpt; + //std::swap(idata_buff, odata_buff); // odata_buff --> idata_buff for next iteration cudaMemcpy(idata_buff, odata_buff, sizeof(int)*n, cudaMemcpyDeviceToDevice); } + + cudaEventRecord(t0); excludesiveShift<<>>(n, odata_buff, idata_buff); + cudaEventRecord(t2); + cudaEventSynchronize(t2); + cudaEventElapsedTime(&tmpt, t0, t2); + milliscs += tmpt; //GPU --> CPU cudaMemcpy(odata, odata_buff, sizeof(int)*n, cudaMemcpyDeviceToHost); cudaFree(idata_buff); cudaFree(odata_buff); + + return milliscs; } } diff --git a/stream_compaction/naive.h b/stream_compaction/naive.h index 21152d6..7090b46 100644 --- a/stream_compaction/naive.h +++ b/stream_compaction/naive.h @@ -2,6 +2,6 @@ namespace StreamCompaction { namespace Naive { - void scan(int n, int *odata, const int *idata); + float scan(int n, int *odata, const int *idata); } } diff --git a/stream_compaction/thrust.cu b/stream_compaction/thrust.cu index f3225e4..38c8db5 100644 --- a/stream_compaction/thrust.cu +++ b/stream_compaction/thrust.cu @@ -6,6 +6,7 @@ #include "common.h" #include "thrust.h" #include +#include namespace StreamCompaction { namespace Thrust { @@ -18,12 +19,14 @@ float scan(int n, int *odata, const int *idata) { // example: for device_vectors dv_in and dv_out: // thrust::exclusive_scan(dv_in.begin(), dv_in.end(), dv_out.begin()); - - for (int i=0;i <10000 ; i++){ + auto begin = std::chrono::high_resolution_clock::now(); + thrust::exclusive_scan(idata , idata +n , odata); - } - - return 0; + + auto end = std::chrono::high_resolution_clock::now(); + float ns = std::chrono::duration_cast(end - begin).count(); + + return ns; } } From c3cf995aa51cd50acb71a0ad8758b1fb2858930a Mon Sep 17 00:00:00 2001 From: XIANG DENG Date: Tue, 27 Sep 2016 21:17:20 -0400 Subject: [PATCH 12/21] , float &milliscs --- src/main.cpp | 20 ++-- stream_compaction/CMakeLists.txt | 2 +- stream_compaction/common.h | 2 +- stream_compaction/efficient.cu | 2 +- stream_compaction/naive.cu | 154 +++++++++++++++---------------- stream_compaction/radixSort.cu | 2 +- 6 files changed, 91 insertions(+), 91 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 2f54e7e..a3de601 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -15,13 +15,13 @@ #include "testing_helpers.hpp" #include #include - +#include int main(int argc, char* argv[]) { - const int SIZE = 1 << 12; + const int SIZE = 1 << 16; const int NPOT = SIZE - 3; int a[SIZE], b[SIZE], c[SIZE]; float milscs; - int nitercpu = 10000; + int nitercpu = 1000; // Scan tests @@ -37,11 +37,11 @@ int main(int argc, char* argv[]) { zeroArray(SIZE, b); printDesc("cpu scan, power-of-two"); auto begin = std::chrono::high_resolution_clock::now(); - for (int i = 1; i < nitercpu; i++){ + for (int i = 0; i < nitercpu; i++){ StreamCompaction::CPU::scan(SIZE, b, a); } - auto end = std::chrono::high_resolution_clock::now(); - milscs = std::chrono::duration_cast(end - begin).count() / nitercpu; + auto end = std::chrono::high_resolution_clock::now(); + milscs = (float)std::chrono::duration_cast(end - begin).count() /1000000/ (float)nitercpu; printArray(SIZE, b, true); printf("time lapsed %f ms\n", milscs); @@ -49,7 +49,7 @@ int main(int argc, char* argv[]) { zeroArray(SIZE, c); printDesc("cpu scan, non-power-of-two"); begin = std::chrono::high_resolution_clock::now(); - for (int i = 1; i < nitercpu; i++){ + for (int i = 0; i < nitercpu; i++){ StreamCompaction::CPU::scan(NPOT, c, a); } end = std::chrono::high_resolution_clock::now(); @@ -119,7 +119,7 @@ int main(int argc, char* argv[]) { zeroArray(SIZE, b); printDesc("cpu compact without scan, power-of-two"); begin = std::chrono::high_resolution_clock::now(); - for (int i = 1; i < nitercpu; i++){ + for (int i = 0; i < nitercpu; i++){ count = StreamCompaction::CPU::compactWithoutScan(SIZE, b, a); } end = std::chrono::high_resolution_clock::now(); @@ -132,7 +132,7 @@ int main(int argc, char* argv[]) { zeroArray(SIZE, c); printDesc("cpu compact without scan, non-power-of-two"); begin = std::chrono::high_resolution_clock::now(); - for (int i = 1; i < nitercpu; i++){ + for (int i = 0; i < nitercpu; i++){ count = StreamCompaction::CPU::compactWithoutScan(NPOT, c, a); } end = std::chrono::high_resolution_clock::now(); @@ -145,7 +145,7 @@ int main(int argc, char* argv[]) { zeroArray(SIZE, c); printDesc("cpu compact with scan"); begin = std::chrono::high_resolution_clock::now(); - for (int i = 1; i < nitercpu; i++){ + for (int i = 0; i < nitercpu; i++){ count = StreamCompaction::CPU::compactWithScan(SIZE, c, a); } end = std::chrono::high_resolution_clock::now(); diff --git a/stream_compaction/CMakeLists.txt b/stream_compaction/CMakeLists.txt index 3dfb146..7c8aae8 100644 --- a/stream_compaction/CMakeLists.txt +++ b/stream_compaction/CMakeLists.txt @@ -15,5 +15,5 @@ set(SOURCE_FILES cuda_add_library(stream_compaction ${SOURCE_FILES} - OPTIONS -arch=sm_20 + OPTIONS -arch=sm_30 ) diff --git a/stream_compaction/common.h b/stream_compaction/common.h index 4f52663..f0962b7 100644 --- a/stream_compaction/common.h +++ b/stream_compaction/common.h @@ -3,7 +3,7 @@ #include #include #include - +#define blockSize 128 #define FILENAME (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) #define checkCUDAError(msg) checkCUDAErrorFn(msg, FILENAME, __LINE__) diff --git a/stream_compaction/efficient.cu b/stream_compaction/efficient.cu index 9ff1a07..0c4a5e7 100644 --- a/stream_compaction/efficient.cu +++ b/stream_compaction/efficient.cu @@ -5,7 +5,7 @@ namespace StreamCompaction { namespace Efficient { -#define blockSize 128 + // TODO: __global__ __global__ void upSweep(int offset, int n, int *idata){ int index = threadIdx.x + blockIdx.x*blockDim.x; diff --git a/stream_compaction/naive.cu b/stream_compaction/naive.cu index c23bd44..89b356f 100644 --- a/stream_compaction/naive.cu +++ b/stream_compaction/naive.cu @@ -6,92 +6,92 @@ #include "naive.h" namespace StreamCompaction { -namespace Naive { + namespace Naive { -#define blockSize 128 -//__global__ -__global__ void scan(int offset, int n, int *odata, const int *idata) { - int index = threadIdx.x + blockIdx.x*blockDim.x; - if (index >=n) return; + + //__global__ + __global__ void scan(int offset, int n, int *odata, const int *idata) { + int index = threadIdx.x + blockIdx.x*blockDim.x; + if (index >= n) return; - if (index >= offset){ - odata[index] = idata[index] + idata[index-offset]; - } - else{ - odata[index] = idata[index]; - } -} -__global__ void excludesiveShift(int n, int *odata, int *idata){ - int index = threadIdx.x + blockIdx.x* blockDim.x; - if (index>=n) return; - if (index>=1){ - odata[index]= idata[index-1] ; - } - else { - odata[index]= 0; - } -} -/** - * Performs prefix-sum (aka scan) on idata, storing the result into odata. - */ -float scan(int n, int *odata, const int *idata) { - cudaEvent_t t0, t2; - cudaEventCreate(&t0); - cudaEventCreate(&t2); + if (index >= offset){ + odata[index] = idata[index] + idata[index - offset]; + } + else{ + odata[index] = idata[index]; + } + } + __global__ void excludesiveShift(int n, int *odata, int *idata){ + int index = threadIdx.x + blockIdx.x* blockDim.x; + if (index >= n) return; + if (index >= 1){ + odata[index] = idata[index - 1]; + } + else { + odata[index] = 0; + } + } + /** + * Performs prefix-sum (aka scan) on idata, storing the result into odata. + */ + float scan(int n, int *odata, const int *idata) { + cudaEvent_t t0, t2; + cudaEventCreate(&t0); + cudaEventCreate(&t2); - float milliscs = 0.0f; - float tmpt; - //dim3 numblocks(std::ceil((double) n / blockSize)); - dim3 numblocks((n + blockSize - 1) / blockSize); - int* idata_buff; - int* odata_buff; + float milliscs = 0.0f; + float tmpt; + //dim3 numblocks(std::ceil((double) n / blockSize)); + dim3 numblocks((n + blockSize - 1) / blockSize); + int* idata_buff; + int* odata_buff; - cudaMalloc((void**)&idata_buff, n*sizeof(int)); - checkCUDAError("cudaMalloc-idata_buff- failed!"); - cudaMalloc((void**)&odata_buff, n*sizeof(int)); - checkCUDAError("cudaMalloc-odata_buff-failed!"); + cudaMalloc((void**)&idata_buff, n*sizeof(int)); + checkCUDAError("cudaMalloc-idata_buff- failed!"); + cudaMalloc((void**)&odata_buff, n*sizeof(int)); + checkCUDAError("cudaMalloc-odata_buff-failed!"); - /// CPU -->GPU - cudaMemcpy(idata_buff,idata,sizeof(int)*n,cudaMemcpyHostToDevice); - cudaMemcpy(odata_buff,idata,sizeof(int)*n,cudaMemcpyHostToDevice); + /// CPU -->GPU + cudaMemcpy(idata_buff, idata, sizeof(int)*n, cudaMemcpyHostToDevice); + cudaMemcpy(odata_buff, idata, sizeof(int)*n, cudaMemcpyHostToDevice); - - for (int level= 1; level <= ilog2ceil(n); level++) { - int offset; - if (level==1){ - offset = 1; - } - else { - offset = 2 << (level -2); - } - cudaEventRecord(t0); - // for the given level, all threads read from idata_buff - scan <<>>(offset, n, odata_buff, idata_buff); - cudaEventRecord(t2); - cudaEventSynchronize(t2); - cudaEventElapsedTime(&tmpt, t0, t2); - milliscs += tmpt; + for (int level = 1; level <= ilog2ceil(n); level++) { + int offset; + if (level == 1){ + offset = 1; + } + else { + offset = 2 << (level - 2); + } - //std::swap(idata_buff, odata_buff); - // odata_buff --> idata_buff for next iteration - cudaMemcpy(idata_buff, odata_buff, sizeof(int)*n, cudaMemcpyDeviceToDevice); - } + cudaEventRecord(t0); + // for the given level, all threads read from idata_buff + scan << > >(offset, n, odata_buff, idata_buff); + cudaEventRecord(t2); + cudaEventSynchronize(t2); + cudaEventElapsedTime(&tmpt, t0, t2); + milliscs += tmpt; - cudaEventRecord(t0); - excludesiveShift<<>>(n, odata_buff, idata_buff); - cudaEventRecord(t2); - cudaEventSynchronize(t2); - cudaEventElapsedTime(&tmpt, t0, t2); - milliscs += tmpt; + //std::swap(idata_buff, odata_buff); + // odata_buff --> idata_buff for next iteration + cudaMemcpy(idata_buff, odata_buff, sizeof(int)*n, cudaMemcpyDeviceToDevice); + } - //GPU --> CPU - cudaMemcpy(odata, odata_buff, sizeof(int)*n, cudaMemcpyDeviceToHost); - cudaFree(idata_buff); - cudaFree(odata_buff); + cudaEventRecord(t0); + excludesiveShift << > >(n, odata_buff, idata_buff); + cudaEventRecord(t2); + cudaEventSynchronize(t2); + cudaEventElapsedTime(&tmpt, t0, t2); + milliscs += tmpt; - return milliscs; -} + //GPU --> CPU + cudaMemcpy(odata, odata_buff, sizeof(int)*n, cudaMemcpyDeviceToHost); + cudaFree(idata_buff); + cudaFree(odata_buff); -} -} + return milliscs; + } + + } +} \ No newline at end of file diff --git a/stream_compaction/radixSort.cu b/stream_compaction/radixSort.cu index 99cf51f..ecaf03e 100644 --- a/stream_compaction/radixSort.cu +++ b/stream_compaction/radixSort.cu @@ -6,7 +6,7 @@ namespace StreamCompaction { namespace RadixSort { -#define blockSize 128 + //int getNbit(int input, int nth){ // return (input >> nth) & 1; From aae65990176a4dbc0f1c89c07199ebd7576a465d Mon Sep 17 00:00:00 2001 From: dengxianga Date: Tue, 27 Sep 2016 23:15:37 -0400 Subject: [PATCH 13/21] ![](images/1.PNG) --- README.md | 17 +++++++++++---- images/1.PNG | Bin 0 -> 16823 bytes images/2.PNG | Bin 0 -> 22330 bytes images/3.PNG | Bin 0 -> 17246 bytes images/4.PNG | Bin 0 -> 15642 bytes images/5.PNG | Bin 0 -> 19989 bytes images/6.PNG | Bin 0 -> 15041 bytes src/main.cpp | 36 ++++++++++++++++++++----------- stream_compaction/CMakeLists.txt | 2 +- stream_compaction/common.h | 2 +- stream_compaction/thrust.cu | 14 ++++++------ stream_compaction/thrust.h | 2 +- 12 files changed, 46 insertions(+), 27 deletions(-) create mode 100644 images/1.PNG create mode 100644 images/2.PNG create mode 100644 images/3.PNG create mode 100644 images/4.PNG create mode 100644 images/5.PNG create mode 100644 images/6.PNG diff --git a/README.md b/README.md index b71c458..e328792 100644 --- a/README.md +++ b/README.md @@ -3,11 +3,20 @@ CUDA Stream Compaction **University of Pennsylvania, CIS 565: GPU Programming and Architecture, Project 2** -* (TODO) YOUR NAME HERE -* Tested on: (TODO) Windows 22, i7-2222 @ 2.22GHz 22GB, GTX 222 222MB (Moore 2222 Lab) +* Xiang Deng +* Tested on: Windows 10-Home, i7-6700U @ 2.6GHz 16GB, GTX 1060 6GB (Personal Computer) ### (TODO: Your README) -Include analysis, etc. (Remember, this is public, so don't put -anything here that you don't want to share with the world.) +![](images/1.PNG) + +![](images/2.PNG) + +![](images/3.PNG) + +![](images/4.PNG) + +![](images/5.PNG) + +![](images/6.PNG) diff --git a/images/1.PNG b/images/1.PNG new file mode 100644 index 0000000000000000000000000000000000000000..49c88d39920cd2197525a04354f9b6474e454475 GIT binary patch literal 16823 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 literal 0 HcmV?d00001 diff --git a/images/2.PNG b/images/2.PNG new file mode 100644 index 0000000000000000000000000000000000000000..a8f5d6b350d5bbb16582609d17ba2140a9f64a75 GIT binary patch literal 22330 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 mA6pjh$N#0{QLyTx_RH_ literal 0 HcmV?d00001 diff --git a/images/3.PNG b/images/3.PNG new file mode 100644 index 0000000000000000000000000000000000000000..6af9f611d0a3de4cc3ef7e3a4165f984934576a6 GIT binary patch literal 17246 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 literal 0 HcmV?d00001 diff --git a/images/4.PNG b/images/4.PNG new file mode 100644 index 0000000000000000000000000000000000000000..602d89a896a2855ebb9d68f83779cc062896120f GIT binary patch literal 15642 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 E0cBYpB>(^b literal 0 HcmV?d00001 diff --git a/images/5.PNG b/images/5.PNG new file mode 100644 index 0000000000000000000000000000000000000000..15f4aa1942a111186578d1fe07172c1f363e3e87 GIT binary patch literal 19989 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 literal 0 HcmV?d00001 diff --git a/images/6.PNG b/images/6.PNG new file mode 100644 index 0000000000000000000000000000000000000000..0484d0e3da964ec95a37034439faa09c7a8756c2 GIT binary patch literal 15041 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 bGPUJC$2Fh76boEC1!+Eds#^Zg>h=ExcC_eN literal 0 HcmV?d00001 diff --git a/src/main.cpp b/src/main.cpp index a3de601..78c1c05 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -41,7 +41,7 @@ int main(int argc, char* argv[]) { StreamCompaction::CPU::scan(SIZE, b, a); } auto end = std::chrono::high_resolution_clock::now(); - milscs = (float)std::chrono::duration_cast(end - begin).count() /1000000/ (float)nitercpu; + milscs = (float)std::chrono::duration_cast(end - begin).count() / (float)nitercpu; printArray(SIZE, b, true); printf("time lapsed %f ms\n", milscs); @@ -62,14 +62,14 @@ int main(int argc, char* argv[]) { zeroArray(SIZE, c); printDesc("naive scan, power-of-two"); milscs = StreamCompaction::Naive::scan(SIZE, c, a); - //printArray(SIZE, c, true); + printArray(SIZE, c, true); printCmpResult(SIZE, b, c); printf("time lapsed %f ms\n", milscs); zeroArray(SIZE, c); printDesc("naive scan, non-power-of-two"); milscs = StreamCompaction::Naive::scan(NPOT, c, a); - //printArray(SIZE, c, true); + printArray(SIZE, c, true); printCmpResult(NPOT, b, c); printf("time lapsed %f ms\n", milscs); @@ -77,29 +77,39 @@ int main(int argc, char* argv[]) { zeroArray(SIZE, c); printDesc("work-efficient scan, power-of-two"); milscs = StreamCompaction::Efficient::scan(SIZE, c, a); - //printArray(SIZE, c, true); + printArray(SIZE, c, true); printCmpResult(SIZE, b, c); printf("time lapsed %f ms\n", milscs); zeroArray(SIZE, c); printDesc("work-efficient scan, non-power-of-two"); milscs = StreamCompaction::Efficient::scan(NPOT, c, a); - //printArray(NPOT, c, true); + printArray(NPOT, c, true); printCmpResult(NPOT, b, c); printf("time lapsed %f ms\n", milscs); zeroArray(SIZE, c); printDesc("thrust scan, power-of-two"); - milscs = StreamCompaction::Thrust::scan(SIZE, c, a); - //printArray(SIZE, c, true); + begin = std::chrono::high_resolution_clock::now(); + for (int i = 0; i < nitercpu; i++){ + StreamCompaction::Thrust::scan(SIZE, c, a); + } + end = std::chrono::high_resolution_clock::now(); + milscs = (float)std::chrono::duration_cast(end - begin).count() / nitercpu; + printArray(SIZE, c, true); printCmpResult(SIZE, b, c); printf("time lapsed %f ms\n", milscs); zeroArray(SIZE, c); printDesc("thrust scan, non-power-of-two"); - milscs = StreamCompaction::Thrust::scan(NPOT, c, a); - //printArray(NPOT, c, true); + begin = std::chrono::high_resolution_clock::now(); + for (int i = 0; i < nitercpu; i++){ + StreamCompaction::Thrust::scan(NPOT, c, a); + } + end = std::chrono::high_resolution_clock::now(); + milscs = (float)std::chrono::duration_cast(end - begin).count() / nitercpu; + printArray(NPOT, c, true); printCmpResult(NPOT, b, c); printf("time lapsed %f ms\n", milscs); @@ -157,14 +167,14 @@ int main(int argc, char* argv[]) { zeroArray(SIZE, c); printDesc("work-efficient compact, power-of-two"); count = StreamCompaction::Efficient::compact(SIZE, c, a, milscs); - //printArray(count, c, true); + printArray(count, c, true); printCmpLenResult(count, expectedCount, b, c); printf("time lapsed %f ms\n", milscs); zeroArray(SIZE, c); printDesc("work-efficient compact, non-power-of-two"); count = StreamCompaction::Efficient::compact(NPOT, c, a, milscs); - //printArray(count, c, true); + printArray(count, c, true); printCmpLenResult(count, expectedNPOT, b, c); printf("time lapsed %f ms\n", milscs); @@ -179,7 +189,7 @@ int main(int argc, char* argv[]) { zeroArray(SIZE, b); printDesc("RADIX SORT POT"); int msb=31; - //printf("size of int is %d bits\n", sizeof(int)*CHAR_BIT); + printf("size of int is %d bits\n", sizeof(int)*CHAR_BIT); StreamCompaction::RadixSort::sort(SIZE, b, a, msb); printArray(SIZE, b, true); @@ -196,7 +206,7 @@ int main(int argc, char* argv[]) { zeroArray(SIZE, b); zeroArray(SIZE, c); printDesc("RADIX SORT NPOT"); - //printf("size of int is %d bits\n", sizeof(int)*CHAR_BIT); + printf("size of int is %d bits\n", sizeof(int)*CHAR_BIT); StreamCompaction::RadixSort::sort(NPOT, b, a, msb); printArray(NPOT, b, true); diff --git a/stream_compaction/CMakeLists.txt b/stream_compaction/CMakeLists.txt index 7c8aae8..44a1ee9 100644 --- a/stream_compaction/CMakeLists.txt +++ b/stream_compaction/CMakeLists.txt @@ -15,5 +15,5 @@ set(SOURCE_FILES cuda_add_library(stream_compaction ${SOURCE_FILES} - OPTIONS -arch=sm_30 + OPTIONS -arch=sm_61 ) diff --git a/stream_compaction/common.h b/stream_compaction/common.h index f0962b7..d924760 100644 --- a/stream_compaction/common.h +++ b/stream_compaction/common.h @@ -3,7 +3,7 @@ #include #include #include -#define blockSize 128 +#define blockSize 1024 #define FILENAME (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) #define checkCUDAError(msg) checkCUDAErrorFn(msg, FILENAME, __LINE__) diff --git a/stream_compaction/thrust.cu b/stream_compaction/thrust.cu index 38c8db5..1141bdb 100644 --- a/stream_compaction/thrust.cu +++ b/stream_compaction/thrust.cu @@ -6,7 +6,7 @@ #include "common.h" #include "thrust.h" #include -#include +//#include namespace StreamCompaction { namespace Thrust { @@ -14,19 +14,19 @@ namespace Thrust { /** * Performs prefix-sum (aka scan) on idata, storing the result into odata. */ -float scan(int n, int *odata, const int *idata) { +void scan(int n, int *odata, const int *idata) { // 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()); - auto begin = std::chrono::high_resolution_clock::now(); + //auto begin = std::chrono::high_resolution_clock::now(); thrust::exclusive_scan(idata , idata +n , odata); - auto end = std::chrono::high_resolution_clock::now(); - float ns = std::chrono::duration_cast(end - begin).count(); - - return ns; + //auto end = std::chrono::high_resolution_clock::now(); + //float ns = std::chrono::duration_cast(end - begin).count(); + // float ns=0; + //return ns; } } diff --git a/stream_compaction/thrust.h b/stream_compaction/thrust.h index d6182b2..06707f3 100644 --- a/stream_compaction/thrust.h +++ b/stream_compaction/thrust.h @@ -2,6 +2,6 @@ namespace StreamCompaction { namespace Thrust { - float scan(int n, int *odata, const int *idata); + void scan(int n, int *odata, const int *idata); } } From 91ecc2db672d83058c88adadf8862758294ae30c Mon Sep 17 00:00:00 2001 From: dengxianga Date: Tue, 27 Sep 2016 23:26:19 -0400 Subject: [PATCH 14/21] add analysis --- README.md | 13 ++++++++++--- stream_compaction/common.h | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e328792..140dde9 100644 --- a/README.md +++ b/README.md @@ -6,16 +6,23 @@ CUDA Stream Compaction * Xiang Deng * Tested on: Windows 10-Home, i7-6700U @ 2.6GHz 16GB, GTX 1060 6GB (Personal Computer) -### (TODO: Your README) + +* Compare all of these GPU Scan implementations (Naive, Work-Efficient, and Thrust) to the serial CPU version of Scan. Plot a graph of the comparison (with array size on the independent axis). ![](images/1.PNG) - +Based on the figure and data above, regarding scanning, I found the bottleneck for GPU outperforms the GPU is around the arraysize of 2^16, after which the GPU sigificantly speed up than the CPU. +The CPU shows its adavantage for small arraysize. ![](images/2.PNG) ![](images/3.PNG) - +Based on the figure and data above, regarding compacting, I found the bottleneck for GPU outperforms the GPU is between the arraysize of 2^16 and 2^20, after which the GPU sigificantly speed up than the CPU. +The CPU still shows its adavantage for small arraysize. ![](images/4.PNG) +* Optimization of blocksize: +Experiments was conducted on various blocksizes from 32 to 1024 with exponential growth. Typically we observed the optimizal value of block size (256) which best +balance the optimal value of scan time as well as compact time for GPU. Since earlier we observed the array size of 2^16 is around the point of "bottleneck", we +used this parameter for the tuning of the blocksize. ![](images/5.PNG) ![](images/6.PNG) diff --git a/stream_compaction/common.h b/stream_compaction/common.h index d924760..5ebcdb9 100644 --- a/stream_compaction/common.h +++ b/stream_compaction/common.h @@ -3,7 +3,7 @@ #include #include #include -#define blockSize 1024 +#define blockSize 256 #define FILENAME (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) #define checkCUDAError(msg) checkCUDAErrorFn(msg, FILENAME, __LINE__) From ea396fd66838ac6fd6960a3803c63c1bdeb035a9 Mon Sep 17 00:00:00 2001 From: dengxianga Date: Tue, 27 Sep 2016 23:27:03 -0400 Subject: [PATCH 15/21] \n --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 140dde9..1fbeec6 100644 --- a/README.md +++ b/README.md @@ -10,19 +10,24 @@ CUDA Stream Compaction * Compare all of these GPU Scan implementations (Naive, Work-Efficient, and Thrust) to the serial CPU version of Scan. Plot a graph of the comparison (with array size on the independent axis). ![](images/1.PNG) + Based on the figure and data above, regarding scanning, I found the bottleneck for GPU outperforms the GPU is around the arraysize of 2^16, after which the GPU sigificantly speed up than the CPU. The CPU shows its adavantage for small arraysize. + ![](images/2.PNG) ![](images/3.PNG) + Based on the figure and data above, regarding compacting, I found the bottleneck for GPU outperforms the GPU is between the arraysize of 2^16 and 2^20, after which the GPU sigificantly speed up than the CPU. The CPU still shows its adavantage for small arraysize. + ![](images/4.PNG) * Optimization of blocksize: Experiments was conducted on various blocksizes from 32 to 1024 with exponential growth. Typically we observed the optimizal value of block size (256) which best balance the optimal value of scan time as well as compact time for GPU. Since earlier we observed the array size of 2^16 is around the point of "bottleneck", we used this parameter for the tuning of the blocksize. + ![](images/5.PNG) ![](images/6.PNG) From 7d6b5ca6763ed5923efdd6f9e92f759eeca1a97a Mon Sep 17 00:00:00 2001 From: dengxianga Date: Tue, 27 Sep 2016 23:31:26 -0400 Subject: [PATCH 16/21] more --- README.md | 4 ++++ images/7.PNG | Bin 0 -> 23155 bytes images/8.PNG | Bin 0 -> 15141 bytes 3 files changed, 4 insertions(+) create mode 100644 images/7.PNG create mode 100644 images/8.PNG diff --git a/README.md b/README.md index 1fbeec6..24958de 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,8 @@ CUDA Stream Compaction ![](images/1.PNG) +![](images/7.PNG) + Based on the figure and data above, regarding scanning, I found the bottleneck for GPU outperforms the GPU is around the arraysize of 2^16, after which the GPU sigificantly speed up than the CPU. The CPU shows its adavantage for small arraysize. @@ -22,6 +24,7 @@ Based on the figure and data above, regarding compacting, I found the bottleneck The CPU still shows its adavantage for small arraysize. ![](images/4.PNG) +![](images/8.PNG) * Optimization of blocksize: Experiments was conducted on various blocksizes from 32 to 1024 with exponential growth. Typically we observed the optimizal value of block size (256) which best @@ -32,3 +35,4 @@ used this parameter for the tuning of the blocksize. ![](images/6.PNG) +* Extra credits diff --git a/images/7.PNG b/images/7.PNG new file mode 100644 index 0000000000000000000000000000000000000000..5f1ff0f1d7b8672019c009546270ff77b45fefb7 GIT binary patch literal 23155 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 z%0A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 literal 0 HcmV?d00001 From 1f9409fd16c223ede479321722bcfb77f76bef08 Mon Sep 17 00:00:00 2001 From: dengxianga Date: Tue, 27 Sep 2016 23:33:04 -0400 Subject: [PATCH 17/21] k --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 24958de..ee3c73e 100644 --- a/README.md +++ b/README.md @@ -20,11 +20,13 @@ The CPU shows its adavantage for small arraysize. ![](images/3.PNG) +![](images/8.PNG) + Based on the figure and data above, regarding compacting, I found the bottleneck for GPU outperforms the GPU is between the arraysize of 2^16 and 2^20, after which the GPU sigificantly speed up than the CPU. The CPU still shows its adavantage for small arraysize. ![](images/4.PNG) -![](images/8.PNG) + * Optimization of blocksize: Experiments was conducted on various blocksizes from 32 to 1024 with exponential growth. Typically we observed the optimizal value of block size (256) which best From 76d4a14beb7f547e64f02de23f134fd9a2f837aa Mon Sep 17 00:00:00 2001 From: dengxianga Date: Tue, 27 Sep 2016 23:42:31 -0400 Subject: [PATCH 18/21] Good --- README.md | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/README.md b/README.md index ee3c73e..53b0400 100644 --- a/README.md +++ b/README.md @@ -38,3 +38,96 @@ used this parameter for the tuning of the blocksize. ![](images/6.PNG) * Extra credits +* 1) I typically found arraysize of 2^16 or greater already makes the GPU outforms the GPU. +* 2) The radix sort was implemented and tested. The testing function (at the end of the main.cpp) generates array size of power of two and not power of two. In both cases, +we compare the sorting result with C++ built in sorting function to verify the correctness. It's correctness has been verified. + +# Test output + +``` +**************** +** SCAN TESTS ** +**************** + [ 38 19 38 37 5 47 15 35 0 12 3 0 42 ... 35 0 ] +==== cpu scan, power-of-two ==== + [ 0 38 57 95 132 137 184 199 234 234 246 249 249 ... 1604374 1604409 ] +time lapsed 0.131000 ms +==== cpu scan, non-power-of-two ==== + [ 0 38 57 95 132 137 184 199 234 234 246 249 249 ... 1604305 1604316 ] + passed +time lapsed 0.130000 ms +==== naive scan, power-of-two ==== + [ 0 38 57 95 132 137 184 199 234 234 246 249 249 ... 1604374 1604409 ] + passed +time lapsed 0.106496 ms +==== naive scan, non-power-of-two ==== + [ 0 38 57 95 132 137 184 199 234 234 246 249 249 ... 0 0 ] + passed +time lapsed 0.080352 ms +==== work-efficient scan, power-of-two ==== + [ 0 38 57 95 132 137 184 199 234 234 246 249 249 ... 1604374 1604409 ] + passed +time lapsed 0.145792 ms +==== work-efficient scan, non-power-of-two ==== + [ 0 38 57 95 132 137 184 199 234 234 246 249 249 ... 1604305 1604316 ] + passed +time lapsed 0.146432 ms +==== thrust scan, power-of-two ==== + [ 0 38 57 95 132 137 184 199 234 234 246 249 249 ... 1604374 1604409 ] + passed +time lapsed 0.036000 ms +==== thrust scan, non-power-of-two ==== + [ 0 38 57 95 132 137 184 199 234 234 246 249 249 ... 1604305 1604316 ] + passed +time lapsed 0.036000 ms + +***************************** +** STREAM COMPACTION TESTS ** +***************************** + [ 2 3 2 1 3 1 1 1 2 0 1 0 2 ... 1 0 ] +==== cpu compact without scan, power-of-two ==== + [ 2 3 2 1 3 1 1 1 2 1 2 1 1 ... 1 1 ] + passed +time lapsed 0.191000 ms +==== cpu compact without scan, non-power-of-two ==== + [ 2 3 2 1 3 1 1 1 2 1 2 1 1 ... 3 1 ] + passed +time lapsed 0.191000 ms +==== cpu compact with scan ==== + [ 2 3 2 1 3 1 1 1 2 1 2 1 1 ... 1 1 ] + passed +time lapsed 0.368000 ms +==== work-efficient compact, power-of-two ==== + [ 2 3 2 1 3 1 1 1 2 1 2 1 1 ... 1 1 ] + passed +time lapsed 0.585728 ms +==== work-efficient compact, non-power-of-two ==== + [ 2 3 2 1 3 1 1 1 2 1 2 1 1 ... 3 1 ] + passed +time lapsed 0.549632 ms + +***************************** +** RADIX SORT TESTS ** +***************************** +==== Array to be sorted power of 2 ==== + [ 38 119 38 37 55 197 165 85 50 12 53 100 142 ... 85 0 ] +==== RADIX SORT POT ==== +size of int is 32 bits + [ 0 0 0 0 0 0 0 0 0 0 0 0 0 ... 199 199 ] +==== C++ SORT POT ==== + [ 0 0 0 0 0 0 0 0 0 0 0 0 0 ... 199 199 ] + passed +==== Array to be sorted not power of 2 ==== + [ 38 1719 1238 437 855 1797 365 285 450 612 1853 100 1142 ... 1085 0 ] +==== RADIX SORT NPOT ==== +size of int is 32 bits + [ 0 0 0 0 0 0 0 0 0 0 0 0 0 ... 1999 1999 ] +==== C++ SORT NPOT ==== + [ 0 0 0 0 0 0 0 0 0 0 0 0 0 ... 1999 1999 ] + passed +``` + +## Note +### Modified files +CMakeList.txt : add radixSort.h and radixSort.cu, changed -arch=sm_20 to sm_61 +Two files added: radixSort.h and radixSort.cu \ No newline at end of file From 6919008a0c29a4de15c80081b6344a981a7975f1 Mon Sep 17 00:00:00 2001 From: dengxianga Date: Tue, 27 Sep 2016 23:45:01 -0400 Subject: [PATCH 19/21] k --- README.md | 58 ++++++++++++++++++++++++++-------------------------- src/main.cpp | 4 ++-- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 53b0400..879e7d2 100644 --- a/README.md +++ b/README.md @@ -48,69 +48,69 @@ we compare the sorting result with C++ built in sorting function to verify the c **************** ** SCAN TESTS ** **************** - [ 38 19 38 37 5 47 15 35 0 12 3 0 42 ... 35 0 ] + [ 38 19 38 37 5 47 15 35 0 12 3 0 42 ... 6 0 ] ==== cpu scan, power-of-two ==== - [ 0 38 57 95 132 137 184 199 234 234 246 249 249 ... 1604374 1604409 ] -time lapsed 0.131000 ms + [ 0 38 57 95 132 137 184 199 234 234 246 249 249 ... 25680538 25680544 ] +time lapsed 2.110000 ms ==== cpu scan, non-power-of-two ==== - [ 0 38 57 95 132 137 184 199 234 234 246 249 249 ... 1604305 1604316 ] + [ 0 38 57 95 132 137 184 199 234 234 246 249 249 ... 25680508 25680512 ] passed -time lapsed 0.130000 ms +time lapsed 2.130000 ms ==== naive scan, power-of-two ==== - [ 0 38 57 95 132 137 184 199 234 234 246 249 249 ... 1604374 1604409 ] + [ 0 38 57 95 132 137 184 199 234 234 246 249 249 ... 25680538 25680544 ] passed -time lapsed 0.106496 ms +time lapsed 1.296384 ms ==== naive scan, non-power-of-two ==== [ 0 38 57 95 132 137 184 199 234 234 246 249 249 ... 0 0 ] passed -time lapsed 0.080352 ms +time lapsed 1.291712 ms ==== work-efficient scan, power-of-two ==== - [ 0 38 57 95 132 137 184 199 234 234 246 249 249 ... 1604374 1604409 ] + [ 0 38 57 95 132 137 184 199 234 234 246 249 249 ... 25680538 25680544 ] passed -time lapsed 0.145792 ms +time lapsed 1.671008 ms ==== work-efficient scan, non-power-of-two ==== - [ 0 38 57 95 132 137 184 199 234 234 246 249 249 ... 1604305 1604316 ] + [ 0 38 57 95 132 137 184 199 234 234 246 249 249 ... 25680508 25680512 ] passed -time lapsed 0.146432 ms +time lapsed 1.668096 ms ==== thrust scan, power-of-two ==== - [ 0 38 57 95 132 137 184 199 234 234 246 249 249 ... 1604374 1604409 ] + [ 0 38 57 95 132 137 184 199 234 234 246 249 249 ... 25680538 25680544 ] passed -time lapsed 0.036000 ms +time lapsed 0.630000 ms ==== thrust scan, non-power-of-two ==== - [ 0 38 57 95 132 137 184 199 234 234 246 249 249 ... 1604305 1604316 ] + [ 0 38 57 95 132 137 184 199 234 234 246 249 249 ... 25680508 25680512 ] passed -time lapsed 0.036000 ms +time lapsed 0.620000 ms ***************************** ** STREAM COMPACTION TESTS ** ***************************** - [ 2 3 2 1 3 1 1 1 2 0 1 0 2 ... 1 0 ] + [ 2 3 2 1 3 1 1 1 2 0 1 0 2 ... 0 0 ] ==== cpu compact without scan, power-of-two ==== - [ 2 3 2 1 3 1 1 1 2 1 2 1 1 ... 1 1 ] + [ 2 3 2 1 3 1 1 1 2 1 2 1 1 ... 2 2 ] passed -time lapsed 0.191000 ms +time lapsed 3.060000 ms ==== cpu compact without scan, non-power-of-two ==== - [ 2 3 2 1 3 1 1 1 2 1 2 1 1 ... 3 1 ] + [ 2 3 2 1 3 1 1 1 2 1 2 1 1 ... 2 2 ] passed -time lapsed 0.191000 ms +time lapsed 3.110000 ms ==== cpu compact with scan ==== - [ 2 3 2 1 3 1 1 1 2 1 2 1 1 ... 1 1 ] + [ 2 3 2 1 3 1 1 1 2 1 2 1 1 ... 2 2 ] passed -time lapsed 0.368000 ms +time lapsed 9.810000 ms ==== work-efficient compact, power-of-two ==== - [ 2 3 2 1 3 1 1 1 2 1 2 1 1 ... 1 1 ] + [ 2 3 2 1 3 1 1 1 2 1 2 1 1 ... 2 2 ] passed -time lapsed 0.585728 ms +time lapsed 2.623488 ms ==== work-efficient compact, non-power-of-two ==== - [ 2 3 2 1 3 1 1 1 2 1 2 1 1 ... 3 1 ] + [ 2 3 2 1 3 1 1 1 2 1 2 1 1 ... 2 2 ] passed -time lapsed 0.549632 ms +time lapsed 2.942464 ms ***************************** ** RADIX SORT TESTS ** ***************************** ==== Array to be sorted power of 2 ==== - [ 38 119 38 37 55 197 165 85 50 12 53 100 142 ... 85 0 ] + [ 38 119 38 37 55 197 165 85 50 12 53 100 142 ... 56 0 ] ==== RADIX SORT POT ==== size of int is 32 bits [ 0 0 0 0 0 0 0 0 0 0 0 0 0 ... 199 199 ] @@ -118,7 +118,7 @@ size of int is 32 bits [ 0 0 0 0 0 0 0 0 0 0 0 0 0 ... 199 199 ] passed ==== Array to be sorted not power of 2 ==== - [ 38 1719 1238 437 855 1797 365 285 450 612 1853 100 1142 ... 1085 0 ] + [ 38 1719 1238 437 855 1797 365 285 450 612 1853 100 1142 ... 656 0 ] ==== RADIX SORT NPOT ==== size of int is 32 bits [ 0 0 0 0 0 0 0 0 0 0 0 0 0 ... 1999 1999 ] diff --git a/src/main.cpp b/src/main.cpp index 78c1c05..dcbbf87 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -17,11 +17,11 @@ #include #include int main(int argc, char* argv[]) { - const int SIZE = 1 << 16; + const int SIZE = 1 << 20; const int NPOT = SIZE - 3; int a[SIZE], b[SIZE], c[SIZE]; float milscs; - int nitercpu = 1000; + int nitercpu = 100; // Scan tests From 39eb71949e5d27809db2a578e330826381ebe50d Mon Sep 17 00:00:00 2001 From: dengxianga Date: Tue, 27 Sep 2016 23:46:36 -0400 Subject: [PATCH 20/21] k --- Book1.xlsx | Bin 0 -> 40165 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 Book1.xlsx diff --git a/Book1.xlsx b/Book1.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..11315bf4bd29367ec2f16eacc666ccd19a3fe137 GIT binary patch literal 40165 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 literal 0 HcmV?d00001 From 44a99950ed6b713bb553ede282387be717b1266c Mon Sep 17 00:00:00 2001 From: dengxianga Date: Tue, 27 Sep 2016 23:55:34 -0400 Subject: [PATCH 21/21] G --- README.md | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 879e7d2..89a2b87 100644 --- a/README.md +++ b/README.md @@ -8,12 +8,13 @@ CUDA Stream Compaction * Compare all of these GPU Scan implementations (Naive, Work-Efficient, and Thrust) to the serial CPU version of Scan. Plot a graph of the comparison (with array size on the independent axis). +(The following are the results for timing vs. varing arraysize with blocksize=128) ![](images/1.PNG) ![](images/7.PNG) -Based on the figure and data above, regarding scanning, I found the bottleneck for GPU outperforms the GPU is around the arraysize of 2^16, after which the GPU sigificantly speed up than the CPU. +Based on the figure and data above, regarding scanning, I found the breakpoint for GPU outperforms the GPU is around the arraysize of 2^16, after which the GPU sigificantly speed up than the CPU. The CPU shows its adavantage for small arraysize. ![](images/2.PNG) @@ -22,15 +23,25 @@ The CPU shows its adavantage for small arraysize. ![](images/8.PNG) -Based on the figure and data above, regarding compacting, I found the bottleneck for GPU outperforms the GPU is between the arraysize of 2^16 and 2^20, after which the GPU sigificantly speed up than the CPU. +Based on the figure and data above, regarding compacting, I found the breakpoint for GPU outperforms the GPU is between the arraysize of 2^16 and 2^20, after which the GPU sigificantly speed up than the CPU. The CPU still shows its adavantage for small arraysize. +# Regarding "bottlenecks" : + +Firstly we didn't observe significant improvement from Naive GPU scanning to Efficient Scanning, one possible reasoning that might shed light on this is the +increasing number of sleeping threads as the levels of the balance tree grows higher. + +The memory transfer between CPU and GPU also slows down the performance, one possible way to alleviate this problem might be increasing the data size which reduces the +percentage of such overhead in the meanwhile. + +The switching between the upsweeping and downsweeping trigerrs new kernels to be established, which could be more efficient if the kernels could be reused. + ![](images/4.PNG) * Optimization of blocksize: Experiments was conducted on various blocksizes from 32 to 1024 with exponential growth. Typically we observed the optimizal value of block size (256) which best -balance the optimal value of scan time as well as compact time for GPU. Since earlier we observed the array size of 2^16 is around the point of "bottleneck", we +balance the optimal value of scan time as well as compact time for GPU. Since earlier we observed the array size of 2^16 is around the "turning poit", we used this parameter for the tuning of the blocksize. ![](images/5.PNG)