From d2f8c3dfcb26ba194f67d07c4fe6266380defb57 Mon Sep 17 00:00:00 2001 From: Dave Kotfis Date: Sun, 28 Sep 2014 22:48:35 -0400 Subject: [PATCH 1/2] Initial commit of lots of working stuff. --- CPUTiming.png | Bin 0 -> 45215 bytes NaiveScanTiming.png | Bin 0 -> 40923 bytes SharedMemory.png | Bin 0 -> 35265 bytes StreamCompaction/StreamCompaction.sln | 20 + .../StreamCompaction/StreamCompaction.vcxproj | 84 ++++ .../StreamCompaction.vcxproj.filters | 44 ++ StreamCompaction/StreamCompaction/main.cpp | 260 +++++++++++ .../StreamCompaction/prefix_sum.cu | 410 ++++++++++++++++++ .../StreamCompaction/prefix_sum.h | 53 +++ .../StreamCompaction/thrust_compact.cu | 36 ++ .../StreamCompaction/thrust_compact.h | 15 + .../StreamCompaction/timing_utils.cu | 32 ++ .../StreamCompaction/timing_utils.h | 11 + 13 files changed, 965 insertions(+) create mode 100644 CPUTiming.png create mode 100644 NaiveScanTiming.png create mode 100644 SharedMemory.png create mode 100644 StreamCompaction/StreamCompaction.sln create mode 100644 StreamCompaction/StreamCompaction/StreamCompaction.vcxproj create mode 100644 StreamCompaction/StreamCompaction/StreamCompaction.vcxproj.filters create mode 100644 StreamCompaction/StreamCompaction/main.cpp create mode 100644 StreamCompaction/StreamCompaction/prefix_sum.cu create mode 100644 StreamCompaction/StreamCompaction/prefix_sum.h create mode 100644 StreamCompaction/StreamCompaction/thrust_compact.cu create mode 100644 StreamCompaction/StreamCompaction/thrust_compact.h create mode 100644 StreamCompaction/StreamCompaction/timing_utils.cu create mode 100644 StreamCompaction/StreamCompaction/timing_utils.h diff --git a/CPUTiming.png b/CPUTiming.png new file mode 100644 index 0000000000000000000000000000000000000000..8870aa358828756618625b8f6ee9ff0a2e6213fe GIT binary patch literal 45215 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 G4vT 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 literal 0 HcmV?d00001 diff --git a/SharedMemory.png b/SharedMemory.png new file mode 100644 index 0000000000000000000000000000000000000000..f983731919236101725d692ae172ccaaa810a8ac GIT binary patch literal 35265 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 XGwQ8ucLqO!Ukl$}{MPX|L$?1D?c + + + + Debug + Win32 + + + Release + Win32 + + + + {9B4AE667-EC32-45C1-8F74-FD42DAF1D7EA} + StreamCompaction + + + + Application + true + MultiByte + + + Application + false + true + MultiByte + + + + + + + + + + + + + + + + Level3 + Disabled + + + true + %(AdditionalLibraryDirectories) + cudart.lib;%(AdditionalDependencies) + + + + + Level3 + MaxSpeed + true + true + + + true + true + true + + + + + Document + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/StreamCompaction/StreamCompaction/StreamCompaction.vcxproj.filters b/StreamCompaction/StreamCompaction/StreamCompaction.vcxproj.filters new file mode 100644 index 0000000..3808e28 --- /dev/null +++ b/StreamCompaction/StreamCompaction/StreamCompaction.vcxproj.filters @@ -0,0 +1,44 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + Source Files + + + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/StreamCompaction/StreamCompaction/main.cpp b/StreamCompaction/StreamCompaction/main.cpp new file mode 100644 index 0000000..c470bef --- /dev/null +++ b/StreamCompaction/StreamCompaction/main.cpp @@ -0,0 +1,260 @@ + +// C Dependencies +#include +#include +#include +#include + +// Project Dependencies +#include "prefix_sum.h" +#include "timing_utils.h" +#include "thrust_compact.h" + +//Global data for run settings +int LENGTH = 100; +const int MAX_LENGTH = 100; +const int MAX = 10; +const bool TIMING = true; +const bool PRINT_DATA = true; + +template +void cpuPrefixSum(const T* in, T* out, int size) { + T sum = static_cast(0.0f); + for (int i = 0; i < size; i++) { + out[i] = sum; + sum += in[i]; + } +} + +template +void cpuScatter(const T* in, const T threshold, int* out, int size) { + int* temp = (int*) malloc(size*sizeof(int)); + for (int i = 0; i < size; i++) { + temp[i] = static_cast(in[i] > threshold); + } + cpuPrefixSum(temp, out, size); + free(temp); +} + +template +int cpuCompact(const T* in, const T threshold, T* out, int size_in) { + int* temp = (int*) malloc(size_in*sizeof(int)); + cpuScatter(in, threshold, temp, size_in); + int result = temp[size_in-1]; + int current = result; + for (int i = (size_in-1); i >= 0; i--) { + if (temp[i] == current) { + out[current] = in[i]; + current--; + } + } + return result+1; +} + +void printResults(float* data, int size) { + fprintf(stdout, "["); + for (size_t i = 0; i < size; i++) { + if (i != (size-1)) { + fprintf(stdout, "%f, ", data[i]); + } else { + fprintf(stdout, "%f", data[i]); + } + } + fprintf(stdout, "]\n"); +} + +void printResults(int* data, int size) { + fprintf(stdout, "["); + for (size_t i = 0; i < size; i++) { + if (i != (size-1)) { + fprintf(stdout, "%d, ", data[i]); + } else { + fprintf(stdout, "%d", data[i]); + } + } + fprintf(stdout, "]\n"); +} + +void printResults(bool* data, int size) { + fprintf(stdout, "["); + for (size_t i = 0; i < size; i++) { + if (i != (size-1)) { + fprintf(stdout, "%d, ", data[i] ? 1 : 0); + } else { + fprintf(stdout, "%d", data[i] ? 1 : 0); + } + } + fprintf(stdout, "]\n"); +} + +int main(int argc, char** argv) { + + int new_length; + + for (LENGTH = 10; LENGTH <= MAX_LENGTH; LENGTH *= 10) { + + // Seed the random number generator + srand(time(NULL)); + + // Initialize data (randomly) + int *start = (int*) malloc(LENGTH*sizeof(int)); + int *end = (int*) malloc(LENGTH*sizeof(int)); + for (int i = 0; i < LENGTH; i++) { + start[i] = (int) ( (float) rand() / (float) (RAND_MAX/MAX) ); + } + int *scat_res = (int*) malloc(LENGTH*sizeof(int)); + if (PRINT_DATA) { + fprintf(stdout, "Input Array:\n"); + printResults(start, LENGTH); + fprintf(stdout, "-------- \n"); + } + + //Call the CPU implementation + fprintf(stdout, "CPU Scan:\n"); + if (!TIMING) { + cpuPrefixSum(start, end, LENGTH); + } else { + startTiming(); + cpuPrefixSum(start, end, LENGTH); + float timeTaken = stopTiming(); + fprintf(stdout, "Length: %d, Execution time: %f ms.\n", LENGTH, timeTaken); + } + if (PRINT_DATA) { + printResults(end, LENGTH); + } + fprintf(stdout, "-------- \n"); + + //Call the naive GPU implementation + fprintf(stdout, "Naive GPU Scan:\n"); + if (!TIMING) { + gpuNaivePrefixSumI(start, end, LENGTH); + } else { + startTiming(); + gpuNaivePrefixSumI(start, end, LENGTH); + float timeTaken = stopTiming(); + fprintf(stdout, "Length: %d, Execution time: %f ms.\n", LENGTH, timeTaken); + } + if (PRINT_DATA) { + printResults(end, LENGTH); + } + fprintf(stdout, "-------- \n"); + + //Call the one block GPU implementation + fprintf(stdout, "One Block GPU Scan:\n"); + if (!TIMING) { + gpuOneBlockPrefixSumI(start, end, LENGTH); + } else { + startTiming(); + gpuOneBlockPrefixSumI(start, end, LENGTH); + float timeTaken = stopTiming(); + fprintf(stdout, "Length: %d, Execution time: %f ms.\n", LENGTH, timeTaken); + } + if (PRINT_DATA) { + printResults(end, LENGTH); + } + fprintf(stdout, "-------- \n"); + + //Call the multi block GPU implementation + fprintf(stdout, "Multi Block GPU Scan:\n"); + if (!TIMING) { + gpuNBlockPrefixSumI(start, end, LENGTH); + } else { + startTiming(); + gpuNBlockPrefixSumI(start, end, LENGTH); + float timeTaken = stopTiming(); + fprintf(stdout, "Length: %d, Execution time: %f ms.\n", LENGTH, timeTaken); + } + if (PRINT_DATA) { + printResults(end, LENGTH); + } + fprintf(stdout, "-------- \n"); + + //Call CPU Scatter + fprintf(stdout, "CPU Scatter:\n"); + if (!TIMING) { + cpuScatter(start, 5, scat_res, LENGTH); + } else { + startTiming(); + cpuScatter(start, 5, scat_res, LENGTH); + float timeTaken = stopTiming(); + fprintf(stdout, "Length: %d, Execution time: %f ms.\n", LENGTH, timeTaken); + } + if (PRINT_DATA) { + printResults(scat_res, LENGTH); + } + fprintf(stdout, "-------- \n"); + + //Call GPU Scatter + fprintf(stdout, "GPU Scatter:\n"); + if (!TIMING) { + gpuScatterI(start, 5, scat_res, LENGTH); + } else { + startTiming(); + gpuScatterI(start, 5, scat_res, LENGTH); + float timeTaken = stopTiming(); + fprintf(stdout, "Length: %d, Execution time: %f ms.\n", LENGTH, timeTaken); + } + if (PRINT_DATA) { + printResults(scat_res, LENGTH); + } + fprintf(stdout, "-------- \n"); + + //Call CPU Compact + fprintf(stdout, "CPU Compact:\n"); + if (!TIMING) { + new_length = cpuCompact(start, 5, end, LENGTH); + } else { + startTiming(); + new_length = cpuCompact(start, 5, end, LENGTH); + float timeTaken = stopTiming(); + fprintf(stdout, "Length: %d, Execution time: %f ms.\n", LENGTH, timeTaken); + } + if (PRINT_DATA) { + printResults(end, new_length); + } + fprintf(stdout, "-------- \n"); + + //Call GPU Compact + fprintf(stdout, "GPU Compact:\n"); + if (!TIMING) { + new_length = gpuCompactI(start, 5, end, LENGTH); + } else { + startTiming(); + new_length = gpuCompactI(start, 5, end, LENGTH); + float timeTaken = stopTiming(); + fprintf(stdout, "Length: %d, Execution time: %f ms.\n", LENGTH, timeTaken); + } + if (PRINT_DATA) { + printResults(end, new_length); + } + fprintf(stdout, "-------- \n"); + + //Call Thrust Compact + /* + fprintf(stdout, "Thrust Compact:\n"); + if (!TIMING) { + new_length = thrustCompactI(start, 5, end, LENGTH); + } else { + startTiming(); + new_length = thrustCompactI(start, 5, end, LENGTH); + float timeTaken = stopTiming(); + fprintf(stdout, "Length: %d, Execution time: %f ms.\n", LENGTH, timeTaken); + } + if (PRINT_DATA) { + printResults(end, new_length); + } + fprintf(stdout, "-------- \n"); + */ + + //Free up memory + free(start); + free(end); + free(scat_res); + } + + while (true) { + + } + + return 0; +} diff --git a/StreamCompaction/StreamCompaction/prefix_sum.cu b/StreamCompaction/StreamCompaction/prefix_sum.cu new file mode 100644 index 0000000..f5605e1 --- /dev/null +++ b/StreamCompaction/StreamCompaction/prefix_sum.cu @@ -0,0 +1,410 @@ + +// C/CUDA Dependencies +#include +#include + +// Project Dependencies +#include "prefix_sum.h" + +template +__global__ void naive_prefix_sum(T* in, T* out, int* size) { + + int index = threadIdx.x; //Keep it simple, only use x since arrays are 1 dimensional + + //Start by shifting right since the calculation is going to be inclusive and we want exclusive + if (index > 0) { + out[index] = in[index-1]; + } else { + out[index] = 0.0f; + } + __syncthreads(); + + // Switch the output back with the input + T* temp1 = in; + in = out; + out = temp1; + + //Calculate the max depth + int max_depth = ceil(log((float) *size)/log(2.0f)); + + //Loop over each depth + for (int d = 1; d <= max_depth; d++) { + + //Calculate the offset for the current depth + int off = pow(2.0f, d-1); + + // calculate the sum + if (index >= off) { + out[index] = in[index - off] + in[index]; + } else { + //Have to leave other elements alone + out[index] = in[index]; + } + + //Sync threads before the next depth to use proper values + __syncthreads(); + + //Swap the input and the output pointers for the next iteration + T* temp2 = in; + in = out; + out = temp2; + } + + //Make sure the output is the out pointer at the end + out = in; + +} + +template +__global__ void one_block_prefix_sum(T* in, T* out, int* size) { + + int index = threadIdx.x; //Keep it simple, only use x since arrays are 1 dimensional + + // Create shared memory + extern __shared__ T s[]; + T* in_s = &s[0]; + T* out_s = &s[*size]; + + //Start by shifting right since the calculation is going to be inclusive and we want exclusive + //Load into shared memory + if (index > 0) { + in_s[index] = in[index-1]; + } else { + in_s[index] = 0.0f; + } + __syncthreads(); + + //Calculate the max depth + int max_depth = ceil(log((float) *size)/log(2.0f)); + + //Loop over each depth + for (int d = 1; d <= max_depth; d++) { + + //Calculate the offset for the current depth + int off = pow(2.0f, d-1); + + // compute left-> or right->left + if ((d%2) == 1) { + + // calculate the sum + if (index >= off) { + out_s[index] = in_s[index - off] + in_s[index]; + } else { + //Have to leave other elements alone + out_s[index] = in_s[index]; + } + + } else { + + // calculate the sum + if (index >= off) { + in_s[index] = out_s[index - off] + out_s[index]; + } else { + //Have to leave other elements alone + in_s[index] = out_s[index]; + } + + } + + //Sync threads before the next depth to use proper values + __syncthreads(); + + } + + //Copy the correct result to global memory + if ((max_depth%2) == 1) { + out[index] = out_s[index]; + } else { + out[index] = in_s[index]; + } + +} + +template +__global__ void n_block_prefix_sum(T* in, T* out, int* size) { + + int index = blockIdx.x * blockDim.x + threadIdx.x; //Keep it simple, only use x since arrays are 1 dimensional + int s_index = threadIdx.x; + + // Create shared memory + extern __shared__ T s[]; + T* in_s = &s[0]; + T* out_s = &s[*size]; + + //Start by shifting right since the calculation is going to be inclusive and we want exclusive + //Load into shared memory + if (index > 0) { + in_s[s_index] = in[index-1]; + } else { + in_s[s_index] = 0.0f; + } + __syncthreads(); + + //Calculate the max depth + int max_depth = ceil(log((float) *size)/log(2.0f)); + + //Loop over each depth + for (int d = 1; d <= max_depth; d++) { + + //Calculate the offset for the current depth + int off = pow(2.0f, d-1); + + // compute left-> or right->left + if ((d%2) == 1) { + + // calculate the sum + if (s_index >= off) { + out_s[s_index] = in_s[s_index - off] + in_s[s_index]; + } else { + //Have to leave other elements alone + out_s[s_index] = in_s[s_index]; + } + + } else { + + // calculate the sum + if (s_index >= off) { + in_s[s_index] = out_s[s_index - off] + out_s[s_index]; + } else { + //Have to leave other elements alone + in_s[s_index] = out_s[s_index]; + } + + } + + //Sync threads before the next depth to use proper values + __syncthreads(); + + } + + //Copy the correct result to global memory + if ((max_depth%2) == 1) { + out[index] = out_s[s_index]; + } else { + out[index] = in_s[s_index]; + } + +} + +template +__global__ void threshold_array(const T* in, const T* threshold, int* out) { + int index = threadIdx.x; //Keep it simple, only use x since arrays are 1 dimensional + out[index] = in[index] > *threshold ? 1 : 0; +} + +template +__global__ void compact_array(const T* in, const int* indices, const int* mask, T* out, int* size) { + int index = threadIdx.x; //Keep it simple, only use x since arrays are 1 dimensional + if (mask[index] == 1) { + out[indices[index]] = in[index]; + } + *size = (indices[*size-1] + 1); +} + +template +void gpuNaivePrefixSum(const T* in, T* out, int size) { + //Allocate data on GPU + T* in_d; + T* out_d; + int* size_d; + cudaMalloc((void**)&in_d, size*sizeof(T)); + cudaMalloc((void**)&out_d, size*sizeof(T)); + cudaMalloc((void**)&size_d, sizeof(int)); + + //Copy data to GPU + cudaMemcpy(in_d, in, size*sizeof(T), cudaMemcpyHostToDevice); + cudaMemcpy(size_d, &size, sizeof(int), cudaMemcpyHostToDevice); + + //Call the kernel + naive_prefix_sum<<<1,size>>>(in_d, out_d, size_d); + + //Copy data from GPU + cudaMemcpy(out, out_d, size*sizeof(T), cudaMemcpyDeviceToHost); + + //Clear memory from GPU + cudaFree(in_d); + cudaFree(out_d); +} + +template +void gpuOneBlockPrefixSum(const T* in, T* out, int size) { + //Allocate data on GPU + T* in_d; + T* out_d; + int* size_d; + cudaMalloc((void**)&in_d, size*sizeof(T)); + cudaMalloc((void**)&out_d, size*sizeof(T)); + cudaMalloc((void**)&size_d, sizeof(int)); + + //Copy data to GPU + cudaMemcpy(in_d, in, size*sizeof(T), cudaMemcpyHostToDevice); + cudaMemcpy(size_d, &size, sizeof(int), cudaMemcpyHostToDevice); + + //Call the kernel + one_block_prefix_sum<<<1,size, 2*size*sizeof(T)>>>(in_d, out_d, size_d); + cudaDeviceSynchronize(); + + //Copy data from GPU + cudaMemcpy(out, out_d, size*sizeof(T), cudaMemcpyDeviceToHost); + + //Clear memory from GPU + cudaFree(in_d); + cudaFree(out_d); +} + +template +void gpuNBlockPrefixSum(const T* in, T* out, int size) { + //Allocate data on GPU + T* in_d; + T* out_d; + int* size_d; + cudaMalloc((void**)&in_d, size*sizeof(T)); + cudaMalloc((void**)&out_d, size*sizeof(T)); + cudaMalloc((void**)&size_d, sizeof(int)); + + //Copy data to GPU + cudaMemcpy(in_d, in, size*sizeof(T), cudaMemcpyHostToDevice); + cudaMemcpy(size_d, &size, sizeof(int), cudaMemcpyHostToDevice); + + //Determine the needed number of blocks/threads + int needed_bytes = 2*size*sizeof(T); + int n_blocks = 16384/needed_bytes; + int threads_per_block = size/n_blocks; + + //Call the kernel + n_block_prefix_sum<<>>(in_d, out_d, size_d); + cudaDeviceSynchronize(); + + //Copy data from GPU + cudaMemcpy(out, out_d, size*sizeof(T), cudaMemcpyDeviceToHost); + + //Clear memory from GPU + cudaFree(in_d); + cudaFree(out_d); +} + +template +void gpuScatter(const T* in, const T threshold, int* out, int size) { + //Allocate data on GPU + T* in_d; + int* out_d; + int* mask_d; + T* threshold_d; + int* size_d; + cudaMalloc((void**)&in_d, size*sizeof(T)); + cudaMalloc((void**)&out_d, size*sizeof(T)); + cudaMalloc((void**)&mask_d, size*sizeof(int)); + cudaMalloc((void**)&threshold_d, sizeof(T)); + cudaMalloc((void**)&size_d, sizeof(int)); + + //Copy data to GPU + cudaMemcpy(in_d, in, size*sizeof(T), cudaMemcpyHostToDevice); + cudaMemcpy(size_d, &size, sizeof(int), cudaMemcpyHostToDevice); + cudaMemcpy(threshold_d, &threshold, sizeof(T), cudaMemcpyHostToDevice); + + // Call the thresold kernel + threshold_array<<<1,size>>>(in_d, threshold_d, mask_d); + cudaDeviceSynchronize(); + + // Call the prefix sum kernel + one_block_prefix_sum<<<1,size, 2*size*sizeof(T)>>>(mask_d, out_d, size_d); + cudaDeviceSynchronize(); + + // Copy the result back from the GPU + cudaMemcpy(out, out_d, size*sizeof(int), cudaMemcpyDeviceToHost); + + //Clear GPU Memory + cudaFree(in_d); + cudaFree(out_d); + cudaFree(mask_d); + cudaFree(threshold_d); + cudaFree(size_d); + +} + +template +int gpuCompact(const T* in, const T threshold, T* out, int size) { + //Allocate data on GPU + T* in_d; + T* out_d; + int* mask_d; + int* indices_d; + T* threshold_d; + int* size_d; + cudaMalloc((void**)&in_d, size*sizeof(T)); + cudaMalloc((void**)&out_d, size*sizeof(T)); + cudaMalloc((void**)&mask_d, size*sizeof(int)); + cudaMalloc((void**)&indices_d, size*sizeof(int)); + cudaMalloc((void**)&threshold_d, sizeof(T)); + cudaMalloc((void**)&size_d, sizeof(int)); + + //Copy data to GPU + cudaMemcpy(in_d, in, size*sizeof(T), cudaMemcpyHostToDevice); + cudaMemcpy(size_d, &size, sizeof(int), cudaMemcpyHostToDevice); + cudaMemcpy(threshold_d, &threshold, sizeof(T), cudaMemcpyHostToDevice); + + // Call the thresold kernel + threshold_array<<<1,size>>>(in_d, threshold_d, mask_d); + cudaDeviceSynchronize(); + + // Call the prefix sum kernel + one_block_prefix_sum<<<1,size, 2*size*sizeof(T)>>>(mask_d, indices_d, size_d); + cudaDeviceSynchronize(); + + // Call the compaction kernel + compact_array<<<1,size>>>(in_d, indices_d, mask_d, out_d, size_d); + + // Copy the result back from the GPU + int result; + cudaMemcpy(&result, size_d, sizeof(int), cudaMemcpyDeviceToHost); + cudaMemcpy(out, out_d, result*sizeof(T), cudaMemcpyDeviceToHost); + + //Clear GPU Memory + cudaFree(in_d); + cudaFree(out_d); + cudaFree(mask_d); + cudaFree(indices_d); + cudaFree(threshold_d); + cudaFree(size_d); + + return result; +} + +void gpuNaivePrefixSumF(const float* in, float* out, int size) { + gpuNaivePrefixSum(in, out, size); +} + +void gpuNaivePrefixSumI(const int* in, int* out, int size) { + gpuNaivePrefixSum(in, out, size); +} + +/* +void gpuOneBlockPrefixSumF(const float* in, float* out, int size) { + gpuOneBlockPrefixSum(in, out, size); +} +*/ + +void gpuOneBlockPrefixSumI(const int* in, int* out, int size) { + gpuOneBlockPrefixSum(in, out, size); +} + +void gpuNBlockPrefixSumI(const int* in, int* out, int size) { + gpuNBlockPrefixSum(in, out, size); +} + +void gpuScatterI(const int* in, const int threshold, int* out, int size) { + gpuScatter(in, threshold, out, size); +} + +void gpuScatterF(const float* in, const float threshold, int* out, int size) { + gpuScatter(in, threshold, out, size); +} + +int gpuCompactI(const int* in, const int threshold, int* out, int size) { + return gpuCompact(in, threshold, out, size); +} + +int gpuCompactF(const float* in, const float threshold, float* out, int size) { + return gpuCompact(in, threshold, out, size); +} + diff --git a/StreamCompaction/StreamCompaction/prefix_sum.h b/StreamCompaction/StreamCompaction/prefix_sum.h new file mode 100644 index 0000000..20804ab --- /dev/null +++ b/StreamCompaction/StreamCompaction/prefix_sum.h @@ -0,0 +1,53 @@ +#ifndef PREFIX_SUM_H_ +#define PREFIX_SUM_H_ + +/* Naive Implementation of GPU Prefix Sum */ +/* @param in Input array of to sum over */ +/* @param out Output array */ +/* @param size The integer size of in */ +template +void gpuNaivePrefixSum(const T* in, T* out, int size); + +/* Single Block Shared Implementation of GPU Prefix Sum */ +/* @param in Input array to sum over */ +/* @param out Output array */ +/* @param size The integer size of in */ +template +void gpuOneBlockPrefixSum(const T* in, T* out, int size); + +/* Multi Block Shared Implementation of GPU Prefix Sum */ +/* @param in Input array to sum over */ +/* @param out Output array */ +/* @param size The integer size of in */ +template +void gpuNBlockPrefixSum(const T* in, T* out, int size); + +/* Perform compaction using GPU Prefix sum */ +/* @param in Input array to scatter */ +/* @param threshold Value to threshold the input array on (using >) */ +/* @param out Output array of scattered indices */ +/* @param size The integer size of in */ +template +void gpuScatter(const T* in, const T threshold, int* out, int size); + +/* Perform compaction using GPU Prefix sum */ +/* @param in Input array to scatter */ +/* @param threshold Value to threshold the input array on (using >) */ +/* @param out Output array of condensed data meeting the threshold */ +/* @param size The integer size of in */ +/* @return The integer size of out */ +template +int gpuCompact(const T* in, const T threshold, T* out, int size); + +// Instances of the templates (need to be compiled) +void gpuNaivePrefixSumF(const float* in, float* out, int size); +void gpuNaivePrefixSumI(const int* in, int* out, int size); +//void gpuOneBlockPrefixSumF(const float* in, float* out, int size); //WHY DOESN"T CUDA LET ME COMPILE BOTH HERE?! +void gpuOneBlockPrefixSumI(const int* in, int* out, int size); +void gpuNBlockPrefixSumI(const int* in, int* out, int size); +void gpuScatterI(const int* in, const int threshold, int* out, int size); +void gpuScatterF(const float* in, const float threshold, int* out, int size); +int gpuCompactI(const int* in, const int threshold, int* out, int size); +int gpuCompactF(const float* in, const float threshold, float* out, int size); + +#endif //PREFIX_SUM_H_ diff --git a/StreamCompaction/StreamCompaction/thrust_compact.cu b/StreamCompaction/StreamCompaction/thrust_compact.cu new file mode 100644 index 0000000..ce61de8 --- /dev/null +++ b/StreamCompaction/StreamCompaction/thrust_compact.cu @@ -0,0 +1,36 @@ + +#include +#include "thrust_compact.h" + +// Predicate for Thrust +template +struct isGreaterThan5 { + __host__ __device__ bool operator() (const T& in) { + return (in > 5); + } +}; + + +int thrustCompactI(const int* in, const int threshold, int* out, int size) { + /* + // Convert the input to thrust formats + thrust::host_ptr h_ptr_in(in); + thrust::fill(h_ptr_in, h_ptr_in+N, ) + thrust::host_ptr h_ptr_out(out); + + // Copy data to the device + thrust::device_vector d_vec_in = h_vec_in; + thrust::device_vector d_vec_out = h_vec_out; + + // Execute stream compaction + thrust::device_vector::iterator d_it = thrust::copy_if(d_vec_in.begin(), d_vec_in.end(), d_vec_out.begin(), isGreaterThan5()); + + // Copy the result vector back + h_vec_out = d_vec_out; + //thrust::host_vector::iterator h_it = d_it; + + // Convert the output back from thrust formats + out = thrust::raw_pointer_cast(&h_vec_out[0]); + */ + return 0;//(h_it - h_vec_out.begin()); +} diff --git a/StreamCompaction/StreamCompaction/thrust_compact.h b/StreamCompaction/StreamCompaction/thrust_compact.h new file mode 100644 index 0000000..9d6c360 --- /dev/null +++ b/StreamCompaction/StreamCompaction/thrust_compact.h @@ -0,0 +1,15 @@ +#ifndef THRUST_COMPACT_H_ +#define THRUST_COMPACT_H_ + +// Thrust Dependencies +#include + +/* Perform compaction using GPU with Thrust libraries */ +/* @param in Input array to scatter */ +/* @param threshold Value to threshold the input array on (using >) */ +/* @param out Output array of condensed data meeting the threshold */ +/* @param size The integer size of in */ +/* @return The integer size of out */ +int thrustCompactI(const int* in, const int threshold, int* out, int size); + +#endif // THRUST_COMPACT_H_ diff --git a/StreamCompaction/StreamCompaction/timing_utils.cu b/StreamCompaction/StreamCompaction/timing_utils.cu new file mode 100644 index 0000000..bb9809f --- /dev/null +++ b/StreamCompaction/StreamCompaction/timing_utils.cu @@ -0,0 +1,32 @@ + +// CUDA Dependencies +#include + +// Project Dependencies +#include "timing_utils.h" + +//Global data +cudaEvent_t beginEvent, endEvent; + +void startTiming() { + //Add timing options + cudaEventCreate( &beginEvent ); + cudaEventCreate( &endEvent ); + + //Execute the naive prefix sum and compute the time (in milliseconds) + cudaEventRecord(beginEvent, 0); +} + +float stopTiming() { + float time; + + cudaEventRecord(endEvent, 0); + cudaEventSynchronize(endEvent); + cudaEventElapsedTime(&time, beginEvent, endEvent); + + //Cleanup timers + cudaEventDestroy(beginEvent); + cudaEventDestroy(endEvent); + + return time; +} \ No newline at end of file diff --git a/StreamCompaction/StreamCompaction/timing_utils.h b/StreamCompaction/StreamCompaction/timing_utils.h new file mode 100644 index 0000000..ffdd450 --- /dev/null +++ b/StreamCompaction/StreamCompaction/timing_utils.h @@ -0,0 +1,11 @@ +#ifndef TIMING_UTILS_H_ +#define TIMING_UTILS_H_ + +/* Function to mark the timer to start */ +void startTiming(); + +/* Function to mark the timer to end and return elapsed time */ +/* @return Time (in ms) since startTiming was called */ +float stopTiming(); + +#endif // TIMING_UTILS_H_ From 4501cb13b51a894fc18204e9cb282f7b960720d1 Mon Sep 17 00:00:00 2001 From: Dave Kotfis Date: Mon, 29 Sep 2014 01:26:15 -0400 Subject: [PATCH 2/2] Updated readme and n-block code. --- README.md | 156 ++++-------------- SharedMemory.png | Bin 35265 -> 0 bytes .../StreamCompaction/prefix_sum.cu | 43 ++++- 3 files changed, 64 insertions(+), 135 deletions(-) delete mode 100644 SharedMemory.png diff --git a/README.md b/README.md index 6e02afa..2c91913 100644 --- a/README.md +++ b/README.md @@ -3,131 +3,31 @@ Project-2 A Study in Parallel Algorithms : Stream Compaction -# INTRODUCTION -Many of the algorithms you have learned thus far in your career have typically -been developed from a serial standpoint. When it comes to GPUs, we are mainly -looking at massively parallel work. Thus, it is necessary to reorient our -thinking. In this project, we will be implementing a couple different versions -of prefix sum. We will start with a simple single thread serial CPU version, -and then move to a naive GPU version. Each part of this homework is meant to -follow the logic of the previous parts, so please do not do this homework out of -order. - -This project will serve as a stream compaction library that you may use (and -will want to use) in your -future projects. For that reason, we suggest you create proper header and CUDA -files so that you can reuse this code later. You may want to create a separate -cpp file that contains your main function so that you can test the code you -write. - -# OVERVIEW -Stream compaction is broken down into two parts: (1) scan, and (2) scatter. - -## SCAN -Scan or prefix sum is the summation of the elements in an array such that the -resulting array is the summation of the terms before it. Prefix sum can either -be inclusive, meaning the current term is a summation of all the elements before -it and itself, or exclusive, meaning the current term is a summation of all -elements before it excluding itself. - -Inclusive: - -In : [ 3 4 6 7 9 10 ] - -Out : [ 3 7 13 20 29 39 ] - -Exclusive - -In : [ 3 4 6 7 9 10 ] - -Out : [ 0 3 7 13 20 29 ] - -Note that the resulting prefix sum will always be n + 1 elements if the input -array is of length n. Similarly, the first element of the exclusive prefix sum -will always be 0. In the following sections, all references to prefix sum will -be to the exclusive version of prefix sum. - -## SCATTER -The scatter section of stream compaction takes the results of the previous scan -in order to reorder the elements to form a compact array. - -For example, let's say we have the following array: -[ 0 0 3 4 0 6 6 7 0 1 ] - -We would only like to consider the non-zero elements in this zero, so we would -like to compact it into the following array: -[ 3 4 6 6 7 1 ] - -We can perform a transform on input array to transform it into a boolean array: - -In : [ 0 0 3 4 0 6 6 7 0 1 ] - -Out : [ 0 0 1 1 0 1 1 1 0 1 ] - -Performing a scan on the output, we get the following array : - -In : [ 0 0 1 1 0 1 1 1 0 1 ] - -Out : [ 0 0 0 1 2 2 3 4 5 5 ] - -Notice that the output array produces a corresponding index array that we can -use to create the resulting array for stream compaction. - -# PART 1 : REVIEW OF PREFIX SUM -Given the definition of exclusive prefix sum, please write a serial CPU version -of prefix sum. You may write this in the cpp file to separate this from the -CUDA code you will be writing in your .cu file. - -# PART 2 : NAIVE PREFIX SUM -We will now parallelize this the previous section's code. Recall from lecture -that we can parallelize this using a series of kernel calls. In this portion, -you are NOT allowed to use shared memory. - -### Questions -* Compare this version to the serial version of exclusive prefix scan. Please - include a table of how the runtimes compare on different lengths of arrays. -* Plot a graph of the comparison and write a short explanation of the phenomenon you - see here. - -# PART 3 : OPTIMIZING PREFIX SUM -In the previous section we did not take into account shared memory. In the -previous section, we kept everything in global memory, which is much slower than -shared memory. - -## PART 3a : Write prefix sum for a single block -Shared memory is accessible to threads of a block. Please write a version of -prefix sum that works on a single block. - -## PART 3b : Generalizing to arrays of any length. -Taking the previous portion, please write a version that generalizes prefix sum -to arbitrary length arrays, this includes arrays that will not fit on one block. - -### Questions -* Compare this version to the parallel prefix sum using global memory. -* Plot a graph of the comparison and write a short explanation of the phenomenon - you see here. - -# PART 4 : ADDING SCATTER -First create a serial version of scatter by expanding the serial version of -prefix sum. Then create a GPU version of scatter. Combine the function call -such that, given an array, you can call stream compact and it will compact the -array for you. Finally, write a version using thrust. - -### Questions -* Compare your version of stream compact to your version using thrust. How do - they compare? How might you optimize yours more, or how might thrust's stream - compact be optimized. - -# EXTRA CREDIT (+10) -For extra credit, please optimize your prefix sum for work parallelism and to -deal with bank conflicts. Information on this can be found in the GPU Gems -chapter listed in the references. - -# SUBMISSION -Please answer all the questions in each of the subsections above and write your -answers in the README by overwriting the README file. In future projects, we -expect your analysis to be similar to the one we have led you through in this -project. Like other projects, please open a pull request and email Harmony. - -# REFERENCES -"Parallel Prefix Sum (Scan) with CUDA." GPU Gems 3. +What I have Done: + 1.) Scan + - CPU + - Naive GPU + - One Block GPU + - N Block GPU + 2.) Scatter + - CPU + - GPU (w/ One Block) + 3.) Compact + - CPU + - GPU (w/ One Block) + +What I have Started: + - Compaction using Thrust. I put this to the side once I had trouble with device/host vectors and lots of crashing my system. The thrust documentation is pretty confusing, and the compiler wasn't catching enough of my dumb mistakes to help me get anywhere. + - N Block Scan Optimizations. I suspect that there is a better way to do this than my brute-force-ish approach that doesn't seem like it utilizes parallelism very well. + +Known Bugs: + - Naive Scan and N Block Scan come up short by 1 depth iteration consistently. I can't find the source of this, since the calculation is the same as in One Block. It seems to be imprecision in the floating point operations on the GPU. + - The last element of Compact can be pretty hit or miss. I use extrinsic calculations for all of the intermediary steps, so the value of the last element isn't maintained. + +What I have learned: + - Start earlier. Even though Patrick projected this to take 3-5 hours, putting it off until the weekend wasn't wise. I spent upwards of 15 hours on it and didn't get a chance to do much analysis or optimization. + - Templates + CUDA can be rough. I tried templating all of my stuff so it could be used with arrays of different data types. A lot of times this resulted in strange and unhelpful compiler errors that not even the internet knew anything about. + +Analysis: + - CPU Performance - I've uploaded a screenshot of console output showing CPU speeds for prefix sum for a wide variety of array lengths. It seems as though it is O(1) for these calculations, at least up to the resolution of timing measurement. More analysis could show whether there is a point where this acutally breaks down. Note: all of these are using arrays of floats, not ints. + - Naive Scan Performance - I've uploaded a screenshot of console output showing GPU speeds for scan matching those for the CPU evaluation. For all cases, it is at least 2 orders of magnitude slower than the CPU implementation, though I didn't average the time over 100+ runs which would allow the GPU to start to speed up. After lengths 100,000 and up, the timing seems to go to O(n) once we start to lose enough time going back and forth to global memory. diff --git a/SharedMemory.png b/SharedMemory.png deleted file mode 100644 index f983731919236101725d692ae172ccaaa810a8ac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 35265 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 XGwQ8ucLqO!Ukl$}{MPX|L$?1D?c __global__ void n_block_prefix_sum(T* in, T* out, int* size) { - int index = blockIdx.x * blockDim.x + threadIdx.x; //Keep it simple, only use x since arrays are 1 dimensional + int index = blockIdx.x * blockDim.x + threadIdx.x; //can't keep it simple anymore int s_index = threadIdx.x; // Create shared memory extern __shared__ T s[]; T* in_s = &s[0]; - T* out_s = &s[*size]; + T* out_s = &s[blockDim.x]; + __shared__ float lower_tile_value; - //Start by shifting right since the calculation is going to be inclusive and we want exclusive //Load into shared memory + //Start by shifting right since the calculation is going to be inclusive and we want exclusive if (index > 0) { in_s[s_index] = in[index-1]; } else { @@ -141,7 +142,7 @@ __global__ void n_block_prefix_sum(T* in, T* out, int* size) { __syncthreads(); //Calculate the max depth - int max_depth = ceil(log((float) *size)/log(2.0f)); + int max_depth = ceil(log((float) blockDim.x)/log(2.0f)); //Loop over each depth for (int d = 1; d <= max_depth; d++) { @@ -184,6 +185,34 @@ __global__ void n_block_prefix_sum(T* in, T* out, int* size) { out[index] = in_s[s_index]; } + __syncthreads(); + + //Determine the number of additional loops that will be required + int kernel_calls = ceil(log((float) gridDim.x)/log(2.0f))+1; + + //Loop over the kernel calls, doing a pseudo-serial scan over the remaining layers + for (int k = 0; k < kernel_calls; k++) { + + //Swap out and in + T* temp = in; + in = out; + out = temp; + + //Load the needed value for this tile into shared memory + if (s_index == 0) { + if (blockIdx.x >= (int) pow(2.0f, k)) { + lower_tile_value = in[(blockIdx.x + 1 - (int) pow(2.0f, k))*blockDim.x - 1]; + } else { + lower_tile_value = 0.0f; + } + } + __syncthreads(); + + //Add to the output + out[index] = in[index] + lower_tile_value; + __syncthreads(); + } + } template @@ -217,6 +246,7 @@ void gpuNaivePrefixSum(const T* in, T* out, int size) { //Call the kernel naive_prefix_sum<<<1,size>>>(in_d, out_d, size_d); + cudaDeviceSynchronize(); //Copy data from GPU cudaMemcpy(out, out_d, size*sizeof(T), cudaMemcpyDeviceToHost); @@ -268,10 +298,9 @@ void gpuNBlockPrefixSum(const T* in, T* out, int size) { //Determine the needed number of blocks/threads int needed_bytes = 2*size*sizeof(T); - int n_blocks = 16384/needed_bytes; - int threads_per_block = size/n_blocks; + int threads_per_block = 16384/needed_bytes; + int n_blocks = size/threads_per_block; - //Call the kernel n_block_prefix_sum<<>>(in_d, out_d, size_d); cudaDeviceSynchronize();