Changes

Jump to: navigation, search

N/A

10,106 bytes added, 02:57, 7 April 2019
Assignment 3
== Team Members ==
# [mailto:wwpark@myseneca.ca?subject=DPS915 Woosle Park], Data Compression
# [mailto:apatel271@myseneca.ca?subject=DPS915 Akshat Patel], Sorting Algorithms
# [mailto:jpitters@myseneca.ca?subject=DPS915 Jordan Pitters], Image Processing
# [mailto:apatel271@myseneca.ca?subject=DPS915 Akshatkumar Patel], Sorting Algorithms
[mailto:jpitters@myseneca.ca;wwpark@myseneca.ca?subject=DPS915;apatel271@myseneca.ca?subject=DPS915 Email All];
This of time is spent in the compress function and the hashtable takes up most of the time because it is constantly being manipulated and read from. It looks like if the hashtable and the compress function were to be parallelized about 90% of the run time would be affected. The big-O for the application should be O(n) time so there is a linear increase in time based on file size, however the hashtable grew more in time compared to the compress function the larger the file. This application is not good for parallelization because of the dictionary hashtable. Due to the hastable or the compress dictionary needing to be accessible globally and be constantly modifiable and read this could pose issues if multiple threads were running especially since modifying and reading the table needs to be done sequentially for efficient compression. Threading the compress could lead to errors in compressions making this difficult to parallelize.
 
