diff --git a/README.md b/README.md index 0e38ddb..ea2d7b4 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,171 @@ -CUDA Stream Compaction -====================== +

+

Prefix Sum and Stream Compaction

+

Author: (Charles) Zixin Zhang

+

+ CPU and GPU Implementations of Exclusive Prefix Sum(Scan) Algorithm and Stream Compaction in CUDA C +

+

-**University of Pennsylvania, CIS 565: GPU Programming and Architecture, Project 2** +--- -* (TODO) YOUR NAME HERE - * (TODO) [LinkedIn](), [personal website](), [twitter](), etc. -* Tested on: (TODO) Windows 22, i7-2222 @ 2.22GHz 22GB, GTX 222 222MB (Moore 2222 Lab) +## Features -### (TODO: Your README) +- CPU Scan & Stream Compaction +- Recusive Naive GPU Scan Algorithm Using Shared Memory +- Work-Efficient GPU Scan Using Shared Memory & Stream Compaction +- Thrust's Scan Algorithm -Include analysis, etc. (Remember, this is public, so don't put -anything here that you don't want to share with the world.) +For all GPU Scan algorithms, I choose to implement inclusive Scan first, and then convert the result of inclusive Scan to exclusive Scan. This can be done in parallel with minimal code. + +## Performance Analysis + +![scan](images/scan.png) + +When the array size is under 20,000, CPU Scan performs better than other algorithms. As the array size increases, GPU Naive Scan performs better than the rest of the algorithms. The Thrust implementation has more stable performance than the rest of the algorithms. + +Output when array size is 65536: + +``` + [SM 8.6 NVIDIA GeForce RTX 3080] + Max threads per block: 1024 + Shared memory per block: 49152 bytes + Max threads per SM: 1536 + Max blocks per SM: 16 + Max grid size: 2147483647, 65535, 65535 +**************** +** SCAN TESTS ** +**************** + [ 27 40 6 30 21 41 41 26 20 5 6 29 41 ... 32 0 ] +==== cpu scan, power-of-two ==== + elapsed time: 0.0972ms (std::chrono Measured) + [ 0 27 67 73 103 124 165 206 232 252 257 263 292 ... 1599954 1599986 ] + +==== cpu scan, non-power-of-two ==== + elapsed time: 0.085ms (std::chrono Measured) + [ 0 27 67 73 103 124 165 206 232 252 257 263 292 ... 1599856 1599858 ] + passed + +==== work-efficient scan, power-of-two ==== + elapsed time: 0.178144ms (CUDA Measured) + passed +==== work-efficient scan, non-power-of-two ==== + elapsed time: 0.096544ms (CUDA Measured) + passed +==== naive scan, power-of-two ==== + elapsed time: 0.091232ms (CUDA Measured) + passed +==== naive scan, non-power-of-two ==== + elapsed time: 0.182464ms (CUDA Measured) + passed +==== thrust scan, power-of-two ==== + elapsed time: 0.10432ms (CUDA Measured) + [ 0 27 67 73 103 124 165 206 232 252 257 263 292 ... 1599954 1599986 ] + passed +==== thrust scan, non-power-of-two ==== + elapsed time: 0.075776ms (CUDA Measured) + [ 0 27 67 73 103 124 165 206 232 252 257 263 292 ... 1599856 1599858 ] + passed + +***************************** +** STREAM COMPACTION TESTS ** +***************************** + [ 0 1 0 1 3 3 2 1 0 1 2 1 2 ... 3 0 ] +==== cpu compact without scan, power-of-two ==== + elapsed time: 0.1293ms (std::chrono Measured) + [ 1 1 3 3 2 1 1 2 1 2 2 1 3 ... 3 2 ] + passed +==== cpu compact without scan, non-power-of-two ==== + elapsed time: 0.1319ms (std::chrono Measured) + [ 1 1 3 3 2 1 1 2 1 2 2 1 3 ... 3 3 ] + passed +==== cpu compact with scan ==== + elapsed time: 0.6768ms (std::chrono Measured) + [ 1 1 3 3 2 1 1 2 1 2 2 1 3 ... 3 2 ] + passed +==== work-efficient compact, power-of-two ==== + elapsed time: 0.096544ms (CUDA Measured) + [ 1 1 3 3 2 1 1 2 1 2 2 1 3 ... 3 2 ] + passed +==== work-efficient compact, non-power-of-two ==== + elapsed time: 0.096544ms (CUDA Measured) + [ 1 1 3 3 2 1 1 2 1 2 2 1 3 ... 3 3 ] + passed +Press any key to continue . . . +``` + + + +### Block Size + +RTX 3080 Stats: + +``` + [SM 8.6 NVIDIA GeForce RTX 3080] + Max threads per block: 1024 + Shared memory per block: 49152 bytes + Max threads per SM: 1536 + Max blocks per SM: 16 + Max grid size: 2147483647, 65535, 65535 +``` + +I want to choose a block configuration that would result in the largest number of threads in the SM. + +:heavy_check_mark: 512 threads per block + +- You need 1536/512 = 3 blocks to fully occupy the SM. Fortunately, SM allows up to 16 blocks. Thus, the actual number of threads that can run on this SM is 3 * 512 = 1536. We have occupied 1536/1536 = 100% of the SM. + +## Naive Scan + +- Implemented ```NaiveGPUScan``` using shared memory. +- Each thread is assigned to evolve the contents of one element in the input array. +- This is largely a four step process: + - compute the scan result for individual sections. Then, store their block sum to ```sumArray``` + - scan block sums + - add scanned block sum ```i``` to all values of scanned block ```i + 1``` + - convert from inclusive to exclusive scan + +In my implementation, the naive kernel can process up to 128 elements in each section by using 128 threads in each block. If the input data consists of 1,000,000 elements, we can use ceil(1,000,000 / 128) = 7813 thread blocks. With up to 2147483647 thread blocks in the x-dimension of the grid, the naive kernel can process up to 2147483647 * 128 = around 274 billion elements. + +## Work Efficient Scan + +Understand thread to data mapping: + +```int index = (threadIdx.x + 1) * stride * 2 - 1;``` + +- (threadIdx.x + 1) shifts thread indices from 0, 1, 2, 3, ... to 1, 2, 3, 4, ...All indices become non-zero integers. +- (threadIdx.x + 1) * stride * 2 - 1 + - For example, when stride = 1, we want thread 0 maps to data index [1], thread 1 maps to data index[3], etc. + - (threadIdx.x + 1) * stride * 2 - 1 = (0 + 1) * 1 * 2 - 1 = 1 + - (threadIdx.x + 1) * stride * 2 - 1 = (1 + 1) * 1 * 2 - 1 = 3 + - For example, when stride = 2, we want thread 0 maps to data index [3], thread 1 maps to data index[7], etc. + - (threadIdx.x + 1) * stride * 2 - 1 = (0 + 1) * 2 * 2 - 1 = 3 + - (threadIdx.x + 1) * stride * 2 - 1 = (1 + 1) * 2 * 2 - 1 = 7 + + + +## Bloopers + +### #1 + +``` +CUDA error (d:\dev\565\project2-stream-compaction\stream_compaction\naive.cu:84): memCpy back failed!: an illegal memory access was encountered + +83 cudaMemcpy(odata, d_OutputData, size, cudaMemcpyDeviceToHost); +84 checkCUDAError("memCpy back failed!"); +``` + +- I encountered this error when implementing the naive version (without considering arbirary-length inputs) of the scan algorithm. At first, I suspected the culprit is on line 83 (because the line 84 reports the error). However, the culprit actually resides in my ```kernNaiveGPUScan``` function where I accessed ```XY[-1]``` inside the for loop. +- Fix: Need a if-statement to make sure we never access```XY[-1]```. Also need to make sure ```__syncthreads()``` are **not** in the if-statement. + +> When a ```__syncthread()``` statement is placed in an if-statement, either all or none of the threads in a block execute the path that includes the __syncthreads(). PMPP p.59 + +## Note + +- CPU sequential scan algorithms are linear algorithms and are extremely work-efficient. +- Expected speed: Thrust > GPU Efficient(Brent Kung) >= CPU > Naive GPU (koggle stone) + - Why is Naive GPU slower than CPU ? + - Naive GPU has control divergence in the first warp. Performance hit is worse for smaller block size. + - Naive GPU is not work-efficient. Naive GPU has NlogN - (N - 1), whereas CPU has only (N - 1) + - Why is GPU Efficient quicker? + - reduction step takes N - 1 operations, distribution phase takes N operations. Overall, it is a work-efficient algorithm. diff --git a/images/plotting/.ipynb_checkpoints/CUDA Flocking-checkpoint.ipynb b/images/plotting/.ipynb_checkpoints/CUDA Flocking-checkpoint.ipynb new file mode 100644 index 0000000..fca51ef --- /dev/null +++ b/images/plotting/.ipynb_checkpoints/CUDA Flocking-checkpoint.ipynb @@ -0,0 +1,189 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 39, + "id": "1f1923e1", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYoAAAEWCAYAAAB42tAoAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjMuNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8QVMy6AAAACXBIWXMAAAsTAAALEwEAmpwYAABJmElEQVR4nO3dd3hUVfrA8e+bTiChJHQICb0kEDoIIiC6gEizgSjgoqyLuGvZXXH5rYvuooh1sXcsLIKsAiKi0kFQAQ2E3kIvIYRAIIWU8/vj3oRJT2AmySTv53nyzMy5d845dwjz5txz73nFGINSSilVEI+y7oBSSqnyTQOFUkqpQmmgUEopVSgNFEoppQqlgUIppVShNFAopZQqlAYK5fZE5G0R+YfD6z+KyGkRuSgiQWXZN3clIodEZEBZ90OVDxoolNszxjxojPkXgIh4Ay8DNxtjqhljzpZFn0Skr4gYEflbWbSvlDNpoFAVTV3AD9hR0jeKxVn/J8YB8fajS4iIl6vqVsqRBgpVLth/fTd3eD1bRP5tP+8rIsdE5HERiRWRkyJyX+59RaQlsMcuThCRlfb260Rkk4ictx+vc3jvahGZLiI/AklAU7svk0Rkn4gkisi/RKSZiGwUkQsiMl9EfAo5Fn/gduAhoIWIdHHYFmrXP1FETtjH8rjD9mkiskBE5tlt/yoiHRy2HxKRJ0RkG3BJRLxEZKiI7BCRBPt42jjsP0VEDth17RSREbn6+oCI7HLY3slhc6SIbLM/t3ki4lfEP6OqoDRQKHdRD6gONAQmAG+ISE3HHYwxe4F29ssaxpj+IlIL+AaYBQRhnZb6Jtfcxb3ARCAAOGyXDQQ6Az2AvwHvAmOAxkA4MLqQvt4GXAS+AL4DxuazTz+gBXAzMCXXfMAw+721gP8CC+1TallGA7cANYCmwFzgEaA2sBT42iGQHQCux/rsngY+E5H6ACJyBzDN7l8gMBRwPFV3p/05hAHtgfGFHLOqwDRQKHeRBjxjjEkzxizF+iJuVYz33QLsM8Z8aoxJN8bMBXYDtzrsM9sYs8PenmaXPW+MuWCM2QFsB743xhw0xpwHvgU6FtLmOGCeMSYD64t+dK4veoCnjTGXjDHRwEfkDDxbjDEL7L68jHUqrYfD9lnGmKPGmGTgLuAbY8wP9v4vAlWA6wCMMV8YY04YYzKNMfOAfUA3u577gZnGmE3Gst8YczhXOyeMMfHA10BkIcesKjANFMpdnDXGpDu8TgKqFeN9DbgySshyGGtkkuVoPu877fA8OZ/X+bYtIo2xRgtz7KJFWF/0t+Ta1bHNw3Y/82wzxmQCxwraTq7js/c/in18IjJWRKLs01IJWKOhYHv3xlgjjoKccnhe3M9bVUAaKFR5kQT4O7yu56R6TwBNcpWFAMcdXjtzCeV7sf5ffS0ip4CDWIEi9+mnxrn6cyK/bfbkeqNc2x37m+P4RETs9x8XkSbAe8BkIMgYUwNrdCT27keBZiU7PFUZaaBQ5UUUcLeIeIrIQOAGJ9W7FGgpInfbE793AW2BJU6qP7exWHMBkQ4/twG35JoX+YeI+ItIO+A+YJ7Dts4iMtK+qukRIBX4qYD25tt132if3nrc3n8DUBUrqJwBsC8ACHd47/vAX0Sks33FV3M7uCiVgwYKVV78GWveIAFr0nihMyq176MYgvUFehZrYnqIMSbOGfU7EpEeQCjwhjHmlMPPYmA/Oech1thlK4AXjTHfO2xbhDX3cA5rhDLSYe4kB2PMHuAe4DUgDuszvNUYc9kYsxN4CdiIdeosAvjR4b1fANOx5lESsT7zWtfyGaiKSTRxkVKlR0RCgRjAO9ecS9b2aUBzY8w9pdw1pQqkIwqllFKF0kChlFKqUHrqSSmlVKF0RKGUUqpQbr2oWHBwsAkNDS3rbiillFvZsmVLnDGmdnH3d+tAERoayubNm8u6G0op5VZEJPdqBYXSU09KKaUKpYFCKaVUoTRQKKWUKpRbz1EoVdmkpaVx7NgxUlJSyroryg34+fnRqFEjvL1zr3JfMhoolHIjx44dIyAggNDQUKyFYpXKnzGGs2fPcuzYMcLCwq6pLpeeehKRGnZax912usWeIlJLRH6w00z+kJWlzF69cpaI7LfTL3Yqqv6rMTd6DuFvhuL5jAfhb4YyN3pO0W9SqpxISUkhKChIg4QqkogQFBTklNGnq+co/gMsM8a0BjoAu4ApwApjTAuslTOn2PsOwkoN2QIrLeVbzu7M3Og5TF05kdcGHSZlquG1QYeZunKiBgvlVjRIqOJy1u+KywKFiAQCfYAPAOxljxOw8gF/bO/2MTDcfj4M+MROyfgTUCMrt6+zTF83lQ+GJtEvDLw9oV8YfDA0ienrpjqzGaWUqlBcOaJoipUw5SMR+U1E3heRqkBdY8xJAPuxjr1/Q3KmeDxGznSVAIjIRBHZLCKbz5w5U6IO7Yo7Qu+QnGW9Q6xypVTRHn30UV599dXs17/73e+4//77s18//vjjvPzyyyxevJgZM2YAsHDhQnbu3Jm9T9++fYu8UTYsLIw9e/bkKHvkkUeYOXMmb7/9Np988okTjuaK0NBQ4uKsFCXXXXfdVdXx7LPP5nh9tfWUR64MFF5AJ+AtY0xH4BJXTjPlJ78xUp4VC40x7xpjuhhjutSuXew70AFoExzC+lwxYf0Rq1ypisjZc3LXXXcdGzZsACAzM5O4uDh27NiRvX3Dhg306tWLoUOHMmWK9d89d6AojlGjRvH5559nv87MzGTBggXcddddPPjgg4wdmzuzrPNkHV9J5Q4UV1tPeeTKQHEMOGaM+dl+vQArcJzOOqVkP8Y67O+YRzh3nuBrNvX66UxY7M+qGEjLgFUxMGGhD1Ovn+7MZpQqF1wxJ9erV6/sL8AdO3YQHh5OQEAA586dIzU1lV27dtGxY0dmz57N5MmT2bBhA4sXL+avf/0rkZGRHDhwAIAvvviCbt260bJlS9atW5enndGjR+cIFGvXriU0NJQmTZowbdo0XnzxRQBmzZpF27Ztad++PaNGjQLIsR0gPDycQ4cOATB8+HA6d+5Mu3btePfdd/M9xmrVqgHw1FNPERkZSWRkJA0bNuS+++4rsI4pU6aQnJxMZGQkY8aMyVGPMYa//vWvhIeHExERwbx5Vtbb1atX07dvX26//XZat27NmDFjKK+rebvs8lhjzCkROSoirex0jTcCO+2fccAM+3GR/ZbFwGQR+RzoDpzPOkXlLKMjrH/Ah7+dyq64I7Tx82G61GF02zud2YxSpeLpr3ew88SFArevPv84n99hzcnBlTm5UV88zsINTfN9T9sGgfzz1nYF1tmgQQO8vLw4cuQIGzZsoGfPnhw/fpyNGzdSvXp12rdvj4+PT/b+1113HUOHDmXIkCHcfvvt2eXp6en88ssvLF26lKeffprly5fnaKd9+/Z4eHiwdetWOnTowOeff87o0aPJbcaMGcTExODr60tCQkKB/c7y4YcfUqtWLZKTk+natSu33XYbQUFB+e77zDPP8Mwzz3D+/Hmuv/56Jk+eXGAdM2bM4PXXXycqKipPPV9++SVRUVFs3bqVuLg4unbtSp8+fQD47bff2LFjBw0aNKBXr178+OOP9O7du8jjKG2uvurpYWCOiGzDSjL/LFaAuElE9gE32a8BlgIHsfIIvwdMckWHRkeMYfukQ2Q8lcn2YQsZfek8bJvviqaUKlNnkmPznZM7kxyb/xuKKWtUkRUoevbsmf26uOflR44cCUDnzp2z/9rPLWtUkZ6ezqJFi7jjjjvy7NO+fXvGjBnDZ599hpdX0X/3zpo1iw4dOtCjRw+OHj3Kvn37Ct3fGMOYMWN49NFH6dy581XVsX79ekaPHo2npyd169blhhtuYNOmTQB069aNRo0a4eHhQWRkZIGfRVlz6Q13xpgooEs+m27MZ18DPOTK/uTR8ndQPxLWvgDt7wJPvf9QuY/C/vIH2PFmCOuPHM4eUYA1J9e2dgjz/tDzqtvNmqeIjo4mPDycxo0b89JLLxEYGMjvf//7YtXh6+sLgKenJ+npeVKHA1aguPnmm7nhhhto3749derUybPPN998w9q1a1m8eDH/+te/2LFjB15eXmRmZmbvk3UfwerVq1m+fDkbN27E39+fvn37FnmPwbRp02jUqFH2aaerqaOw00lZnwMU/lmUtcq91pMI9J0C52Jg27yy7o1STpXvnNxi/2uek+vVqxdLliyhVq1aeHp6UqtWLRISEti4cSM9e+YNQAEBASQmJpa4nWbNmhEUFMSUKVPyPe2UmZnJ0aNH6devHzNnziQhIYGLFy8SGhrKr7/+CsCvv/5KTEwMAOfPn6dmzZr4+/uze/dufvrpp0LbX7JkCT/88AOzZs3KLiusDm9vb9LS0vLU06dPH+bNm0dGRgZnzpxh7dq1dOvWrcSfR1mq3IECoOVAqN/BGlVklM9ortTVGB0xhun93+Xhb5vgN114+NsmTO//bvZc3dWKiIggLi6OHj165CirXr06wcHBefYfNWoUL7zwAh07dsyezC72MYweze7duxkxYkSebRkZGdxzzz1ERETQsWNHHn30UWrUqMFtt91GfHw8kZGRvPXWW7Rs2RKAgQMHkp6eTvv27fnHP/6Ro//5eemllzhx4gTdunUjMjKSp556qtA6Jk6cmH0qzNGIESNo3749HTp0oH///sycOZN69eqV6HMoa26dM7tLly7GKYmLdi+Fz0fD8Lcg8u5rr08pF9m1axdt2rQp624oN5Lf74yIbDHG5DctkC8dUQC0GgT12sOamTqqUEqpXDRQgD1X8aQ1VxH9RVn3RimlyhUNFFmyRhVrdVShlFKONFBkyboCKv6gjiqUUsqBBgpHrQZDvQi9AkoppRy4OnHRIRGJFpEoEdlsl00TkeN2WZSIDHbY/0k7cdEeEfmdK/tWQIfhhikQfwC2Lyj15pVSqjwqjRFFP2NMZK5LsV6xyyKNMUsBRKQtMApoBwwE3hQRz1LoX06tb7FGFXoFlFJ5lNYy48WVe8XWLOPHj+edd97JUbZw4UIGDx7M5s2b+dOf/uSU9h3bW7DA+uPy/vvvL/FquQCzZ8/mxIkr66BebT2uUJ5OPQ0DPjfGpBpjYrDWfCr92xd1VKEqEHddZry4CgoUuVefBbIXFuzSpUuOu62d7f3336dt27Ylfl/uQHG19biCqwOFAb4XkS0iMtGhfLKdF/vDrJzZlELiomJrfQvU1bkK5d7cbZnxlJQU7rvvvuw7rVetWgWQXVeWIUOGsHr16nyX9s4yYMAAdu/ezcmT1gLUSUlJLF++nOHDh7N69WqGDBkCwJo1a7KXEu/YsSOJiYk5tgNMnjyZ2bNnA9aKsl27diU8PJyJEyfmu45T1ohp8eLF2XW3atWKsLCwAutYsGABmzdvZsyYMURGRpKcnJxj5DV37lwiIiIIDw/niSeeyG6rWrVqTJ06NXuRwtOnT1/lv2zhXB0oehljOmHlw35IRPpg5cJuhrWa7EngJXtflycuKjYR6PsEnN0P2//nmjaUulbfToGPbinwZ/q39+ef+vfb+wt+37eF5RbLf5nx7t27s3HjRjZv3lzgMuMvvPACUVFRNGvWDLiyzPirr77K008/DcAbb7wBQHR0NHPnzmXcuHGFLrg3Y8YMqlSpQlRUFHPm5Ax+np6ejBw5kvnzrZWhFy9eTL9+/QgICMix34svvsgbb7xBVFQU69ato0qVKoUe/+TJk9m0aRPbt28nOTmZJUuWFLjv0KFDiYqKIioqig4dOvCXv/ylwDpuv/12unTpwpw5c4iKisrRjxMnTvDEE0+wcuVKoqKi2LRpEwsXLgTg0qVL9OjRg61bt9KnTx/ee++9Qvt/tVwaKIwxJ+zHWOAroJsx5rQxJsMYk4m1nHjW6SWXJy4qkVZZo4qZkJlRZt1Q6mrtSk7JP/VvcuGrnRbFVcuMr1+/nnvvvReA1q1b06RJE/bu3XvV/XQ8/VRQPotevXrx2GOPMWvWLBISEopcqnzVqlV0796diIgIVq5cmeO0W0FmzpxJlSpVeOihh66qjk2bNtG3b19q166Nl5cXY8aMYe3atQD4+Phkj34KW7L9WrlsXW07P7aHMSbRfn4z8IyI1HdISDQC2G4/Xwz8V0ReBhoALYBfXNW/Inl4wA1/g/n3WqOK9prcSJUzg2YUurnNm6H5LjPepnYTuO+bq27WVcuMF7TuXEHLhhelV69enDx5kq1bt7Jhw4Y8cxZgZaa75ZZbWLp0KT169GD58uUFtpeSksKkSZPYvHkzjRs3Ztq0aUX2ZcWKFXzxxRfZX+xXU0dh6/F5e3sjYp2MceUy5a4cUdQF1ovIVqwv/G+MMcuAmfYls9uAfsCjAMaYHcB8rAx4y4CHjDFl+6d86yFQNxzWPK+jCuV23G2Z8T59+mSfQtq7dy9HjhyhVatWhIaGEhUVlb2s+C+/XPn7saClvQFEhDvvvJNx48YxePBg/Pz88uxz4MABIiIieOKJJ+jSpQu7d++mSZMm7Ny5k9TUVM6fP8+KFSuAKwEjODiYixcvZl/lVJDDhw8zadIk5s+fn30qqbA6Cvqcunfvzpo1a4iLiyMjI4O5c+dyww03FNq2s7kyFepBoEM+5fcW8p7pQPlJYO3hATc8oaMK5ZbypP4NDmF6/+lOW2b87rvvzlF28eLFApcZf+CBB5g1a1ahX66TJk3iwQcfJCIiAi8vL2bPno2vry+9evUiLCwsezK3U6dO2e/JWtq7U6dOeeYpwDr99MILL2Rfqpvbq6++yqpVq/D09KRt27YMGjQIX19f7rzzTtq3b0+LFi3o2LEjADVq1OCBBx4gIiKC0NBQunbtWujnNHv2bM6ePZu9RHqDBg1YunRpgXWMHz+eBx98kCpVqrBx48bs8vr16/Pcc8/Rr18/jDEMHjyYYcOGFdq2s+ky40XJzIS3e0PGZXjoZ/Ao/Vs7lMqiy4yrktJlxkuDh4d9BdQ+2P5lWfdGKaVKnQaK4mh9K9Rpp3MVSqlKSQNFcWRdAXV2H+z4qqx7oyo5dz5drEqXs35XNFAUV5uhUKetjipUmfLz8+Ps2bMaLFSRjDGcPXs236u9SsplVz1VOFlXQH0xzhpVRNxe1j1SlVCjRo04duwYLlu+RlUofn5+NGrU6Jrr0UBREtmjipnQboReAaVKnbe3d/aaQUqVFj31VBJZcxVxe3SuQilVaZRF4qJaIvKDiOyzH2va5SIis+zERdtEpFPhtZeRNsOgdhtrVKFzFUqpSqAsEhdNAVYYY1oAK+zXYK0w28L+mYi1ymz54ziq2LmwrHujlFIuVxannoYBH9vPPwaGO5R/Yiw/ATVEpH4Z9K9obYdD7dY6qlBKVQplkbiobtbqsfZjHbu8/CQuKkrWFVBnduuoQilV4ZVF4qKClJ/ERcWRY1SRWeTuSinlrko9cRFwOuuUkv0Ya+9evhIXFSVrrkJHFUqpCs5lgUJEqopIQNZzrMRF27ESFI2zdxsHLLKfLwbG2lc/9QDOOyQ4Kp/aDofgVjqqUEpVaGWRuGgGcJOI7ANusl8DLAUOAvuxUqROcmHfnMPD0x5V7IJdi4reXyml3JDmo7hWmRnwZk8QD/jjBuuUlFJKlWOaj6K06ahCKVXBaaBwhnYjILilzlUopSokDRTO4OFp3VcRuxN2LS7r3iillFNpoHCW7FHF8zqqUEpVKBoonMXDE/r8zRpV7P66rHujlFJOo4HCmcJHQlALWK2jCqVUxaGBwpmyroCK3aGjCqVUhaGBwtnCb4Og5noFlFKqwnB5oBARTxH5TUSW2K9ni0iMncwoSkQi7XL3SFxUlKwroE5vh91Lyro3Sil1zUpjRPFnYFeusr/ayYwijTFRdpl7JC4qjuxRhc5VKKXcn6tToTYCbgHeL8bu7pO4qChZV0Cd3g57vinr3iil1DVx9YjiVeBvQO4/q6fbp5deERFfu6xUEhfNjZ5D+JuheD7jQfibocyNnlPiOoola1ShV0AppdycK5cZHwLEGmO25Nr0JNAa6ArUAp7Ieks+1Tg1cdHc6DlMXTmR1wYdJmWq4bVBh5m6cqJrgoWnF/T5K5yO1lGFUsqtuXJE0QsYKiKHgM+B/iLymTHmpH16KRX4CCuZEZRC4qLp66bywdAk+oWBtyf0C4MPhiYxfd1UZzZzRfjtUKuZNVfhxqv0KqUqN5cFCmPMk8aYRsaYUGAUsNIYc49DdjsBhmMlM4JSSFy0K+4IvUNylvUOscpdwtPLuq/iVDTs1lGFUso9lcV9FHNEJBqIBoKBf9vlLk9c1CY4hPW5YsL6I1a5y4TfDrWawpoZOqpQSrmlUgkUxpjVxpgh9vP+xpgIY0y4MeYeY8xFu9wYYx4yxjSztzs9I9HU66czYbE/q2IgLQNWxcDYr/yYev10Zzd1haeXdQXUqWjYs9R17SillIt4lXUHStPoiDEAPPztVHbFHaG+fz3Szo8hotYQ1zYccQesnQmrn4NWg0Hym7dXSqnyqdIt4TE6YgzbJx0i46lMdkw6TGPf3zH1q2gyMl14WijrCigdVSil3FClCxSOqvt7848hbdh67Dxzfj7s2sYi7oSaYbBa5yqUUu6lUgcKgKEdGnB9i2BmLtvD6Qsprmso+wqobbDnW9e1o5RSTlbpA4WI8O/h4aRlZPL01ztc21j2qOI5HVUopdxGpQ8UAE2CqvKnG1uwNPoUK3efdl1D2XMV22DvMte1o5RSTqSBwvbA9U1pUaca/1i4g6TL6a5rqP1dOqpQSrkVDRQ2Hy8Pnh0ZwfGEZP6zYp/rGvL0gj5/gZNbdVShlHILZZG4KExEfhaRfSIyT0R87HJf+/V+e3uoq/uWW9fQWozq2pj318Ww6+QF1zXU/i6oGapXQCml3EJZJC56HnjFGNMCOAdMsMsnAOeMMc2BV+z9St2UQa2pUcWbv38VTaar7q3w9LbmKk5Gwd7vXNOGUko5SakmLrIXAuwPLLB3+RhrYUCwEhd9bD9fANxo71+qavj78H9D2vDbkQT++4uLFgsEa1RRo4nOVSilyr3STlwUBCQYY7Jmix2TE2UnLrK3n7f3z+FaExcVx/DIhvRqHsTzy3YTm+iieyscRxX7vndNG0op5QSlnbiosORELk9cVFzWvRURpKZn8q8ludN9O1GHUTqqUEqVe6WauAhrhFFDRLIWI3RMTpSduMjeXh2Id2H/ChUWXJXJ/Zrz9dYTrN4T65pGPL2tK6BO/KajCqVUuVXaiYvGAKuA2+3dxgGL7OeL7dfY21caU7Z/Zv/hhqY0rV2VfyzaTvLlDNc00mE01AjRK6CUUuVWWdxH8QTwmIjsx5qD+MAu/wAIsssfA6aUQd9y8PXy5NkRERyNT+a1lS66tyJrruLEr7DvB9e0oZRS10DK+I/2a9KlSxezebPT8xvl8ZcvtrLwt+N886fraVUvwPkNZKTBa53APxgeWKn5KpRSLiUiW4wxXYq7v96ZXQx/H9yGAD8vprrq3gpPb7j+LzqqUEqVSxooiqFWVR+m3tKWzYfPMW/zUdc00mE0VA/R3NpKqXJHA0Ux3dapIT2a1uK5pbs4k5jq/Aa8fKDP43B8C+xf7vz6lVLqKmmgKKaseytS0jKZ/s1O1zTS4W5rVKH3VSilyhENFCXQvE41HuzbjIVRJ1i3zwV3hXv5wPWP2aOKFc6vXymlroIGihKa1LcZYcFV+cfC7aSkueDeisgxUL2xjiqUUuWGBooS8vP2ZPrwcA6dTeKNVfud34CXD1z/OBzfrKMKpVS5oIHiKlzXPJiRHRvy9poD7I9NdH4DWaMKvQJKKVUOuHJRQD8R+UVEtorIDhF52i6fLSIxIhJl/0Ta5SIis+zERdtEpJOr+uYMU29pQ1VfL/7+5Xbn31uRNVdxbBMc0FGFUqpsuXJEkQr0N8Z0ACKBgSLSw972V2NMpP0TZZcNAlrYPxOBt1zYt2sWVM2XJwe15pdD8SzYcsz5DUTeA4GNdA0opVSZc+WigMYYc9F+6W3/FPaNNwz4xH7fT1irzNZ3Vf+c4Y7OjekWWotnv93F2YtOvrcix6hipXPrVkqpEnB1hjtPEYkCYoEfjDE/25um26eXXhERX7ssO3GRzTGpkWOdLk9cVFweHsL0EeFcSk1n+lIX5K3oqKMKpVTZc2mgMMZkGGMisfJOdBORcOBJoDXQFaiFtZoslKPERSXRom4Af+jTjC9/Pc6GA3HOrdzL1x5V/KKjCqVUmSmVq56MMQnAamCgMeakfXopFfgI6Gbvlp24yOaY1Khcm9y/OU2C/Pm/r1xwb0XHeyCwIax5XkcVSqky4cqrnmqLSA37eRVgALA7a95BRAQYDmy337IYGGtf/dQDOG+MOemq/jmTn7cn/x4ezsG4S7y1+oBzK88aVRz9GQ6ucm7dSilVDK4cUdQHVonINmAT1hzFEmCOiEQD0UAw8G97/6XAQWA/8B4wyYV9c7rrW9RmWGQD3lp9gANnLhb9hpLoeK81qtC5CqVUGdDERU50JjGVG19aTdsGgcx9oAfizAREv7wHS/8C9y6EZv2cV69SqtLRxEVlqHaAL1MGteGng/F8+etx51beaSwENNBRhVKq1GmgcLJRXRvTuUlN/v3NTuIvXXZexdlzFT/BwdXOq1cppYpQaKAQEX8R8XZ43UpEHhWRka7vmnvKurciMSWd55x9b0XWqEKvgFJKlaKiRhTLgFAAEWkObASaAg+JyHOu7Zr7al0vkAf6NOWLLcf46eBZ51WcNao4shFi1jivXqWUKkRRgaKmMWaf/XwcMNcY8zDWukxDXNozN/en/i1oXKsKU7+KJjXdifdWdLxX5yqUUqWqqEDh+E3UH/gBwBhzGch0Vacqgio+njwzLJwDZy7x7pqDzqvY2w96P2qPKtY6r16llCpAUYFim4i8KCKPAc2B7wGybqRThevXqg63tK/Pa6v2ExN3yXkVdxoLAfV1VKGUKhVFBYoHgDggBLjZGJNkl7cFXnRlxyqKfw5pi6+nB/+3MBqn3bPi7Qe9H4MjG3RUoZRyuUIDhTEmGfgOWA9cdijfYIz5tLD3FpK4KExEfhaRfSIyT0R87HJf+/V+e3votR5ceVAn0I+/DWzFj/vPsijKiUtXdRrLXD9/whcMxPMZD8LfDGVu9Bzn1a+UUraiLo99CpgH3AZ8IyIPlKDughIXPQ+8YoxpAZwDJtj7TwDOGWOaA6/Y+1UId3dvQmTjGvxryU4Skpxzb8Xc3f9jqm88r92RQspUw2uDDjN15UQNFkoppyvq1NNdQKQxZjTWsuATi1txIYmL+gML7PKPsRYGBCtx0cf28wXAjeLUNTDKjqeH8OyICBKS03h+2W6n1Dl93VQ+GH6ZfmHg7Qn9wuCDoUlMXzfVKfUrpVSWogJFSta8hDHmbDH2zyF34iLgAJBgjEm3d3FMTpSduMjefh4IyqfOcpO4qCTaNghkQu8w5v5ylE2H4q+5vl1xR+gdkrOsd4hVrpRSzlTUF38zEVls/3yd6/XioirPnbgIaJPfbvajWyYuKolHBrSgYY0q/P3LaC6nX9vVxW2CQ1ifKyasPwJtghvn/wallLpKRQWKYcBL9s+LuV6/VNxGHBIX9cDKhe1lb3JMTpSduMjeXh249j+9yxF/Hy+eGdaOfbEXeW/dtd1bMfX66UxY7M+qGEjLgFUxMOF/MNWvGWTqLS5KKefxKmJ7jDHmqs5liEhtIM0Yk+CQuOh5YBVwO/A51t3ei+y3LLZfb7S3rzTuvAZ6AW5sU5dB4fWYtWIfQ9rXp0lQ1auqZ3TEGAAe/nYqu+KO0CY4hOkhPRi981tY8gjc+h+oGFM8SqkyVmg+ChH51RjTyX7+P2PMbcWuWKQ91uS0J9bIZb4x5hkRaYoVJGoBvwH3GGNSRcQP+BToiDWSGGWMKfTP7vKWj6K4Tp1PYcDLa+jUpCYf39fVeXkrjIEVz8D6l6HbH2DQ8xoslFJ5lDQfRVEjCsdvmaYl6YgxZhvWl37u8oNcyZPtWJ4C3FGSNtxVvep+/OXmlkz7eidfbzvJ0A4NnFOxCNz4FKSnwE9vWjfmDXhag4VS6pqUZK2nCncaqCzd2zOU9o2q88zXOzmfnOa8ikXgd89Cl9/Dj/+xlvlQSqlrUFSg6CAiF0QkEWhvP78gIokicqE0OlhRZd1bEX8plZlOurcimwgMfgkix8CaGbD+FefWr5SqVAo99WSM8SytjlRG4Q2rc1+vMD5YH8PITo3o3KSm8yr38IChr0F6KiyfBl5+0OOPzqtfKVVpaCrUMvbYTS2pX92PqV9Fk5bh5MtaPTxhxNvQeggsmwKbP3Ju/UqpSkEDRRmr6uvF00PbsftUIh+sj3F+A57ecPtH0OJmWPIoRM11fhtKqQpNA0U5cHO7etzUti6vLt/L0fikot9QUl4+cOenENYHFk2C7f9zfhtKqQpLA0U58fTQdniI8NSi7c7LW+HI2w9Gz4XGPeB/D8CuJc5vQylVIWmgKCca1KjC4ze3YtWeMyyNPuWaRnyqwt3zoEFH+GI87PvBNe0opSoUlwUKEWksIqtEZJeduOjPdvk0ETkuIlH2z2CH9zxpJy7aIyK/c1XfyqtxPZvQrkEgT3+9gwspTry3wpFfINyzAOq0hnn3wME1rmlHKVVhuHJEkQ48boxpg7UY4EMi0tbe9ooxJtL+WQpgbxsFtAMGAm+KSKW6PNfL04PnRkYQdzGVF7/b47qGqtSEexdBzTCYOwoOb3RdW0opt+eyQGGMOWmM+dV+ngjs4kruifwMAz43xqQaY2KA/eSz1EdF175RDcb2DOXTnw4TdTTBdQ1VDYKxiyCwAcy5A45tcV1bSim3VipzFHb+647Az3bRZBHZJiIfikjWXWbZiYtsjkmNHOtyy8RFJfH4zS2pE+DLk19Gk+7seyscBdSFsYvBvxZ8NgJObnNdW0opt+XyQCEi1YD/AY8YYy4AbwHNsPJon+RKXosKn7iouAL8vJl2azt2nbzARz8ecm1j1RvCuK/BJwA+HQ6xu1zbnlLK7bg0UIiIN1aQmGOM+RLAGHPaznyXCbzHldNL2YmLbI5JjSqdgeH1uLF1HV7+YS/Hzrng3gpHNZvAuMXg4Q2fDIOzB1zbnlLKrbjyqicBPgB2GWNediiv77DbCGC7/XwxMEpEfEUkDGgB/OKq/pV3IsLTw9oBMG3xDtfcW+EoqJk1Z5GZDh/fCucOubY9pZTbcOWIohdwL9A/16WwM0UkWkS2Af2ARwGMMTuA+cBOYBnwkDEmw4X9K/ca1fTn0ZtasHxXLN/tOO36Buu0toLF5Uvw8VA4f9z1bSqlyr1CM9yVd+6a4a4k0jIyGfr6j5y7dJkfHutDgJ+36xs9vgU+GQ5Va8N9SyGgnuvbVEqVmpJmuNM7s8s5b08Pnh0RzunEFF76fm/pNNqwM4z5AhJPWXMWl+JKp12lVLmkgcINdAypyT3dm/DJxkNsO5ZQOo2G9IC7P7fmKj4dDsnnSqddpVS5o4HCTfx1YCuCqvny969cfG+Fo7A+cNccOLMHPrsNUjSpoVKVkQYKNxHo580/b23L9uMX+GTj4dJruMUAuGM2nNwK/73TmuhWSlUqGijcyC0R9enbqjYvfb+HEwnJpddw61tg5Htw9Gdrbai0UmxbKVXmNFC4ERHhX8PCyTCGaYt3lG7j4SNh+FsQsw7m3Wvl4lZKVQoaKNxM41r+/PnGlny/8zTf73BR3oqCdBgFt74K+3+ABb+HDBctha6UKlc0ULih+68Po1XdAKYt3sGl1PTSbbzzeBg0E3Yvga/+AJmV+p5IpSqFskhcVEtEfhCRffZjTbtcRGSWnbhom4h0clXf3J23pwfPjgxn38Vvaf16CJ7PeBD+Zihzo+eUTge6/wEGPG3l3l40GTJL6SospVSZ8HJh3VmJi34VkQBgi4j8AIwHVhhjZojIFGAK8AQwCGt9pxZAd6xVZru7sH9ube+Fb/ELeoNPRqTQOwTWHznMhMUTARgdMcb1Hej9CKSnwOrnwMsXhrwCkt8CwEopd1cWiYuGAR/bu30MDLefDwM+MZafgBq5FhBUDqavm8onI1LoFwbentAvDD4YmsT0dVNLrxM3PAG9HoEtH8GyJ8GNl4NRShXMlSOKbLkSF9U1xpwEK5iISB17t4ISF53MVddEYCJASEiIazteju2KO0LvXIffO8QqPxqfRONa/q7vhAgMmGaNLH5+C7z94MZ/6shCqQqmLBIXFbhrPmWVMnFRcbQJDmH9kZxl649AoFcwfV5Yxf0fb2bdvjNkZrr4r3wRGDjDmuRe/wqsmena9pRSpa7UExcBp7NOKdmPsXa5Ji4qganXT2fCYn9WxUBaBqyKgQmL/Zl+4/NM7tecqKPnuPeDXxjwyho++jGGCykuvJRVBG55BTqMhtXPwo//cV1bSqlS57JTTwUlLsJKUDQOmGE/LnIonywin2NNYp/POkWl8sqasH7426nsijtCm+AQpvefnl0+uX9zvo0+xccbD/H01zt54bs9jOzUkLE9Q2lZN8D5HfLwgKGvW6ehfngKvPysq6OUUm7PZfkoRKQ3sA6IBrKun/w71jzFfCAEOALcYYyJtwPL68BAIAm4zxhTaLKJypCPwhm2HUvgk42HWbz1BJfTM+nZNIixPZtwU9u6eHk6eVCZkQbzx8Geb+DW/1inpJRS5UpJ81Fo4qJKJP7SZeZvPsqnGw9zPCGZ+tX9GNM9hFHdQgiu5uu8htJT4fO7Yf8KGPEOdLjLeXUrpa6ZBgpVpIxMw8rdsXyy8RDr9sXh4+nB4Ih6jL0ulI6NayDOuGopLdlabfbQerj9Q2g34trrVEo5hQYKVSIHzlzk042HWbDlGBdT04loWJ2xPZtwa4cG+Hl7Xlvlly/BpyPh+Ga481NoPdg5nVZKXRMNFOqqXExN56vfjvPJhkPsi71ITX9v7uzamHu6N7m2ezJSLljpVE9vh9FzofkA53VaKXVVNFCoa2KM4aeD8Xyy8RDf7zxNpjHc2Lou465rQq9mwXh4XMVpqaR4+HgonN1n5eIO6+P8jiulik0DhXKaEwnJ/PfnI8z95QhnL12mae2q3NujCbd1bkSgn3fJKrsUB7NvgYSjcO+XVk5upVSZ0EChnC41PSP7nozfjiTg7+N5dfdkJJ6CjwZZQWPsImioCwQrVRY0UCiXyu+ejHHXNWFAm2Lek3H+mBUsUi7A+CVQL8L1nVZK5aCBQpWK+EuXmbfpKJ/9dBX3ZJw7BB8OgozLMP4bqNO6VPqslLKUm0AhIh8CQ4BYY0y4XTYNeAA4Y+/2d2PMUnvbk8AEIAP4kzHmu6La0EBR9vK7J+OW9vUZ27MJkYXdkxG33xpZiAfctxSCmpVux5WqxMpToOgDXMTKMeEYKC4aY17MtW9bYC7QDWgALAdaGmMKzbOpgaJ82R97kc9+KsE9GbG7rAlurypWsKjZpPQ7rVQlVNJA4crERWuB+GLuPgz43BiTaoyJAfZjBQ3lRprXqca0oe346e838q/h4aSkZfDXBdvo+dwKZny7m6PxSTnfUKcN3LsQLicy9/3rCX+jUemndVVKFcnl+SjyMdnOif1hVr5sCk5alIeITBSRzSKy+cyZM/ntospYNV8v7u3RhO8f7cN/H+hO97Ag3lt3kBsc8mRkj2Trt2duz4lMlWO8Nvg4KVMNrw06zNSVEzVYKFVOuHQy285st8Th1FNdIA4rIdG/gPrGmN+LyBvARmPMZ/Z+HwBLjTH/K6x+PfXkPvK7J2OsfU9Gzw9b8Nqgw/QLu7L/qhh4+NsmbJ90qMz6rFRFVW5OPeXHGHPaGJNhjMkE3uPK6SVNWlTBNahRhb/8rhUbnuzPK3d1INDPm2lf76THsyvYeeZw/mldzxyGPcsgMzP/SpVSpaJUA0VWZjvbCGC7/XwxMEpEfEUkDGgB/FKafVOlw9fLkxEdG7HwoV4seqgXA8PrE+DjkW9a15Y+AnPvgte7wM/vQGpi2XRaqUrOZYFCROYCG4FWInJMRCYAM0UkWkS2Af2ARwGMMTuwkhntBJYBDxV1xZNyfx0a1+ClOzuQeDmTCYvJldYV9l82cNsH4F8Lvv0bvNwWlj0J8TFl3XWlKhW94U6VufA3Qxne+jALd8OuOGgTDMNbw/ub6/L1nTvpGloLjm2Gn96CnQshMwNaDYYeD0Lo9VbObqVUsZWb+yhKgwaKimFu9BymrpzIB0OT6B1inXYat7AKXol/JjOpN31a1ubxm1rSoXENuHACNn0AWz6CpLNQN9zKzR1xB3hXKetDUcotaKBQbmlu9Bymr5vKrrgjtAkOYer10xneahSf/nSIt1Yf4FxSGgPa1OWxm1rStkGglUEv+gv46W2I3QH+QdD5Puh6PwTWL7pBpSoxDRSqwrmYms5H62N4d91BElPSuaV9fR4d0ILmdQLAGDi0zgoYe5aChye0HQ49JkGjzmXddaXKJQ0UqsI6n5TG++sP8uH6GJLTMhjesSF/vrEFTYKqWjvEH4Rf3oPfPoPUC9CoK3R/ENoOA88S5s9QqgLTQKEqvLMXU3ln7UE+3nCIjEzDHV0aMbl/CxrWsOcoUhMh6r/w89tW8AhoAF0nWKemqgaVbeeVKgc0UKhKI/ZCCm+uPsB/f7Zuwri7ewiT+jajTqCftUNmJuz/AX56Ew6uBi8/aH8ndP8j1G1bdh1XqoxpoFCVzvGEZF5fuY/5m4/h7SmM7RnKH/o0JcgxL0bsLmuEsfVzSE+x8nb3mAQtfgceZbHkmVJlRwOFqrQOn73Ef1bsY+Fvx6ni7cnve4dx//VNqV7FYX4iKR62zIZN78OF41AzzLq8NnIM+AWWWd+VKk3lJlAUkLioFjAPCAUOAXcaY86Jld3mP8BgIAkYb4z5tag2NFCo/OyPTeSV5fv4ZttJAvy8mHh9U+7rHUY1X68rO2Wkwa6vrVHG0Z/BJwA6joFuEzWJkqrwylOgyC9x0Uwg3hgzQ0SmADWNMU+IyGDgYaxA0R34jzGme1FtaKBQhdl54gKvLN/LDztPU9Pfmz/2bca9PUKp4pMridLxLdbltTu+gsx0aDnQuus77Aa961tVSOUmUNidCSXnMuN7gL7GmJP2AoGrjTGtROQd+/nc3PsVVr8GClUcUUcTePmHvazde4baAb481LcZo7uH4OuVK2AknrLu+t78ISTFQZ221mmp9nfpXd+qQinvgSLBGFPDYfs5Y0xNEVkCzDDGrLfLVwBPGGPyRAERmQhMBAgJCel8+PBhl/VfVSybDsXz4nd7+DkmngbV/Xj4xhbc3rkR3p65JrPTUmD7AmuUcToaqtS8ctd39XzzaSnlVsp1PopC5De+zzeCGWPeNcZ0McZ0qV27tou7pSqSrqG1+HxiD+bc35261f148stobnxpDf/bcoyMTIdfN28/6HgPPLgOxn8DTXrBj6/CqxHwxX1wdFOZHYNSZaG0A8XprJwU9mOsXa6Ji1SpEBF6NQ/myz9ex4fjuxDg58XjX2zl5lfW8PXWE2Q6BgwRCO0No+bAn36DHn+E/SvggwHwXn/Y9gWkXy67g1GqlJR2oFgMjLOfjwMWOZSPFUsP4HxR8xNKXQsRoX/ruix5uDdv39MJTw/h4bm/MXjWOr7fcYo8p2RrhsLvpsNjO2Hwi5ByHr683xplrHkBLsWVyXEoVRpcedXTXKAvEAycBv4JLMRKUBQCHAHuMMbE25fHvg4MxLo89r785idy08ls5SwZmYYl207w6vJ9xMRdon2j6jx+cyv6tAhG8rvyKTMT9i+Hn9+CAyvB0xfa3wHd/8jcM1vzrIQ7OmJM6R+UUgUoV5PZrqaBQjlbekYmX/52nP8s38fxhGS6NKnJ4ze3omezQtaIit0Nv7wDWz9nbnoCU6ul8cHIjOzcGhMW+zO9/7saLFS5oYFCKSe4nJ7JvM1HeX3lPk5fSKVX8yAeu6kVnZvULPhNSfGEv9Oc14afo1/YleJVMfDwoiC2370OgltaS6ErVYY0UCjlRClpGcz5+Qhvrd5P3MXL9GtVm8dvbkV4w+r57u/5jAcpUw3eDrEgLQP8/g0ZJtC6A7xBJDTqAg27WI8B9UrnYJSylTRQeBW9i1KVl5+3JxN6hzGqa2M+3niId9YcZMhr6xnYrh6P3tSSVvUCcuzfJjiE9UcO5xhRrD8CbYIaQO8XrNzfxzfDhtesu8ABAhtCw85XgkeDSPCpWnoHqVQRdEShVAlcSEnjw/UxfLAuhouX07m1fQMeGdCCprWrAfnn/853jiItGU5us4LGsc3WMiIJ9s2j4mHdFZ4dPDpD7dZ6yko5jZ56UqoUnLt0mXfXHWT2j4dITc/gtk6N+NONLWhcy5+Hl05iTvS7JKRkUMPPkzERE3lt8JtFV3rxjBUwjtuB4/gW6zJcAJ9q0KCjFTSyAkhgA9cepKqwNFAoVYrOJKby9poDfPrTYYwxhDffRtSFf/FhUSOK4sjMhPgDV0YcxzfDqe2QmWZtD2hg5QVv2Nk+ZdURfKs5/yBVhVOpAkVY2zDzz//+M0dZu9rt6NqwK2kZacyJnpPnPZH1IomsF0lSWhLzd8zPs71Lgy6E1wnnfMp5vtr9VZ7tPRv1pFVwK+KS4liyd0me7X2a9KFpzaacuniKZfuX5dl+Y9iNNK7emKPnj7IiZkWe7QObD6RetXocPHeQtYfX5tk+pOUQgv2D2RO3h43HNubZPqL1CKr7VWd77HY2n8gbRO9sdyf+3v5EnYoi6lRUnu1jIsbg7enNpuOb2HFmR57t4yPHA7Dh6Ab2nt2bY5uXhxf3tL8HgDWH1hCTEJNjexWvKtwVfhcAyw8u59iFYzm2B/oGMrLNSACW7V/GqYuncmwPqhLEra1uBeDrPV9zNvlsju31qtVjYPOBAHy560supF7Isb1RYCMGNB0AwLzt80hOT86xPaxGGDeE3gDAZ9s+Iz1rDsHWMqgl1zW+DoDZUbNzbDuflMa2QwF8eehPzL/rDMcTr2zbdQY+i67FN3evuPbfvepNiDu0liXbPoP4Q3AuxlrAEOgj3jStHc6pOq1Y5uVp3SQY2MA6lYX+7lXU3z0o+ffefR3v08lspUpbdX9vHryhGbP3x9GrMczfeWVbyyA4kRjPXe9spEngeYKqwTlzhIAq3lT388p+bFQ1mRa1MgpvyNsPGnaCSycgK21GaiKcOwyZwNmDsO8HSLW/yDx9oUaIFTTS06HFIBccvaroymREISKHgEQgA0g3xnQpKKlRYfXoqSdV3oS/Gcprgw7nuY9i/FcNGNfsO05fSOH0hVTOJKYSm5hCWkbe/381/L2pG+BHnUBf6gT4UTfQlzoBvtQN9KNOoB91AnypE+ibd5n0LMZA/MErV1gd2wynoh1OWdXPOdfRoCP4BuSpZm70HL3DvIJyp8tj+xljHBfImQKscEhqNAV4omy6ptTVmXr9dCYsznvV04ybZjI6IjzHvpmZhnNJl4lNTOX0hRRiL9iP9uvTiakciI0jNjGV9My8AaWmvzd17IBS1w4gdQOtwFI7oBZ1Q26ldrvbrYCSnmoFi6zgcXwL7M46dSrWVVWNOmff2zH3dBRTVt7P7OGX7eM4zPiFvwfQYFEJleWIootjoCgoqVFh9eiIQpVHzv5LPDPTEJ902QokiSnEZgWVRGt0EpuYapUlpuZcLt1W09+buoF+1HYIJHUC/Gjkm0STlN3UubCdanFb8TixBZKtQXy470VeG5WZZ2T0+8VBxPxZF0B0d24xmS0iMcA5rJwT7xhj3i0oqVE+79XERUrlIyugZI1OYu1AkjVKic067XUx/4BSy9+bDlXj6eYTw+T4v5Pyf+R/h3m9PtacR81QqBV25XlgI/DUaU934C6nnnoZY06ISB3gBxHZXdw3GmPeBd4Fa0Thqg4q5W48PITgar4EV/OlXSG3WGRkGuIvXSY28crprtPZgaUW3yY2wt/HOm2W+w7zRj6wKVZofGYTwRlL8DJXrs4x4gk1GiM1HYKHYzDxy3/ZE1X+lUmgMMacsB9jReQroBt2UiOHU0+xhVailLoqnh5C7QBfagcUHFAavxzE+EVnmT2M7LmW8YvgolTn/dAXOZ6QzMn4i/ilnCbEI5bGEkuIxBIaF0vThKM0itlCoMl5iWiGbw2kVhgetfIJJIEN9c7zcqzUA4WIVAU8jDGJ9vObgWe4ktRoBjmTGimlStnMm/7DI8vu44Gv04hJgLAakJLmzeu3vMHoiCtnLJIup3MiIZlj55I5npDMznPJLE+wnifEx+Fz8SiNiSVEThOSHktIUixhJzdQn0V4ceVS4EzxJi2gIR61wvAKboo4BpKaoeAXWLofgMqhLEYUdYGv7GQwXsB/jTHLRGQTMF9EJmAnNSqDvimluHJl0/R1U4Ej+HmF8K9+eSfl/X28aF4ngOZ18l5eC5CWkcmp8ymcsIPH1nPJLE1I5uS5i1yOP4pP4hHqZZ6miZwm5FwsjRMO0yRmEzXkYo56Un1qkh4YgkdQGH61myO1Qh3mRhoUORrRS32vjVvfma1XPSnl3owxnL10meP2iCTrMf7sGUx8DD6JRwhOO0mI2KMSiaWhxOElmdl1pIs3Sf4NyagegmdQGP51m+EV1MwOJE2Yu3cxU5b/3uFSXxi/0IcZAz6stMHCXSaznePsWZg9O2dZu3bQtSukpcGcvLeyExlp/SQlwfy8yyjQpQuEh8P58/BV3mUU6NkTWrWCuDhYkncJD/r0gaZN4dQpWJZ3CQ9uvBEaN4ajR2FF3mUUGDgQ6tWDgwdhbd5lFBgyBIKDYc8e2Jh3GQVGjIDq1WH7dsgviN55J/j7Q1SU9ZPbmDHg7Q2bNsGOvMsoMH689bhhA+zNuYwCXl5wj7WMAmvWQEzOZRSoUgXuspZRYPlyOJZzGQUCA2GktYwCy5ZZn6GjoCC41VpGga+/tv79HdWrZ31+AF9+CRdyniOnUSMYYC2jwLx5kJxzGQXCwuAGaxkFPvvMupPZUcuWcJ21jEKe3zvQ372r+N0TrFzJwUAHx9+9+EMQJBDUhJS0RiQkpbHrdyNZm5CMrF9Htd1bICkOr5Sz+Kedo6bnRbzaH6HJ8c14Hb4ACVcCSbRXKktvMLSzJ+b7HYBl6Zf5aPr9jO6y01rSPage3HUPePlUzt+9Irh3oFBKVXh+3p7Uq+5JvdZ1rILM4xB85Ys0PSOThMuGvTcP4/uEFLxXLKXK/ii4FIdPyllOpa+kVXDOOlsEQVxKCqx/2SrwFdj3KMke/qQf8CQz2Yd0L3+Md1Xw8ccE1SbNex8+AcFUO3wQn8sGD5+qVpDxqlI6H0QZ0lNPSqkKLeA5YfEo8tw8ePvn8HKjDyE5Hkk5h2dqAr6XE6iSfp6qGReozkVqkEgNuUR1LuEh+X9XZuDBJY8AkjwDSfGuQbpPdTL8amKq1ET8a+FZNQifgCD8AmtTpUZtqlavjUfVIPC+ugBz1cvYO6hcp56UUqoINXzzv9TX3zeIcffel+97jDEkp2VwPjmN08lp7L2YQtKFsyRfiCP9YhzpF+MxSfF4pJzDM+UcPmkJ+KWdp0ryBQIuHqW67KImF/GX1AL7lYIPFz0CSPKsTrJXdS77VCfdtybGrwbiH4Rn1Vp4BwTjExiMf2Aw1WrV4e8bn2bB7nf4351Zx5LB3V++BVDiYFESOqJQSlVoc6Pn8Miy+wjwvXKpb2KqN68O/Mglk9nGGJIuW0HmQmIil87HkXLhDJcvxJFx6SyZl+KR5Hg8UhLwvpyAb9p5/NPPUy3zAgEmkRpczDFZ7yjcN5HXRpk8o6Pb5nsS/0R6vu/Jj44olFLKQXEv9XUWEaGqrxdVfb1oUKMKNK5T7PcaY0hKTefM+XguJcSSlHCGy4lnSb94lsxLZ9m1///oHZLzPb1DICGliOXpr5EGCqVUhTc6YoxbXAorIlT186aqX12oWzfP9urP/5P1RzLyLK1Sw8+1d7V7uLR2pZRSTjMmYiJ3f2mdbkrLsB7v/tIqd6VyN6IQkYHAfwBP4H1jzIwy7pJSSpULWRPWt82/tqueSqpcTWaLiCewF7gJOAZsAkYbY3bmt79OZiulVMmVdDK7vJ166gbsN8YcNMZcBj4HhpVxn5RSqlIrb4GiIXDU4fUxuyybiEwUkc0isvnMmTOl2jmllKqMylugkHzKcpwbM8a8a4zpYozpUrt27VLqllJKVV7lLVAcAxo7vG4EnCijviillKL8BYpNQAsRCRMRH2AUVkIjpZRSZaRcXfUEICKDgVexLo/90BgzvZB9zwCHr7KpYCDuKt9b3uixlE8V5VgqynGAHkuWJsaYYp+7L3eBorSIyOaSXB5WnumxlE8V5VgqynGAHsvVKm+nnpRSSpUzGiiUUkoVqjIHinfLugNOpMdSPlWUY6koxwF6LFel0s5RKKWUKp7KPKJQSilVDBoolFJKFc4Y4zY/wIdALLDdoawW8AOwz36saZcLMAvYD2wDOhVQ50Bgj73fFIfyMOBnu955gI9d7mu/3m9vD73KY2kMrAJ2ATuAP7vj8QB+wC/AVvs4nnZGe8A4+737gHEO5Z2BaPv9s7hy+jTfz+0q/208gd+AJe58LMAhu/4oYLM7/n7ZddQAFgC7sf6/9HTT42hl/1tk/VwAHnGHY7mq/0hl9QP0ATqRM1DMzPpwgCnA8/bzwcC39ofdA/g5n/o8gQNAU8AH68uurb1tPjDKfv428Ef7+STgbfv5KGDeVR5L/ax/eCAAa3n1tu52PHZ/qtnPve1fvB7X0p79H+eg/VjTfp71n+cXrC8KsT+PQYX9Hlzlv81jwH+5Eijc8liwAkVwrjK3+v2y3/cxcL/93AcrcLjdceTTh1NAE3c4llL5gnfmDxBKzkCxB6hvP68P7LGfv4OVyyLPfg5lPYHvHF4/af8I1h2PXrn3A74DetrPvez9xAnHtQgrD4fbHg/gD/wKdL+W9oDRwDsOr9+xy+oDu/Pbr6DP7SqOoRGwAugPLLnWz66Mj+UQeQOFW/1+AYFATD6fq1sdRz7HdTPwo7scS0WYo6hrjDkJYD9mZTIvcsnyQvYJAhKMMen5vDf7Pfb28/b+V01EQoGOWH+Nu93xiIiniERhnRb8AesvnGtpr6DjaGg/z10OBX9uJfUq8Dcg0359rZ9dWR6LAb4XkS0ikpUr091+v5oCZ4CPROQ3EXlfRKq64XHkNgqYaz8v98dSEQJFQYpcsryQfQp7b3HqLTYRqQb8D3jEGHOhsF2L0W6ZHI8xJsMYE4n113g3oM01tnc1x3HNRGQIEGuM2VKMvhS1raj3u/RYbL2MMZ2AQcBDItKnkH3L6++XF9bp5reMMR2BS1inZwpSXo/jSuPWgqdDgS+K2rUYbZbKsVSEQHFaROoD2I+xdnlxliwvaJ84oIaIeOXz3uz32NurA/FX03ER8cYKEnOMMV+6+/EYYxKA1VjnU6+lvYKO45j9PHc5FPy5lUQvYKiIHMLKrtgfa4ThjseCMeaE/RgLfIUVxN3t9+sYcMwY87P9egFW4HC343A0CPjVGHPafl3uj6UiBIrFWFeVYD8ucigfK5YewPms4Z2I7Lb3yXdZc2OdvFsF3F5AvVnt3Q6stPcvERER4ANglzHmZXc9HhGpLSI17OdVgAFYV6aUqD0RaSgiK+zy74CbRaSmiNTEOp/7nX28iSLSw/78xhZQr2N7xWaMedIY08gYE4r12a00xoxxx2MRkaoiEpD13G53eyF1l8vfL2PMKeCoiLSyi24EdrrbceQymiunnXLXXT6P5WonY8rix/5wTwJpWFFxAta5tRVYl4CtAGrZ+wrwBtb58migi10ejMPkINaVBXvt/aY6lDfFuiplP9YQ0dcu97Nf77e3N73KY+mNNdzbxpXL5Qa72/EA7bEuJd2G9UX01NW0B3Qh56Tc7+199gP3OZR3sds5ALzOlUtK8/3cruF3rS9Xrnpyu2Ox+7yVK5ctTy2s7vL6+2XXEQlstn/HFmJdPeZ2x2HX4w+cBao7lJX7Y6l0S3jY56GbGmNmlXVfnKGiHI+ITAaOGGPcPlFVBTuWivL7VSGOA8rmWCpdoFBKKVUyFWGOQimllAtpoFBKKVUoDRRKKaUKpYFCKaVUoTRQKLckIkZEXnJ4/RcRmeakumeLyO1F73nN7dwhIrtEZFWu8lARSRaRKBHZKiIbHO4jKKiuLiKS71UwInJIRIKd2XdVuWigUO4qFRhZ3r4ARcSzBLtPACYZY/rls+2AMSbSGNMBa/XUvxdWkTFmszHmTyVoW6li00Ch3FU6Vs7gR3NvyD0iEJGL9mNfEVkjIvNFZK+IzBCRMSLyi4hEi0gzh2oGiMg6e78h9vs9ReQFEdkkIttE5A8O9a4Skf9i3RiVuz+j7fq3i8jzdtlTWDddvi0iLxRxrIHAOft9fiLykV3fbyLSz6EPS+znQSLyvb39Hey1fey7tb+xRynbReSuYnzOSuFV9C5KlVtvANtEZGYJ3tMBa9HCeKzcEO8bY7qJyJ+Bh7ESyYC1nP0NQDNglYg0x1pm47wxpquI+AI/isj39v7dgHBjTIxjYyLSAHgeK0nROazVXIcbY54Rkf7AX4wxm/PpZzOxVuQNwLqbt7td/hCAMSZCRFrb9bXM9d5/AuvtNm4BslaOHQicMMbcYvetevE+MlXZ6YhCuS1jrbb7CVCSUy6bjDEnjTGpWEseZH3RR2MFhyzzjTGZxph9WAGlNdZ6SWPtL/CfsZZeaGHv/0vuIGHrCqw2xpwx1pLOc7AScBUl69RTM6zg9a5d3hv4FMAYsxs4DOQOFH2Az+x9vsEejdjHOEBEnheR640x54vRD6U0UCi39yrWuf6qDmXp2L/b9oJ7Pg7bUh2eZzq8ziTnCDv3kgVZSzc/bH+BRxpjwowxWYHmUgH9y29J55JazJXgUtz68iy5YIzZy5X0q8/Zp7+UKpIGCuXWjDHxWCkfJzgUH8L6QgQYhpWitaTuEBEPe96iKVZ2se+AP4q1PDwi0tJembUwPwM3iEiwPdE9GlhTwr70xhr9AKwFxmS1D4TYfXPkuM8grEX0sk6DJRljPgNexFquW6ki6RyFqgheAiY7vH4PWCQiv2CtxlnQX/uF2YP1hV4XeNAYkyIi72OdnvrVHqmcAYYXVokx5qSIPIm15LMAS40xxVk2PGuOQoDLwP12+ZtYE+DRWCOn8caYVKs72Z4G5orIr/YxHLHLI4AXRCQTawXmPxajH0rpooBKKaUKp6eelFJKFUoDhVJKqUJpoFBKKVUoDRRKKaUKpYFCKaVUoTRQKKWUKpQGCqWUUoX6f8peSWnMolyoAAAAAElFTkSuQmCC\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib\n", + "import matplotlib.pyplot as plt\n", + "%matplotlib inline\n", + "\n", + "import numpy as np\n", + "# with visualization\n", + "naiveBoids = np.linspace(10000, 100000, 8)\n", + "naiveFPS = [180, 92, 64, 46, 27, 20, 15, 12]\n", + "# without visualization\n", + "naiveBoidsV = np.linspace(10000, 100000, 8)\n", + "naiveFPSV = [207, 102, 68, 49, 29, 20, 15, 12]\n", + "\n", + "naiveFig, naiveAxes = plt.subplots()\n", + "\n", + "naiveAxes.plot(naiveBoids, naiveFPS, label=\"With Visualization\", marker='o', markerfacecolor=\"yellow\", markeredgecolor=\"green\")\n", + "naiveAxes.plot(naiveBoidsV, naiveFPSV, label=\"Without Visualization\", marker='o', markerfacecolor=\"yellow\", markeredgecolor=\"green\")\n", + "naiveAxes.get_xaxis().set_major_formatter(\n", + " matplotlib.ticker.FuncFormatter(lambda x, p: format(int(x), ',')))\n", + "naiveAxes.yaxis.set_ticks(np.arange(0, 220, 10))\n", + "naiveAxes.xaxis.set_ticks(np.arange(10000, 110000, 15000))\n", + "naiveAxes.set_xlabel('Number of Boids') # Notice the use of set_ to begin methods\n", + "naiveAxes.set_ylabel('FPS')\n", + "naiveAxes.set_title('Naive Approach')\n", + "naiveAxes.axhline(y=30, color='r', linestyle='--',alpha=0.5)\n", + "naiveAxes.axhline(y=60, color='g', linestyle='--',alpha=0.5)\n", + "naiveAxes.legend()\n", + "\n", + "# with visualization\n", + "uniformBoids = np.linspace(100000, 700000, 7)\n", + "uniformFPS = [440, 200, 137, 60, 45, 27, 17]\n", + "# without visualization\n", + "uniformBoidsV = np.linspace(100000, 700000, 7)\n", + "uniformFPSV = [600, 300, 145, 80, 48, 28, 17]\n", + "\n", + "uniformFig, uniformAxes = plt.subplots()\n", + "\n", + "uniformAxes.plot(uniformBoids, uniformFPS, label=\"With Visualization\", marker='o', markerfacecolor=\"yellow\", markeredgecolor=\"green\")\n", + "uniformAxes.plot(uniformBoidsV, uniformFPSV, label=\"Without Visualization\", marker='o', markerfacecolor=\"yellow\", markeredgecolor=\"green\")\n", + "uniformAxes.get_xaxis().set_major_formatter(\n", + " matplotlib.ticker.FuncFormatter(lambda x, p: format(int(x), ',')))\n", + "uniformAxes.yaxis.set_ticks(np.arange(0, 650, 50))\n", + "#uniformAxes.xaxis.set_ticks(np.arange(10000, 110000, 15000))\n", + "uniformAxes.set_xlabel('Number of Boids') # Notice the use of set_ to begin methods\n", + "uniformAxes.set_ylabel('FPS')\n", + "uniformAxes.set_title('uniform Approach')\n", + "uniformAxes.axhline(y=30, color='r', linestyle='--',alpha=0.5)\n", + "uniformAxes.axhline(y=60, color='g', linestyle='--',alpha=0.5)\n", + "uniformAxes.legend()\n", + "\n", + "\n", + "# with visualization\n", + "coherentBoids = np.linspace(1000000, 2500000, 4)\n", + "coherentFPS = [95, 50, 30, 19]\n", + "# without visualization\n", + "coherentBoidsV = np.linspace(1000000, 2500000, 4)\n", + "coherentFPSV = [108, 53, 31, 20]\n", + "\n", + "coherentFig, coherentAxes = plt.subplots()\n", + "\n", + "coherentAxes.plot(coherentBoids, coherentFPS, label=\"With Visualization\", marker='o', markerfacecolor=\"yellow\", markeredgecolor=\"green\")\n", + "coherentAxes.plot(coherentBoidsV, coherentFPSV, label=\"Without Visualization\", marker='o', markerfacecolor=\"yellow\", markeredgecolor=\"green\")\n", + "coherentAxes.yaxis.set_ticks(np.arange(0, 110, 10))\n", + "coherentAxes.set_xlabel('Number of Boids') # Notice the use of set_ to begin methods\n", + "coherentAxes.set_ylabel('FPS')\n", + "coherentAxes.set_title('coherent Approach')\n", + "coherentAxes.axhline(y=30, color='r', linestyle='--',alpha=0.5)\n", + "coherentAxes.axhline(y=60, color='g', linestyle='--',alpha=0.5)\n", + "coherentAxes.legend()\n", + "\n", + "# with visualization\n", + "blockSize = [128, 256, 512, 1024]\n", + "blockFPS = [850, 859, 860, 900]\n", + "\n", + "blockFig, blockAxes = plt.subplots()\n", + "\n", + "blockAxes.plot(blockSize, blockFPS, marker='o', markerfacecolor=\"yellow\", markeredgecolor=\"green\")\n", + "blockAxes.xaxis.set_ticks(np.arange(0, 1088, 128))\n", + "blockAxes.set_xlabel('Block Size') \n", + "blockAxes.set_ylabel('FPS')\n", + "blockAxes.set_title('Block Size vs FPS')\n", + "\n", + "\n", + "\n", + "naiveFig.savefig(\"naive.png\")\n", + "uniformFig.savefig(\"uniform.png\")\n", + "coherentFig.savefig(\"coherent.png\")\n", + "blockFig.savefig(\"blocksize.png\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "8015ef9a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([1000000., 1500000., 2000000., 2500000.])" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.8" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/images/plotting/CUDA Flocking.ipynb b/images/plotting/CUDA Flocking.ipynb new file mode 100644 index 0000000..d18ac08 --- /dev/null +++ b/images/plotting/CUDA Flocking.ipynb @@ -0,0 +1,91 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 18, + "id": "1f1923e1", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib\n", + "import matplotlib.pyplot as plt\n", + "%matplotlib inline\n", + "\n", + "import numpy as np\n", + "\n", + "arraySize = [1024, 4096, 16384, 65536]\n", + "CPUScan = [0.0014, 0.0054, 0.0213, 0.0917]\n", + "GPUEfficient = [0.017408, 0.017408, 0.018432, 0.099712]\n", + "GPUNaive = [0.016384, 0.016384, 0.018432, 0.075776]\n", + "Thrust = [0.059072, 0.068608, 0.05632, 0.08601]\n", + "\n", + "scanFig, scanAxes = plt.subplots()\n", + "\n", + "scanAxes.plot(arraySize, CPUScan, label=\"CPU Scan\", marker='o')\n", + "scanAxes.plot(arraySize, GPUEfficient, label=\"GPU Efficient\", marker='o')\n", + "scanAxes.plot(arraySize, GPUNaive, label=\"GPU Naive\", marker='o')\n", + "scanAxes.plot(arraySize, Thrust, label=\"Thrust\", marker='o')\n", + "scanAxes.set_xlabel('Array Size') # Notice the use of set_ to begin methods\n", + "scanAxes.set_ylabel('Time (ms)')\n", + "scanAxes.set_title('Array Size vs Time')\n", + "scanAxes.legend()\n", + "\n", + "\n", + "scanFig.savefig(\"../scan.png\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "8015ef9a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([1000000., 1500000., 2000000., 2500000.])" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.8" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/images/scan.png b/images/scan.png new file mode 100644 index 0000000..7ae527e Binary files /dev/null and b/images/scan.png differ diff --git a/src/main.cpp b/src/main.cpp index 896ac2b..8c7d351 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,19 +7,68 @@ */ #include +#include +#include #include #include #include #include #include "testing_helpers.hpp" -const int SIZE = 1 << 8; // feel free to change the size of array +// The tests default to an array of size 1 << 8 = 256 +const int SIZE = 1 << 25; // feel free to change the size of array const int NPOT = SIZE - 3; // Non-Power-Of-Two int *a = new int[SIZE]; int *b = new int[SIZE]; int *c = new int[SIZE]; +int* bookArraya = new int[8]{ 3, 1, 7, 0 ,4 ,1 ,6, 3 }; +int* bookArrayb = new int[8]{}; +const int BOOK_SIZE = 8; + +std::string deviceName; +int deviceMaxThreadsPerBlock; +int deviceSharedMemPerBlock; +int deviceMaxThreadsPerSM; +int deviceMaxBlocksPerSM; + int main(int argc, char* argv[]) { + cudaDeviceProp deviceProp; + int gpuDevice = 0; + int device_count = 0; + cudaGetDeviceCount(&device_count); + if (gpuDevice > device_count) { + std::cout + << "Error: GPU device number is greater than the number of devices!" + << " Perhaps a CUDA-capable GPU is not installed?" + << std::endl; + return false; + } + cudaGetDeviceProperties(&deviceProp, gpuDevice); + int major = deviceProp.major; + int minor = deviceProp.minor; + deviceMaxThreadsPerBlock = deviceProp.maxThreadsPerBlock; + deviceSharedMemPerBlock = deviceProp.sharedMemPerBlock; + deviceMaxThreadsPerSM = deviceProp.maxThreadsPerMultiProcessor; + deviceMaxBlocksPerSM = deviceProp.maxBlocksPerMultiProcessor; + + + + std::ostringstream ss; + ss << " [SM " << major << "." << minor << " " << deviceProp.name << "]" + << "\n Max threads per block: " << deviceMaxThreadsPerBlock + << "\n Shared memory per block: " << deviceSharedMemPerBlock << " bytes" + // << "\n Shared memory in each block can fit " << deviceSharedMemPerBlock / sizeof(int) << " number of integers" + << "\n Max threads per SM: " << deviceMaxThreadsPerSM + << "\n Max blocks per SM: " << deviceMaxBlocksPerSM + << "\n Max grid size: " << deviceProp.maxGridSize[0] << ", " + << deviceProp.maxGridSize[1] << ", " << deviceProp.maxGridSize[2]; + + + deviceName = ss.str(); + + std::cout << deviceName << '\n'; + // Scan tests printf("\n"); @@ -31,15 +80,19 @@ int main(int argc, char* argv[]) { a[SIZE - 1] = 0; printArray(SIZE, a, true); + // initialize b using StreamCompaction::CPU::scan you implement // We use b for further comparison. Make sure your StreamCompaction::CPU::scan is correct. // At first all cases passed because b && c are all zeroes. zeroArray(SIZE, b); + // Here, power-of-two refers to the length of the input array printDesc("cpu scan, power-of-two"); StreamCompaction::CPU::scan(SIZE, b, a); printElapsedTime(StreamCompaction::CPU::timer().getCpuElapsedTimeForPreviousOperation(), "(std::chrono Measured)"); printArray(SIZE, b, true); + printf("\n"); + zeroArray(SIZE, c); printDesc("cpu scan, non-power-of-two"); StreamCompaction::CPU::scan(NPOT, c, a); @@ -47,11 +100,27 @@ int main(int argc, char* argv[]) { printArray(NPOT, b, true); printCmpResult(NPOT, b, c); + printf("\n"); +#if 0 + zeroArray(SIZE, c); + printDesc("work-efficient scan, power-of-two"); + StreamCompaction::Efficient::scan(SIZE, c, a); + printElapsedTime(StreamCompaction::Efficient::timer().getGpuElapsedTimeForPreviousOperation(), "(CUDA Measured)"); + printArray(SIZE, c, true); + printCmpResult(SIZE, b, c); + + zeroArray(SIZE, c); + printDesc("work-efficient scan, non-power-of-two"); + StreamCompaction::Efficient::scan(NPOT, c, a); + printElapsedTime(StreamCompaction::Efficient::timer().getGpuElapsedTimeForPreviousOperation(), "(CUDA Measured)"); + printArray(NPOT, c, true); + printCmpResult(NPOT, b, c); +#endif zeroArray(SIZE, c); printDesc("naive scan, power-of-two"); StreamCompaction::Naive::scan(SIZE, c, a); printElapsedTime(StreamCompaction::Naive::timer().getGpuElapsedTimeForPreviousOperation(), "(CUDA Measured)"); - //printArray(SIZE, c, true); + printArray(SIZE, c, true); printCmpResult(SIZE, b, c); /* For bug-finding only: Array of 1s to help find bugs in stream compaction or scan @@ -64,37 +133,25 @@ int main(int argc, char* argv[]) { printDesc("naive scan, non-power-of-two"); StreamCompaction::Naive::scan(NPOT, c, a); printElapsedTime(StreamCompaction::Naive::timer().getGpuElapsedTimeForPreviousOperation(), "(CUDA Measured)"); - //printArray(SIZE, c, true); + printArray(SIZE, c, true); printCmpResult(NPOT, b, c); - zeroArray(SIZE, c); - printDesc("work-efficient scan, power-of-two"); - StreamCompaction::Efficient::scan(SIZE, c, a); - printElapsedTime(StreamCompaction::Efficient::timer().getGpuElapsedTimeForPreviousOperation(), "(CUDA Measured)"); - //printArray(SIZE, c, true); - printCmpResult(SIZE, b, c); - - zeroArray(SIZE, c); - printDesc("work-efficient scan, non-power-of-two"); - StreamCompaction::Efficient::scan(NPOT, c, a); - printElapsedTime(StreamCompaction::Efficient::timer().getGpuElapsedTimeForPreviousOperation(), "(CUDA Measured)"); - //printArray(NPOT, c, true); - printCmpResult(NPOT, b, c); zeroArray(SIZE, c); printDesc("thrust scan, power-of-two"); StreamCompaction::Thrust::scan(SIZE, c, a); printElapsedTime(StreamCompaction::Thrust::timer().getGpuElapsedTimeForPreviousOperation(), "(CUDA Measured)"); - //printArray(SIZE, c, true); + // printArray(SIZE, c, true); printCmpResult(SIZE, b, c); zeroArray(SIZE, c); printDesc("thrust scan, non-power-of-two"); StreamCompaction::Thrust::scan(NPOT, c, a); printElapsedTime(StreamCompaction::Thrust::timer().getGpuElapsedTimeForPreviousOperation(), "(CUDA Measured)"); - //printArray(NPOT, c, true); + // printArray(NPOT, c, true); printCmpResult(NPOT, b, c); + printf("\n"); printf("*****************************\n"); printf("** STREAM COMPACTION TESTS **\n"); @@ -137,18 +194,21 @@ int main(int argc, char* argv[]) { printDesc("work-efficient compact, power-of-two"); count = StreamCompaction::Efficient::compact(SIZE, c, a); printElapsedTime(StreamCompaction::Efficient::timer().getGpuElapsedTimeForPreviousOperation(), "(CUDA Measured)"); - //printArray(count, c, true); + printArray(count, c, true); printCmpLenResult(count, expectedCount, b, c); zeroArray(SIZE, c); printDesc("work-efficient compact, non-power-of-two"); count = StreamCompaction::Efficient::compact(NPOT, c, a); printElapsedTime(StreamCompaction::Efficient::timer().getGpuElapsedTimeForPreviousOperation(), "(CUDA Measured)"); - //printArray(count, c, true); + printArray(count, c, true); printCmpLenResult(count, expectedNPOT, b, c); system("pause"); // stop Win32 console from closing on exit delete[] a; delete[] b; delete[] c; + + delete[] bookArraya; + delete[] bookArrayb; } diff --git a/src/testing_helpers.hpp b/src/testing_helpers.hpp index 025e94a..39161c7 100644 --- a/src/testing_helpers.hpp +++ b/src/testing_helpers.hpp @@ -49,6 +49,8 @@ void onesArray(int n, int *a) { } } +// This function populates n elements of array a with values +// between 0 and maxval - 1 void genArray(int n, int *a, int maxval) { srand(time(nullptr)); diff --git a/stream_compaction/common.h b/stream_compaction/common.h index d2c1fed..cb83569 100644 --- a/stream_compaction/common.h +++ b/stream_compaction/common.h @@ -10,7 +10,16 @@ #include #include +#if 0 +/*! Block size used for CUDA kernel launch. */ +#define blockSize 512 +#define sectionSize 512 + +#define MAX_SUM_ARRAY_SIZE 1024 +#endif + #define FILENAME (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) +// usage: checkCUDAError("a descriptive name of this error") #define checkCUDAError(msg) checkCUDAErrorFn(msg, FILENAME, __LINE__) /** @@ -26,6 +35,7 @@ inline int ilog2(int x) { return lg; } +// computes the ceiling of log2(x), as an integer. inline int ilog2ceil(int x) { return x == 1 ? 0 : ilog2(x - 1) + 1; } diff --git a/stream_compaction/cpu.cu b/stream_compaction/cpu.cu index 719fa11..9880e84 100644 --- a/stream_compaction/cpu.cu +++ b/stream_compaction/cpu.cu @@ -3,6 +3,8 @@ #include "common.h" +#include // for smart pointers + namespace StreamCompaction { namespace CPU { using StreamCompaction::Common::PerformanceTimer; @@ -13,38 +15,115 @@ namespace StreamCompaction { } /** - * CPU scan (prefix sum). + * A work-efficient CPU inclusive scan. + */ + void sequentialInclusiveScan(int n, int* odata, const int* idata) + { + int accumulator = idata[0]; + odata[0] = accumulator; + for (int i = 1; i < n; i++) + { + accumulator += idata[i]; + odata[i] = accumulator; + } + } + + /** + * A work-efficient CPU exclusive scan. + */ + void sequentialExclusiveScan(int n, int* odata, const int* idata) { + odata[0] = 0; + for (int j = 1; j < n; j++) + { + odata[j] = odata[j - 1] + idata[j - 1]; + } + } + + /** + * CPU scan (exclusive prefix sum). * For performance analysis, this is supposed to be a simple for loop. * (Optional) For better understanding before starting moving to GPU, you can simulate your GPU scan in this function first. */ void scan(int n, int *odata, const int *idata) { timer().startCpuTimer(); - // TODO + sequentialExclusiveScan(n, odata, idata); timer().endCpuTimer(); } /** * CPU stream compaction without using the scan function. - * + * This stream compaction method will remove 0s from an array of ints. * @returns the number of elements remaining after compaction. */ int compactWithoutScan(int n, int *odata, const int *idata) { timer().startCpuTimer(); - // TODO + // Given an array of elements, create a new array with all the 0s + // removed while preserving order + int index = 0; + for (int i = 0; i < n; i++) + { + int thisElement = idata[i]; + if (thisElement != 0) + { + odata[index] = thisElement; + index++; + } + } timer().endCpuTimer(); - return -1; + return n - index; } /** * CPU stream compaction using scan and scatter, like the parallel version. - * + * This stream compaction method will remove 0s from an array of ints. * @returns the number of elements remaining after compaction. */ int compactWithScan(int n, int *odata, const int *idata) { timer().startCpuTimer(); - // TODO + + int numElement = 0; + std::unique_ptrtempArray{ new int[n] }; + std::unique_ptrscanResult{ new int[n] }; + for (int i = 0; i < n; i++) + { + scanResult[i] = -1; + } + + // STEP 1: Compute temp Array with 0s and 1s + // intialize array such that all elements meet criteria + for (int i = 0; i < n; i++) + { + tempArray[i] = 1; + } + // next, figure out which one doesn't meet criteria + for (int i = 0; i < n; i++) + { + // since we want to remove 0s, elements with value = 0 doesn't + // meet criteria + if (idata[i] == 0) + { + tempArray[i] = 0; + } + } + + // STEP 2: Run exclusive scan on tempArray + sequentialExclusiveScan(n, scanResult.get(), tempArray.get()); + + // STEP 3: scatter + for (int i = 0; i < n; i++) + { + // result of scan is index into final array + int index = scanResult[i]; + // only write an element if temp array has a 1 + if (tempArray[i] == 1) + { + odata[index] = idata[i]; + numElement++; + } + } + timer().endCpuTimer(); - return -1; + return n - numElement; } } } diff --git a/stream_compaction/efficient.cu b/stream_compaction/efficient.cu index 2db346e..e3d8f15 100644 --- a/stream_compaction/efficient.cu +++ b/stream_compaction/efficient.cu @@ -2,6 +2,10 @@ #include #include "common.h" #include "efficient.h" +#include +#include + +#define SECTION_SIZE 1024 namespace StreamCompaction { namespace Efficient { @@ -12,15 +16,274 @@ namespace StreamCompaction { return timer; } + __global__ void convertFromInclusiveToExclusive(const int* inputArray, + int* outputArray, int inputSize) + { + int i = blockIdx.x * blockDim.x + threadIdx.x; + // convert inclusive scan into exclusive scan by shifting + // all elements to the right by one position and fill the frist + // element and out-of-bound elements with 0. + if (i < inputSize && i != 0) + { + + outputArray[i] = inputArray[i - 1]; + } + else { + outputArray[i] = 0; + } + } + + // lanuch this kernel with SECTION_SIZE / 2 threads in a block + __global__ void kernBrentKungScan(const int* X, int* Y, int* S, int inputSize) + { + __shared__ int XY[SECTION_SIZE]; + // 2 * here responsible for handling multiple blocks + // now you only consider one block + int i = 2 * blockIdx.x * blockDim.x + threadIdx.x; + if (i < inputSize) + { + XY[threadIdx.x] = X[i]; + } + else { + XY[threadIdx.x] = 0; + } + if ((i + blockDim.x) < inputSize) + { + XY[threadIdx.x + blockDim.x] = X[i + blockDim.x]; + } + else { + XY[threadIdx.x + blockDim.x] = 0; + } + + // note here we have stride <= blockDim.x + for (unsigned int stride = 1; stride <= blockDim.x; stride *= 2) + { + __syncthreads(); + int index = (threadIdx.x + 1) * 2 * stride - 1; + if (index < SECTION_SIZE) + { + XY[index] += XY[index - stride]; + } + } + + for (int stride = SECTION_SIZE / 4; stride > 0; stride /= 2) + { + __syncthreads(); + int index = (threadIdx.x + 1) * 2 * stride - 1; + if ((index + stride) < SECTION_SIZE) + { + XY[index + stride] += XY[index]; + } + } + + __syncthreads(); + if (i < inputSize) + { + Y[i] = XY[threadIdx.x]; + } + else { + Y[i] = 0; + } + if ((i + blockDim.x) < inputSize) + { + Y[i + blockDim.x] = XY[threadIdx.x + blockDim.x]; + } + else { + Y[i + blockDim.x] = 0; + } + + // the last thread in the block should write the output value of + // the last XY element in the block to the blockIdx.x position of + // SumArray + + // make sure XY[sectionSize - 1] has the correct partial sum + __syncthreads(); + if (threadIdx.x == blockDim.x - 1) + { + S[blockIdx.x] = XY[SECTION_SIZE - 1]; + } + } + + __global__ void kernBrentKungScan(const int* X, int* Y, int inputSize) + { + __shared__ int XY[SECTION_SIZE]; + // 2 * here responsible for handling multiple blocks + // now you only consider one block + int i = 2 * blockIdx.x * blockDim.x + threadIdx.x; + if (i < inputSize) + { + XY[threadIdx.x] = X[i]; + } + else { + XY[threadIdx.x] = 0; + } + if ((i + blockDim.x) < inputSize) + { + XY[threadIdx.x + blockDim.x] = X[i + blockDim.x]; + } + else { + XY[threadIdx.x + blockDim.x] = 0; + } + + // note here we have stride <= blockDim.x + for (unsigned int stride = 1; stride <= blockDim.x; stride *= 2) + { + __syncthreads(); + int index = (threadIdx.x + 1) * 2 * stride - 1; + if (index < SECTION_SIZE) + { + XY[index] += XY[index - stride]; + } + } + + for (int stride = SECTION_SIZE / 4; stride > 0; stride /= 2) + { + __syncthreads(); + int index = (threadIdx.x + 1) * 2 * stride - 1; + if ((index + stride) < SECTION_SIZE) + { + XY[index + stride] += XY[index]; + } + } + + __syncthreads(); + if (i < inputSize) + { + Y[i] = XY[threadIdx.x]; + } + else { + Y[i] = 0; + } + if ((i + blockDim.x) < inputSize) + { + Y[i + blockDim.x] = XY[threadIdx.x + blockDim.x]; + } + else { + Y[i + blockDim.x] = 0; + } + } + + __global__ void kernBrentKungScanAddUpSumArray(const int* S, + int* Y, int inputSize) + { + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < inputSize && blockIdx.x > 0) + { + Y[i] += S[blockIdx.x - 1]; + } + } /** * Performs prefix-sum (aka scan) on idata, storing the result into odata. */ void scan(int n, int *odata, const int *idata) { + // n could be larger than SECTION_SIZE + int idataSizeBytes = n * sizeof(int); + int sumArraySizeBytes = (n / SECTION_SIZE) * sizeof(int); + + // MaxThreadsPerBlock: 1024. However, SECTION_SIZE / 2 is needed + // for kernBrentKungScan + assert(SECTION_SIZE <= 1024); + assert(n <= 524288); + + dim3 dimGridBrent((n + (SECTION_SIZE / 2) - 1) / (SECTION_SIZE / 2), 1, 1); + dim3 dimBlockBrent(SECTION_SIZE / 2, 1, 1); + + dim3 dimGridBrentSumArray(1, 1, 1); + dim3 dimBlockBrentSumArray(SECTION_SIZE / 2, 1, 1); + + dim3 dimGridArray((n + SECTION_SIZE - 1) / SECTION_SIZE, 1, 1); + dim3 dimBlockArray(SECTION_SIZE, 1, 1); + + int* d_X; + int* d_Y; + int* d_S; + int* d_SOut; + int* d_YExclusive; + cudaMalloc((void**)&d_X, idataSizeBytes); + checkCUDAError("cudaMalloc d_X failed!"); + cudaMalloc((void**)&d_Y, idataSizeBytes); + checkCUDAError("cudaMalloc d_Y failed!"); + cudaMalloc((void**)&d_YExclusive, idataSizeBytes); + checkCUDAError("cudaMalloc d_YExclusive failed!"); + cudaMalloc((void**)&d_S, sumArraySizeBytes); + checkCUDAError("cudaMalloc d_S failed!"); + cudaMalloc((void**)&d_SOut, sumArraySizeBytes); + checkCUDAError("cudaMalloc d_SOut failed!"); + + cudaMemcpy(d_X, idata, idataSizeBytes, cudaMemcpyHostToDevice); + timer().startGpuTimer(); - // TODO + kernBrentKungScan << > > (d_X, d_Y, d_S, n); + kernBrentKungScan << > > (d_S, d_SOut, n); + kernBrentKungScanAddUpSumArray << > > (d_SOut, d_Y, n); + convertFromInclusiveToExclusive << > > (d_Y, d_YExclusive, n); timer().endGpuTimer(); + + cudaMemcpy(odata, d_YExclusive, idataSizeBytes, cudaMemcpyDeviceToHost); + checkCUDAError("memCpy back failed!"); + + cudaFree(d_X); + cudaFree(d_Y); + cudaFree(d_S); + cudaFree(d_SOut); + cudaFree(d_YExclusive); + checkCUDAError("cudaFree failed!"); + } + + void scanWithoutTimer(int n, int* odata, const int* idata) { + // n could be larger than SECTION_SIZE + int idataSizeBytes = n * sizeof(int); + int sumArraySizeBytes = (n / SECTION_SIZE) * sizeof(int); + + // MaxThreadsPerBlock: 1024. However, SECTION_SIZE / 2 is needed + // for kernBrentKungScan + assert(SECTION_SIZE <= 1024); + assert(n <= 524288); + + dim3 dimGridBrent((n + (SECTION_SIZE / 2) - 1) / (SECTION_SIZE / 2), 1, 1); + dim3 dimBlockBrent(SECTION_SIZE / 2, 1, 1); + + dim3 dimGridBrentSumArray(1, 1, 1); + dim3 dimBlockBrentSumArray(SECTION_SIZE / 2, 1, 1); + + dim3 dimGridArray((n + SECTION_SIZE - 1) / SECTION_SIZE, 1, 1); + dim3 dimBlockArray(SECTION_SIZE, 1, 1); + + int* d_X; + int* d_Y; + int* d_S; + int* d_SOut; + int* d_YExclusive; + cudaMalloc((void**)&d_X, idataSizeBytes); + checkCUDAError("cudaMalloc d_X failed!"); + cudaMalloc((void**)&d_Y, idataSizeBytes); + checkCUDAError("cudaMalloc d_Y failed!"); + cudaMalloc((void**)&d_YExclusive, idataSizeBytes); + checkCUDAError("cudaMalloc d_YExclusive failed!"); + cudaMalloc((void**)&d_S, sumArraySizeBytes); + checkCUDAError("cudaMalloc d_S failed!"); + cudaMalloc((void**)&d_SOut, sumArraySizeBytes); + checkCUDAError("cudaMalloc d_SOut failed!"); + + cudaMemcpy(d_X, idata, idataSizeBytes, cudaMemcpyHostToDevice); + + kernBrentKungScan << > > (d_X, d_Y, d_S, n); + kernBrentKungScan << > > (d_S, d_SOut, n); + kernBrentKungScanAddUpSumArray << > > (d_SOut, d_Y, n); + convertFromInclusiveToExclusive << > > (d_Y, d_YExclusive, n); + + cudaMemcpy(odata, d_YExclusive, idataSizeBytes, cudaMemcpyDeviceToHost); + checkCUDAError("memCpy back failed!"); + + cudaFree(d_X); + cudaFree(d_Y); + cudaFree(d_S); + cudaFree(d_SOut); + cudaFree(d_YExclusive); + checkCUDAError("cudaFree failed!"); } + /** * Performs stream compaction on idata, storing the result into odata. * All zeroes are discarded. @@ -31,10 +294,51 @@ namespace StreamCompaction { * @returns The number of elements remaining after compaction. */ int compact(int n, int *odata, const int *idata) { - timer().startGpuTimer(); - // TODO - timer().endGpuTimer(); - return -1; + timer().startCpuTimer(); + + int numElement = 0; + std::unique_ptrtempArray{ new int[n] }; + std::unique_ptrscanResult{ new int[n] }; + for (int i = 0; i < n; i++) + { + scanResult[i] = -1; + } + + // STEP 1: Compute temp Array with 0s and 1s + // intialize array such that all elements meet criteria + for (int i = 0; i < n; i++) + { + tempArray[i] = 1; + } + // next, figure out which one doesn't meet criteria + for (int i = 0; i < n; i++) + { + // since we want to remove 0s, elements with value = 0 doesn't + // meet criteria + if (idata[i] == 0) + { + tempArray[i] = 0; + } + } + + // STEP 2: Run exclusive scan on tempArray + scanWithoutTimer(n, scanResult.get(), tempArray.get()); + + // STEP 3: scatter + for (int i = 0; i < n; i++) + { + // result of scan is index into final array + int index = scanResult[i]; + // only write an element if temp array has a 1 + if (tempArray[i] == 1) + { + odata[index] = idata[i]; + numElement++; + } + } + + timer().endCpuTimer(); + return n - numElement; } } } diff --git a/stream_compaction/naive.cu b/stream_compaction/naive.cu index 4308876..88b4501 100644 --- a/stream_compaction/naive.cu +++ b/stream_compaction/naive.cu @@ -3,6 +3,11 @@ #include "common.h" #include "naive.h" +#include // testing +#include // for assert() + +#define SECTION_SIZE 1024 + namespace StreamCompaction { namespace Naive { using StreamCompaction::Common::PerformanceTimer; @@ -11,15 +16,278 @@ namespace StreamCompaction { static PerformanceTimer timer; return timer; } - // TODO: __global__ + __global__ void convertFromInclusiveToExclusive(const int* inputArray, + int* outputArray, int inputSize) + { + int i = blockIdx.x * blockDim.x + threadIdx.x; + // convert inclusive scan into exclusive scan by shifting + // all elements to the right by one position and fill the frist + // element and out-of-bound elements with 0. + if (i < inputSize && i != 0) + { + + outputArray[i] = inputArray[i - 1]; + } + else { + outputArray[i] = 0; + } + } + + __global__ void kernKoggeStoneScanAddUpSumArray(const int* S, + int* Y, int inputSize) + { + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < inputSize && blockIdx.x > 0) + { + Y[i] += S[blockIdx.x - 1]; + } + } + + __global__ void kernKoggeStoneScan(int* X, int* Y, int* S, int inputSize) + { + __shared__ int XY[SECTION_SIZE]; + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < inputSize) + { + XY[threadIdx.x] = X[i]; + } + else { + XY[threadIdx.x] = 0; + } + // performs iterative scan on XY + // note that it is stride < blockDim.x, not stride <= blockDim.x: + // if you have 16 elements, stride could only be 1,2,4,8 + for (unsigned int stride = 1; stride < blockDim.x; stride *= 2) + { + // make sure that input is in place + __syncthreads(); + bool written = false; + int temp = 0; + if (threadIdx.x >= stride) + { + temp = XY[threadIdx.x] + XY[threadIdx.x - stride]; + written = true; + } + // make sure previous output has been consumed + __syncthreads(); + if (written) + { + XY[threadIdx.x] = temp; + } + } + Y[i] = XY[threadIdx.x]; + + // the last thread in the block should write the output value of + // the last XY element in the block to the blockIdx.x position of + // SumArray + + // make sure XY[sectionSize - 1] has the correct partial sum + __syncthreads(); + if (threadIdx.x == blockDim.x - 1) + { + S[blockIdx.x] = XY[SECTION_SIZE - 1]; + } + } + __global__ void kernKoggeStoneScan(int* X, int* Y, int inputSize) + { + __shared__ int XY[SECTION_SIZE]; + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < inputSize) + { + XY[threadIdx.x] = X[i]; + } + else { + XY[threadIdx.x] = 0; + } + // performs iterative scan on XY + // note that it is stride < blockDim.x, not stride <= blockDim.x: + // if you have 16 elements, stride could only be 1,2,4,8 + for (unsigned int stride = 1; stride < blockDim.x; stride *= 2) + { + // make sure that input is in place + __syncthreads(); + bool written = false; + int temp = 0; + if (threadIdx.x >= stride) + { + temp = XY[threadIdx.x] + XY[threadIdx.x - stride]; + written = true; + } + // make sure previous output has been consumed + __syncthreads(); + if (written) + { + XY[threadIdx.x] = temp; + } + } + Y[i] = XY[threadIdx.x]; + } + + void scanRecursiveHelper(int n, int* odata, const int* idata) + { + int blockSize = (n + SECTION_SIZE - 1) / SECTION_SIZE; + int idataSizeBytes = n * sizeof(int); + + int sumArraySizeBytes = n <= 1024 ? n * sizeof(int) + : (n / SECTION_SIZE) * sizeof(int); + + dim3 dimGridKogge(blockSize, 1, 1); + dim3 dimBlockKogge(SECTION_SIZE, 1, 1); + + if (blockSize == 1) + { + int* d_X; + int* d_Y; + cudaMalloc((void**)&d_X, idataSizeBytes); + checkCUDAError("cudaMalloc d_X failed!"); + cudaMalloc((void**)&d_Y, idataSizeBytes); + checkCUDAError("cudaMalloc d_Y failed!"); + + cudaMemcpy(d_X, idata, idataSizeBytes, cudaMemcpyHostToDevice); + + kernKoggeStoneScan << > > ( + d_X, d_Y, n); + + cudaMemcpy(odata, d_Y, idataSizeBytes, cudaMemcpyDeviceToHost); + checkCUDAError("memCpy back failed!"); +#if 0 + std::cout << '\n'; + for (int i = 0; i < n; i++) + { + std::cout << odata[i] << '\n'; + } +#endif + cudaFree(d_X); + cudaFree(d_Y); + checkCUDAError("cudaFree failed!"); + } + else { + int* d_X; + int* d_Y; + cudaMalloc((void**)&d_X, idataSizeBytes); + checkCUDAError("cudaMalloc d_X failed!"); + cudaMalloc((void**)&d_Y, idataSizeBytes); + checkCUDAError("cudaMalloc d_Y failed!"); + int* d_S; + int* d_SOut; + cudaMalloc((void**)&d_S, sumArraySizeBytes); + checkCUDAError("cudaMalloc d_S failed!"); + cudaMalloc((void**)&d_SOut, sumArraySizeBytes); + checkCUDAError("cudaMalloc d_SOut failed!"); + + cudaMemcpy(d_X, idata, idataSizeBytes, cudaMemcpyHostToDevice); + checkCUDAError("memCpy back failed!"); + + kernKoggeStoneScan << > > (d_X, d_Y, d_S, n); + + scanRecursiveHelper(n / SECTION_SIZE, d_SOut, d_S); + kernKoggeStoneScanAddUpSumArray << > > ( + d_SOut, d_Y, n); + + cudaMemcpy(odata, d_Y, idataSizeBytes, cudaMemcpyDeviceToHost); + checkCUDAError("memCpy back failed!"); + + cudaFree(d_X); + cudaFree(d_Y); + cudaFree(d_S); + cudaFree(d_SOut); + checkCUDAError("cudaFree failed!"); + } + } /** * Performs prefix-sum (aka scan) on idata, storing the result into odata. */ void scan(int n, int *odata, const int *idata) { + // n could be larger than SECTION_SIZE + int idataSizeBytes = n * sizeof(int); + + int sumArraySizeBytes = (n / SECTION_SIZE) * sizeof(int); + + dim3 dimGridKogge((n + SECTION_SIZE - 1) / SECTION_SIZE, 1, 1); + dim3 dimBlockKogge(SECTION_SIZE, 1, 1); + + int* sumArrayOutput = new int[n / SECTION_SIZE]; + + int* d_X; + int* d_Y; + int* d_S; + int* d_SOut; + int* d_YExclusive; + cudaMalloc((void**)&d_X, idataSizeBytes); + checkCUDAError("cudaMalloc d_X failed!"); + cudaMalloc((void**)&d_Y, idataSizeBytes); + checkCUDAError("cudaMalloc d_Y failed!"); + cudaMalloc((void**)&d_YExclusive, idataSizeBytes); + checkCUDAError("cudaMalloc d_YExclusive failed!"); + cudaMalloc((void**)&d_S, sumArraySizeBytes); + checkCUDAError("cudaMalloc d_S failed!"); + cudaMalloc((void**)&d_SOut, sumArraySizeBytes); + checkCUDAError("cudaMalloc d_SOut failed!"); + + cudaMemcpy(d_X, idata, idataSizeBytes, cudaMemcpyHostToDevice); + timer().startGpuTimer(); - // TODO + kernKoggeStoneScan <<>> (d_X, d_Y, d_S, n); + scanRecursiveHelper(n / SECTION_SIZE, d_SOut, d_S); + + kernKoggeStoneScanAddUpSumArray <<>> ( + d_SOut, d_Y, n); + convertFromInclusiveToExclusive << > > ( + d_Y, d_YExclusive, n); timer().endGpuTimer(); + + cudaMemcpy(odata, d_YExclusive, idataSizeBytes, cudaMemcpyDeviceToHost); + checkCUDAError("memCpy back failed!"); + + cudaFree(d_X); + cudaFree(d_Y); + cudaFree(d_S); + cudaFree(d_SOut); + cudaFree(d_YExclusive); + checkCUDAError("cudaFree failed!"); } } } + +#if 0 +void unitTestConversion() +{ + // for testing + int numObject = 8; + int size = numObject * sizeof(int); + int* toyExclusiveArray = new int[numObject]; + int* toyInclusiveArray = new int[numObject] {3, 4, 11, 11, 15, 16, 22, 25}; + + int* dev_toyExclusiveArray; + int* dev_toyInclusiveArray; + + cudaMalloc((void**)&dev_toyExclusiveArray, size); + checkCUDAError("cudaMalloc dev_toyExclusiveArray failed!"); + + cudaMalloc((void**)&dev_toyInclusiveArray, size); + checkCUDAError("cudaMalloc dev_toyInclusiveArray failed!"); + + cudaMemcpy(dev_toyInclusiveArray, toyInclusiveArray, size, + cudaMemcpyHostToDevice); + + dim3 dimGridArray((numObject + blockSize - 1) / blockSize, 1, 1); + dim3 dimBlockArray(blockSize, 1, 1); + convertFromInclusiveToExclusive << > > ( + dev_toyInclusiveArray, dev_toyExclusiveArray, numObject); + + cudaMemcpy(toyExclusiveArray, dev_toyExclusiveArray, size, + cudaMemcpyDeviceToHost); + checkCUDAError("memCpy back failed!"); + + printf("\n"); + + for (int i = 0; i < numObject; i++) + { + std::cout << toyExclusiveArray[i] << '\n'; + } + + printf("\n"); + +} +#endif \ No newline at end of file diff --git a/stream_compaction/thrust.cu b/stream_compaction/thrust.cu index 1def45e..4320c2c 100644 --- a/stream_compaction/thrust.cu +++ b/stream_compaction/thrust.cu @@ -18,11 +18,17 @@ namespace StreamCompaction { * Performs prefix-sum (aka scan) on idata, storing the result into odata. */ void scan(int n, int *odata, const int *idata) { + + thrust::device_vector d_idata(idata, idata + n); + thrust::device_vector d_result(n); + timer().startGpuTimer(); // TODO use `thrust::exclusive_scan` // example: for device_vectors dv_in and dv_out: // thrust::exclusive_scan(dv_in.begin(), dv_in.end(), dv_out.begin()); + thrust::exclusive_scan(d_idata.begin(), d_idata.end(), d_result.begin()); timer().endGpuTimer(); + thrust::copy(d_result.begin(), d_result.end(), odata); } } }