----
----
'''Application 3 - Sorting Algorithms'''
'''Description:'''
I decided to select an option from the suggested projects – Sorting Algorithms. The sorting algorithms I included were: Bubble, Insertion, Selection, Heap, Merge, Heap, and Quicksort. In order I decided to profile create an application that uses all of the sorting algorithms all at once and have calls their respective functions instead of creating individual modules and profiling them, simply because the 99% percent of the total running time would be taken up by the sorting algorithm function. So for a better understanding and comparison of what the time taken by each sorting algorithm uses the most time, I created decided to create a single module which with functions that perform the sorting algorithms. I allocated memory for all of the arrays and populated a single array of size ''n'' with randomly generated data. I then copied the memory from the populated array to all of the other arrays to ensure consistency amongst the comparison consistent data throughout all of the sorting algorithms. I then passed each array to its respective sorting function which returned the sorted array using pass-by-reference. One of the things to keep in mind is that when ''n '' increases (the size of the array being sorted), the time increases. I have included 3 different profiles where n (the size of the array) equals 50000, 100000 and lastly 50000.
'''Source Code:'''
#include <iostream> [[File:SortingAlgorithms.cpp.txt]]#include <cstdlib>#include <cstring>#include <vector> The link to the source code AND the make file can also be found on GitHub at: [https://github.com/Akshat55/Sorting-Algorithms-Comparison https://github.com/Akshat55/Sorting-Algorithms-Comparison]
//Generates random numbers and assigns them to the array
void fillArray(int* arr, int size) {
for (int i = 0; i < size; i++) {
arr[i] = rand() % size;
}
}
'''N = 50,000'''
Total Time (seconds) = 7.98 seconds
Flat profile:
Each sample counts as 0.01 seconds. % cumulative self self total time seconds seconds calls ms/call ms/Performs Bubble Sort on the array passed through parametercall name void 63.21 5.04 5.04 bubbleSort(int *arr, int size) { for 29.91 7.42 2.38 selectionSort(int i = 0; i < size - 1; i++*, int) { for 6.79 7.96 0.54 insertionSort(int j = 0; j < size - i - 1; j++*, int) { if 0.13 7.97 0.01 49999 0.00 0.00 merge(arr[j] int*, std::vector<int, std::allocator<int> arr[j + 1]>&, int, int, int) { 0.13 7.98 0.01 quickSort(int tmp = arr[j];*, int, int) arr[j] = arr[j + 0.00 7.98 0.00 8 0.00 1];.25 mergeIndexSort(int*, std::vector<int, std::allocator<int> >&, int, int) arr[j + 0.00 7.98 0.00 1] = tmp; } } }} 0.00 0.00 _GLOBAL__sub_I__Z9fillArrayPii
'''N = 100,000'''
Total Time (seconds) = 31.42 seconds
Flat profile:
Each sample counts as 0.01 seconds. % cumulative self self total time seconds seconds calls ms/call ms/Performs Selection Sort on the array passed through parametercall name 63.91 20.05 20.05 bubbleSort(int*, int)void 29.15 29.19 9.14 selectionSort(int *arr, int size) { for 6.96 31.38 2.18 insertionSort(int i = 0; i < size; i++*, int) { 0.06 31.40 0.02 heapSort(int*, int minIdx = i;) for 0.03 31.41 0.01 99999 0.00 0.00 merge(int j = i; j *, std::vector< size; j++int, std::allocator<int> >&, int, int, int) { if 0.03 31.42 0.01 quickSort(arr[j] < arr[minIdx]int*, int, int) { minIdx = j; } } //swap 0.00 31.42 0.00 8 0.00 1.25 mergeIndexSort(int*, std::vector<int, std::allocator<int> >&, int, int tmp = arr[i];) arr[i] = arr[minIdx]; arr[minIdx] = tmp; }} 0.00 31.42 0.00 1 0.00 0.00 _GLOBAL__sub_I__Z9fillArrayPii
'''N = 500,000'''
Total Time (minutes) = 13.47 minutes
Flat profile:
Each sample counts as 0.01 seconds. % cumulative self self total time seconds seconds calls ms/call ms/Performs Insertion Sort on the array passed through parametercall name void 62.36 503.32 503.32 bubbleSort(int*, int) 30.67 750.86 247.54 selectionSort(int*, int) 7.08 807.99 57.14 insertionSort(int *arr, int size) { for 0.02 808.12 0.13 heapSort(int i = *, int) 0.01 808.20 0.08 499999 0.00 0; i .00 merge(int*, std::vector< size; i++int, std::allocator<int> >&, int, int, int) { 0.01 808.26 0.06 quickSort(int curr = arr[i]; *, int, int j = i;) for 0.00 808.26 0.00 8 0.00 10.01 mergeIndexSort(j = i; j int*, std::vector<int, std::allocator<int> > 0 && arr[j - 1] > curr; j--, int, int) { arr[j] = arr[j - 0.00 808.26 0.00 1]; } arr[j] = curr; }} 0.00 0.00 _GLOBAL__sub_I__Z9fillArrayPii
'''Conclusion:'''
Based on the results from profiling 3 different sizes of arrays, we can assume that majority of the time taken to sort the arrays is taken by the O(n^2) algorithms (Bubble, Insertion, and Selection). However, the other sorting algorithms (Heap, Merge, Quick) that are O(n log(n)) are extremely fast even when the size of the arrays are large. As observed from the profile, the elapsed time increased as the size of the array increased. I went from approximately 8 seconds to execute the entire program to 13 minutes to execute.
void heapify(int *arr, int n, int i){ int largest = i; // Initialize largest as root int l = 2 * i + 1; // left = 2*i + 1 int r = 2 * i + 2; // right = 2*i + 2 ----
// If left child is larger than root if (l < n && arr[l] > arr[largest]) largest = l;'''Final Selection: Sorting Algorithms'''
// If right child is larger than largest Based on the profiled applications above, we think that the sorting algorithms would benefit a lot from offloading to the GPU. Sorting Algorithms are commonly used in programming and can have a strong impact on the programs speed and efficiency. Since they are so far commonly used, we think it would be quite interesting to see if we can speed up the O(r < n && arr[r] > arr[largest]^2) largest = r;sorting algorithms to potentially match the sorting speed of the O(n log n) algorithms since there won’t much change for the parallelized version of them, as they are already fast. Bubble Sort is an elementary sort, it is the most basic sorting method out there however it is the worst sorting algorithm. By Speeding it up using the GPU, we plan to see how much we can improve this sorting algorithm. Also, we would like to look at Insertion Sort, mainly because we know that it is not possible to do on the GPU. We want to take this opportunity to be innovative and attempt or to the very least find a way to solve this issue.
// If largest is not root if (largest != i) { std'''Summary of selection::swap(arr[i], arr[largest]);'''
// Recursively heapify the affected sub-tree heapify(arr, n, largest); }}Bubble & Insertion Sort
=== Assignment 2 ===
//Performs Heap Sort on the array passed through parameter//Acts as a wrapper functionvoid heapSort(int *arr'''Akshatkumar Patel, Jordan Pitters, int size) {Woosle Park''' // Build heap (rearrange array) for (int i = size / 2 - 1; i >= 0; i--)- heapify(arr, size, i);Sorting algorithm parallelization completion:
// One by one extract an element from heap
for (int i = size - 1; i >= 0; i--)
{
// Move current root to end
std::swap(arr[0], arr[i]);
// call max heapify on We found a pseudo code for this algorithm which we got from the reduced heap heapify(arr, i, 0); }}following link. The link describes bubble sort & how to parallelize it.
'''Serial Bubble Sort Pseudo'''
for i ← 1 to length [A] do
for j ← length [A] downto i +1 do
If A[A] < A[j-1] then
Exchange A[j] ↔ A[j-1]
//Performs Quick '''Parallel Bubble Sort on the array passed through parametervoid quickSort(int *arr, int left, int right) { int leftIdx = left, rightIdx = right; int tmp; int pivot = arr[(left + right) / 2];Pseudo'''
/* partition * For k = 0 to n-2 If k is even then for i = 0 to (n/2)-1 do in parallel If A[2i] > A[2i+1] then Exchange A[2i] ↔ A[2i+1] Else while for i = 0 to (leftIdx <= rightIdxn/2) {-2 do in parallel while (arr If A[2i+1] > A[leftIdx2i+2] < pivot)then leftIdx Exchange A[2i+1] ↔ A[2i+;2] Next k
while (arr[rightIdx] > pivot)
rightIdx--;
After doing some further research, we found out this presumed bubble sort is a variation of a bubble sort. It is known as an odd-even bubble sort. A bubble sort works by repeatedly iterating through the list and comparing adjacent pairs and swapping them if they are in the wrong order. This is an elementary sorting algorithm; it is the basic learning block to all sorting algorithms. It is quite easy to implement but it is extremely slow in comparison to its counterparts (leftIdx <= rightIdxquick, merge, heap, & insertion) { tmp = arr[leftIdx]; arr[leftIdx] = arr[rightIdx]; arr[rightIdx] = tmp; leftIdx++; rightIdx--; } };.
/* recursion */
if (left < rightIdx)
quickSort(arr, left, rightIdx);
if (leftIdx < right) quickSort(arr, leftIdx, right);}'''Time Complexity'''
Bubble sort has a worse case and average complexity of O(n^2), where n is the number of items being sorted. Surprisingly even all the other O(n^2) algorithms such as insertion and selection sort run faster than bubble (As shown in the profile in A1). We know from experience that this algorithm is inefficient and should be avoided in case of large collections (Collection size depends on system specs, but we will just assume 20K is the average).
//Merges the arrays and sorts it
void merge(int *arr, std::vector<int>& tmp, int start, int start2, int end) {
int lptr = start;
int rptr = start2;
int i = start;
while (lptr < start2 && rptr <= end) {
if (arr[lptr] < arr[rptr])
tmp[i++] = arr[lptr++];
else
tmp[i++] = arr[rptr++];
}
while (lptr < start2) {
tmp[i++] = arr[lptr++];
}
while (rptr <= end) {
tmp[i++] = arr[rptr++];
}
for (i = start; i <= end; i++) {
arr[i] = tmp[i];
}
}
'''Time Complexity of Parallel Bubble Sort'''
Based on the given pseudo code above, we iterate through the list (n) number of times and call the odd or even kernel based on the index (i). We then iterate through the list n/2 number of times making it O(n^2)/n.
//this function sorts arr from index start to end
void mergeIndexSort(int* arr, std::vector<int>& tmp, int start, int end) {
if (start < end) {
int mid = (start + end) / 2;
mergeIndexSort(arr, tmp, start, mid);
mergeIndexSort(arr, tmp, mid + 1, end);
merge(arr, tmp, start, mid + 1, end);
}
}
'''Our Approach'''
We iterate through the list (n-1) number of times calling the kernel. We make use of the power of the GPU which is doing the same thing concurrently. In this case, we will be launching 512 threads of n/1024 blocks (We found this to be the most efficient block & thread counts). When we launch a kernel, the kernel will concurrently compare 2 values. The only difference to our approach and the pseudo approach is that we will need to pass in a flag (index % 2) to let our kernel know whether it is even or odd. Since we sped up our iteration to be iterating only once (n number of times), we can assume that the parallel time complexity is O(n) and needs O(n) processors.
//Acts as a wrapper function
void mergeSort(int* arr, int size) {
std::vector<int> tmp(size);
//call mergeSort with the array, the temporary
//and the starting and ending indices of the array
mergeIndexSort(arr, tmp, 0, size - 1);
}
__global__ void oddeven_bubble(int *a, int flag, int size)
{
int index = blockIdx.x * blockDim.x + threadIdx.x;
int temp;
if ((index >= size / 2 - 1) && flag % 2 != 0)
return;
if (flag % 2 == 0)
{
if (a[index * 2] > a[index * 2 + 1])
{
temp = a[index * 2];
a[index * 2] = a[index * 2 + 1];
a[index * 2 + 1] = temp;
}
}
else
{
if (a[index * 2 + 1] > a[index * 2 + 2])
{
temp = a[index * 2 + 1];
a[index * 2 + 1] = a[index * 2 + 2];
a[index * 2 + 2] = temp;
}
}
}
//Print the values This Algorithm were created & profiled on Tesla K80 GPU (Not used due Cloud – Google Collab). In order to big size of array)void print(int *arr, int size){ for (int i = 0; i < size; i++) { std::cout << arr[i] << " "; } std::cout << std:compile them the follow was typed:endl;}
!nvcc bubble.cu -o bubble
'''Parallel Odd-Even Sort Comparison to Bubble & Overview'''
int mainOur Odd-even sort is uses N independent threads to do sort the number of elements in the list. The number of blocks used is (int argcsize+1024)/1024 * 512 threads to sort the elements in the list. Hence, char *argv[]) {we can assume N = half of size. The odd/even sorts by selecting every even array index & comparing with the adjacent and odd array index as observed an be observed in the image below. If the numbers are in the wrong order, a swap takes place. This process is repeated size-1 number of times.
//Get the size of the array int n = std::atoi(argv[1]);'''Observation'''
// Create 6 arrays Offloading the computing intensive sorting algorithm to the GPU resulted in DRASTIC increase in speed. We were able to take an algorithm that took about 13 minutes to sort a list of size n 500k randomly generated values and allocate memory for them int *bubbleArray = new int[n]; int *selectionArray = new int[n]; int *insertionArray = new int[n]; int *heapArray = new int[n]; int *mergeArray = new int[n]; int *quickArray = new int[n];solve it within 15 seconds.
//Fill the array with randomly generated numbers fillArray(bubbleArray, n); //print(bubbleArray, n);'''Profile for our bubble sort'''
//COPY the values Command used to the other //This adds consistency among the elements of the array stdgenerate profile::memcpy(selectionArray, bubbleArray, n*sizeof(int)); std::memcpy(insertionArray, bubbleArray, n * sizeof(int)); std::memcpy(heapArray, bubbleArray, n * sizeof(int)); std::memcpy(mergeArray, bubbleArray, n * sizeof(int)); std::memcpy(quickArray, bubbleArray, n * sizeof(int));
/ !nvprof ./Call the sorting algorithms 1 by 1 with their respecive array bubbleSort(bubbleArray, n); std::cout << "Bubble Sort performed." << std::endl; selectionSort(selectionArray, n); std::cout << "Selection Sort performed." << std::endl; insertionSort(insertionArray, n); std::cout << "Insertion Sort performed." << std::endl; heapSort(heapArray, n); std::cout << "Heap Sort performed." << std::endl; mergeSort(mergeArray, n); std::cout << "Merge Sort performed." << std::endl; quickSort(quickArray, 0, n-1); std::cout << "Quick Sort performed." << std::endl;bubble 500000
'''Profile generated for N = 500000'''
==437== NVPROF is profiling process 437, command: ./bubble 500000 ==437== Profiling application: ./Deallocate the arraysbubble 500000 ==437== Profiling result: Type Time(%) Time Calls Avg Min Max Name GPU activities: 100.00% 15.6692s 499999 31.338us 23.360us 47.807us oddeven_bubble(int*, int, int) delete 0.00% 324.09us 1 324.09us 324.09us 324.09us [CUDA memcpy HtoD] bubbleArray; delete 0.00% 258.27us 1 258.27us 258.27us 258.27us [CUDA memcpy DtoH] selectionArray; delete[] insertionArray; API calls: 98.63% 17.9870s 499999 35.974us 5.6240us 6.0456ms cudaLaunchKernel 0.85% 155.57ms 1 155.57ms 155.57ms 155.57ms cudaMalloc 0.33% 60.878ms 1 60.878ms 60.878ms 60.878ms cudaDeviceReset 0.17% 31.076ms 1 31.076ms 31.076ms 31.076ms cudaDeviceSynchronize 0.00% 835.79us 2 417.90us 407.55us 428.25us cudaMemcpy 0.00% 328.55us 96 3.4220us 156ns 146.17us cuDeviceGetAttribute 0.00% 264.00us 1 264.00us 264.00us 264.00us cudaFree 0.00% 187.13us 1 187.13us 187.13us 187.13us cuDeviceTotalMem 0.00% 26.678us 1 26.678us 26.678us 26.678us cuDeviceGetName 0.00% 2.9030us 1 2.9030us 2.9030us 2.9030us cuDeviceGetPCIBusId delete[] heapArray; 0.00% 2.1570us 3 719ns 210ns 1.2000us cuDeviceGetCount delete[] mergeArray; 0.00% 1.3290us 2 664ns 305ns 1.0240us cuDeviceGet delete[] quickArray; 0.00% 304ns 1 304ns 304ns 304ns cuDeviceGetUuid
return 0;}----
'''Insertion Sort'''
We stated in A1 that we wanted to attempt Insertion sort in order to learn & be innovate.
 
'''Kernel Code:'''
 
__global__ void Sort(int* arr, int size) {
for (int i = threadIdx.x; i < size; i++) {
int curr = arr[i];
int j = i;
for (j = i; j > 0 && arr[j - 1] > curr; j--) {
arr[j] = arr[j - 1];
}
arr[j] = curr;
}
}
 
 
'''Our Scenario'''
We actually thought we had it working after multiple iterations and adding __syncthreads() everywhere. Suddenly for array size 100000 it worked. We tested it for 200000 to 500000, they all worked extremely fast (about 2x faster than CPU). We were quite surprised with our findings, but something felt wrong. We tested the kernel with size 512 - 25000 and they were all incorrect. We were a bit curious to know what was happening, why there were inconsistencies and lastly how our tester function was returning 0 errors for big arrays and errors for small arrays. This required us to come up with a solution to test our tester & the # of same values generated to the # of values in the so called sorted array.
 
 
'''Here is how we found out that the values were getting overridden:'''
 
We created two separate arrays of size n (value passed through the argument), first array held the random values and the second was initialized to 0.
 
 
Our goal was to find out why it worked for some values and it didn’t work for some. Generally, the lower values did not work ( < 50000) and the big values worked according to our original tester. This made us curious to know why our kernel worked for big values but not small values.
 
 
In order to do so, we created a hash table sort of array structure. Since the insertionArray (array that held random values) had only values 0 < n < argument (Ex.500000), we simply iterated through the list at the start before passing the array on to the kernel. In this process, we iterated through the randomly numbers and added incremented the count array for each index we found (countArray[insertionArray[i]]++).
 
 
Then we simply performed the same process on the “sorted” array to find out that 50% of the values would be missing. We tried multiple methods for the insertion sort, even syncing the threads after every step taking. Unfortunately, this increased the time drastically and still did not work.
'''N = 50,000'''
Flat profile:
Each sample counts as 0The logic of insertion sort cannot be applied to the kernel because the threads will be competing against one another instead of working together.01 secondsEx. All the threads would try to replace arr[0] all at once. This is done multiple times throughout the array and this is the reason why we lost 50% cumulative self self total time seconds seconds calls ms/call ms/call name 63.21 5.04 5of the data.04 bubbleSortThe data lost in a way correlates to the number of threads launched (int*, int) 29.91 7.42 2.38 selectionSort(int*, intsize + 512) 6.79 7.96 0.54 insertionSort(int*, int/512) 0.13 7.97 0.01 49999 0.00 0.00 merge(int*, std::vector<int, std::allocator<int> >&, int, int, int512) 0.13 7.98 0.01 quickSort(int*, int, int) 0.00 7.98 0.00 8 0.00 when we launch 1 block of 1.25 mergeIndexSort(int*thread, std::vector<int, std::allocator<int> >&, int, int) 0.00 7.98 0.00 1 0.00 0it works but that is because we do not utilize the power of the GPU to compute multiple things at once.00 _GLOBAL__sub_I__Z9fillArrayPii
'''N = 100CUDA is designed to allow blocks to run in any order. There is also no cross-block synchronization within a single run making insertion sort impossible to work along with a lot of other sorting algorithms. In fact,000'''Flat profile:this is the problem with many of the sorting algorithms we have looked at.
Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls ms/call ms/call name
63.91 20.05 20.05 bubbleSort(int*, int)
29.15 29.19 9.14 selectionSort(int*, int)
6.96 31.38 2.18 insertionSort(int*, int)
0.06 31.40 0.02 heapSort(int*, int)
0.03 31.41 0.01 99999 0.00 0.00 merge(int*, std::vector<int, std::allocator<int> >&, int, int, int)
0.03 31.42 0.01 quickSort(int*, int, int)
0.00 31.42 0.00 8 0.00 1.25 mergeIndexSort(int*, std::vector<int, std::allocator<int> >&, int, int)
0.00 31.42 0.00 1 0.00 0.00 _GLOBAL__sub_I__Z9fillArrayPii
'''Final files & Comparitive Graph'''
'''N = 500Graph Comparison: - Please give it a few seconds to load,000we noticed it takes longer than expected to load the graph'''Flat profile:
Each sample counts as 0https://docs.01 secondsgoogle. % cumulative self self total time seconds seconds calls mscom/spreadsheets/call msd/call name 62.36 503.32 503.32 bubbleSort(int*, int) 30.67 750.86 247.54 selectionSort(int*, int) 7.08 807.99 57.14 insertionSort(int*, int) 0.02 808.12 0.13 heapSort(int*, int) 0.01 808.20 0.08 499999 0.00 0.00 merge(int*, std::vector<int, std::allocator<int> >&, int, int, int) 0.01 808.26 0.06 quickSort(int*, int, int) 0.00 808.26 0.00 8 0.00 10.01 mergeIndexSort(int*, std::vector<int, std::allocator<int> >&, int, int) 0.00 808.26 0.00 1 0.00 0.00 _GLOBAL__sub_I__Z9fillArrayPii176TTtES25qwWpm18aDRkPYDCF2EMKmsIGz3k0iuVafk/edit?usp=sharing
'''Final files:'''
'''Bubble.cu:''' [[File:Bubble.cu.txt]]
'''Insertion.cu (With method to test):''' [[File:Insertion.cu.txt]]
=== Assignment 2 ===
=== Assignment 3 ===
 
Bubble sort - Optimization
----
 
'''Thoughts about using Shared Memory:'''
 
After doing some research, we believe that we cannot use __shared__ memory for this. The reason for this is because the array size can be anything (500 thousand, 1 million, 2 million, etc.). There is very limited amount of __shared__ memory depending on the GPU. Even if we used __shared__ memory, there is always the issue that only the values within each block would get sorted. We would also need to deal with bank conflicts (Thread Id would access memory of id id & id+1 and write to memory at id).
 
 
'''Our attempting at reducing the kernel counts:'''
 
For this we removed the for loop that called the kernel n-1 times, and added a for loop inside the kernel surrounding the condition statement (if-else). This was quite interesting in the sense that it was much faster (about 30 – 50%) than the method we had derived from the pseudo code. Unfortunately, about 1% of size (argument entered) would be incorrectly sorted. When we ran this code on different types of GPU with a higher & lower compute capability, we had fewer and more errors respectively however the sorting was still incorrect. So overall, we decided to stick with our multiple kernel call approach as we didn’t want to have errors with the benefit of faster completion time.
 
----
'''Final thoughts:'''
 
Bubble sort was not able to be speed up as fast as quick/merge/heap sort O(n * logn) but compared to serial bubble sort we ran on matrix & average pc, there was very noticeable difference. Our overall approach felt a lot like the reduction lab where we called the kernel multiple times to get the answer. We also realized that the optimal solution will be different on different hardware platforms.While doing research, we found out there are even better parallel sorting algorithms (radix, rank sort, parallel merge sort, hyper quick sort). We actually found the CUDA sample code for parallel merge sort (It was for older version of CUDA). When we profiled it and ran it, it turned out to be much slower (1 – 3 seconds) than serial merge sort for N = 500,000. However, the CUDA quick sort given was just as fast as the serial quick sort (potentially faster by few milliseconds).
 
'''Some of the things we learned'''
 
- Odd-even sort
 
- Parallel Bubble Sort & its implementation
 
- Race Conditions
 
- We CANNOT do Insertion sort in parallel (Confirmed!)
 
- Sequencing issues will be caused for coalesced hardware when acceding array elements 0 & 1 in thread 0, accessing 2 & 3 in thread 1 (Issue with shared memory & why we avoided it).
 
- Shared memory is an optimal choice where we call the kernel once and the kernel has a for loop inside of it. However, causes bank conflict (can be solved using strides?) but unfortunately gave us an incorrect result so we avoided that method.
 
- As the size of array goes up, the number of threads used goes up (size/2 thread are used). This will eventually result in the maximum number of threads being used.
 
- Blocks will run in random orders (One of the reasons why we encountered an error with the single kernel with a for loop inside odd-even approach). We were able make small numbers work & it was much faster but for big numbers approximately 1 percent of the array size would be incorrectly sorted.
 
- Algorithms with alot of branching and are VERY sequential are not suitable for the GPU.
 
- We came across some interesting sorting algorithms (Bucket, radix, rank, hyper quick and merge sort) which are better options than the parallel bubble sort.
46
edits

Navigation menu