This shows you the differences between two versions of the page.
haas:spring2017:sysprog:projects:pnc1 [2016/02/28 17:29] – external edit 127.0.0.1 | haas:spring2017:sysprog:projects:pnc1 [2017/03/21 19:41] (current) – wedge | ||
---|---|---|---|
Line 6: | Line 6: | ||
~~TOC~~ | ~~TOC~~ | ||
- | ======Project: | + | ======Project: |
=====Objective===== | =====Objective===== | ||
- | To apply your skills in the implementation of prime number calculating | + | To apply your skills in algorithmic optimization through |
- | + | ||
- | =====Prerequisites/ | + | |
- | In addition to the new skills required on previous projects, to successfully accomplish/ | + | |
- | + | ||
- | * **fork**(**2**) | + | |
- | * algorithms to appropriately split up the work among a group | + | |
=====Algorithmic Complexity===== | =====Algorithmic Complexity===== | ||
Line 24: | Line 18: | ||
Additionally, | Additionally, | ||
- | Through the concurrent implementations in this project, we should start to see some of the impacts those approaches can have on runtime. It may not offer exact 50% cuts in runtime, but more fully utilizing available computing resources should have a notable | + | This project |
- | =====Background===== | + | =====Optimizing the prime number calculation===== |
- | We explored | + | We should be fairly familiar with the process |
- | =====Calculating the primality of a number===== | + | ====odds (primeodds)==== |
- | As of yet, there is no quick and direct way of determining | + | Some optimizations can be the result |
- | This process incurs a considerable amount of processing overhead on the task, so much so that increasingly large values take increasing amounts of time. Often, approaches | + | So does it make sense to check an even number |
- | + | ||
- | Your task for this project is to implement 2 prime number programs: | + | |
- | + | ||
- | - forked brute force prime calculation | + | |
- | - forked square root-optimized brute force calculation | + | |
- | + | ||
- | ====brute force==== | + | |
- | The brute force approach is the simplest to implement (and likely also the worst-performing). We will use it as our baseline (it is nice to have something to compare against). | + | |
- | + | ||
- | To perform it, we simply attempt to evenly divide all the values between 1 and the number in question. If any one of them divides evenly, the number is **NOT** prime, but instead a composite value. | + | |
- | + | ||
- | Checking the remainder of a division indicates whether or not a division was clean (having 0 remainder indicates such a state). | + | |
- | + | ||
- | For example, the number 11: | + | |
- | + | ||
- | < | + | |
- | 11 % 2 = 1 (2 is not a factor of 11) | + | |
- | 11 % 3 = 2 (3 is not a factor of 11) | + | |
- | 11 % 4 = 3 (4 is not a factor of 11) | + | |
- | 11 % 5 = 1 (5 is not a factor of 11) | + | |
- | 11 % 6 = 5 (6 is not a factor of 11) | + | |
- | 11 % 7 = 4 (7 is not a factor of 11) | + | |
- | 11 % 8 = 3 (8 is not a factor of 11) | + | |
- | 11 % 9 = 2 (9 is not a factor of 11) | + | |
- | 11 % 10 = 1 (10 is not a factor of 11) | + | |
- | </ | + | |
- | + | ||
- | Because none of the values 2-10 evenly divided into 11, we can say it passed the test: **11 is a prime number** | + | |
- | + | ||
- | On the other hand, take 119: | + | |
- | + | ||
- | < | + | |
- | 119 % 2 = 1 (2 is not a factor of 119) | + | |
- | 119 % 3 = 2 (3 is not a factor of 119) | + | |
- | 119 % 4 = 3 (4 is not a factor of 119) | + | |
- | 119 % 5 = 4 (5 is not a factor of 119) | + | |
- | 119 % 6 = 5 (6 is not a factor of 119) | + | |
- | 119 % 7 = 0 (7 is a factor of 119) | + | |
- | </ | + | |
- | Because 7 evenly divided into 119, it failed the test: 119 is **not** a prime, but instead a composite | + | And can we predict even numbers? Yes, we can: they occur every other number. |
- | There is no further need to check the remaining values, as once we have proven the non-primality of a number, | + | Therefore, we can start our number |
- | So when contemplating a concurrent solution, imagine | + | To make our output correct, we would simply display |
- | * 4096 / 4 is 1024 | + | |
- | * one child would do 2-1024 | + | |
- | * another would do 1025-2048 | + | |
- | * a third would do 2049-3072 | + | |
- | * and the last would do 3073-4096 (4095, technically) | + | |
- | This way the same amount of work is being performed, only instead of in one batch run, as 4 concurrent fractions of the work. Theoretically, | + | This program |
- | ====square root==== | + | ====square root (primesqrt)==== |
An optimization to the computation of prime numbers is the square root trick. Basically, if we've processed numbers up to the square root of the number we're testing, and none have proven to be evenly divisible, we can also assume primality and bail out. | An optimization to the computation of prime numbers is the square root trick. Basically, if we've processed numbers up to the square root of the number we're testing, and none have proven to be evenly divisible, we can also assume primality and bail out. | ||
Line 116: | Line 66: | ||
This will dramatically improve the runtime, and offers a nice comparison against our brute force baseline. | This will dramatically improve the runtime, and offers a nice comparison against our brute force baseline. | ||
- | Similar to the forked brute force, we will look to split up the work among a group of child processes, | + | NOTE: You will be reverting |
- | =====Program===== | + | This program should be an optimization based on your **primebruteopt** program from pnc0. |
- | It is your task to write 2 separate prime number calculating programs: | + | |
- | - **primebrutefork.c**: | + | ====sqrt() odds (primesqrtodds)==== |
- | | + | In the previous program we used **sqrt()** against all the values, even or odd. |
- | Your program | + | This program will eliminate |
- | * obtain 3 parameters from the command-line (see **command-line arguments** section below): | + | |
- | * argv[1]: maximum value to calculate to (your program should run from (approximately) 2 through that number (inclusive of that number) | + | |
- | * argv[2]: visibility. If a **1** is provided, print out the prime numbers in a space separated list; if a **0** is provided, run silent: only display the runtime information. | + | |
- | * argv[3]: number of child processes to fork (we'll be testing with 2 and 4 child processes). | + | |
- | * these values should be positive integer values; you can make the assumption that the user will always do the right thing. | + | |
- | * start your stopwatch (see **timing** section below): | + | |
- | * determine how to split up the work load, and fork() the requested number of child processes | + | |
- | * have each child perform its share of the work utilizing the given algorithm | + | |
- | * if enabled, display the prime numbers found in the range | + | |
- | * in a concurrent implementation, | + | |
- | * output the processing run-time to STDERR (do this always). | + | |
- | * your output **MUST** be conformant to the example output in the **execution** section below. This is also a test to see how well you can implement to specifications. Basically: | + | |
- | * if primes are being displayed, they are space-separated (first prime hugs the left margin), and when all said and done, a newline is issued. | + | |
- | * the timing information will be displayed in accordance to code I will provide (in the **timing** section). | + | |
- | ====Other considerations==== | + | This program should be an optimization of your **primesqrt** program. |
- | All your programs MUST perform the calculations to determine primality- you may not always be printing it out (depending on argv[2]), but work must be done to ensure the value is identified as a prime/ | + | |
- | For example: | + | ====sqrt()-less square root (primesqrtopt)==== |
+ | An optimization to the previous process, which used **sqrt()**, this variation will do the exact same thing, but without using the **sqrt()** function. It will approximate the square root. | ||
- | < | + | We know that a square root (especially a whole numbered square root), is when we have whole number |
- | if (show == 1) | + | |
- | { | + | |
- | work to determine if it is prime | + | |
- | if prime | + | |
- | print | + | |
- | } | + | |
- | </ | + | |
- | will actually skip the core processing, and you’ll see some amazing runtimes as a result. They may be amazing, but they’re not real, because you’re not actually doing anything. | + | < |
+ | lab46:~$ count=0; for ((i=2; i<152; i++)); do printf "[%3d] %2d " " | ||
+ | [ 2] 1 [ 3] 1 [ 4] 2 [ 5] 2 [ 6] 2 [ 7] 2 [ 8] 2 [ 9] 3 [ 10] 3 [ 11] 3 | ||
+ | [ 12] 3 [ 13] 3 [ 14] 3 [ 15] 3 [ 16] 4 [ 17] 4 [ 18] 4 [ 19] 4 [ 20] 4 [ 21] 4 | ||
+ | [ 22] 4 [ 23] 4 [ 24] 4 [ 25] 5 [ 26] 5 [ 27] 5 [ 28] 5 [ 29] 5 [ 30] 5 [ 31] 5 | ||
+ | [ 32] 5 [ 33] 5 [ 34] 5 [ 35] 5 [ 36] 6 [ 37] 6 [ 38] 6 [ 39] 6 [ 40] 6 [ 41] 6 | ||
+ | [ 42] 6 [ 43] 6 [ 44] 6 [ 45] 6 [ 46] 6 [ 47] 6 [ 48] 6 [ 49] 7 [ 50] 7 [ 51] 7 | ||
+ | [ 52] 7 [ 53] 7 [ 54] 7 [ 55] 7 [ 56] 7 [ 57] 7 [ 58] 7 [ 59] 7 [ 60] 7 [ 61] 7 | ||
+ | [ 62] 7 [ 63] 7 [ 64] 8 [ 65] 8 [ 66] 8 [ 67] 8 [ 68] 8 [ 69] 8 [ 70] 8 [ 71] 8 | ||
+ | [ 72] 8 [ 73] 8 [ 74] 8 [ 75] 8 [ 76] 8 [ 77] 8 [ 78] 8 [ 79] 8 [ 80] 8 [ 81] 9 | ||
+ | [ 82] 9 [ 83] 9 [ 84] 9 [ 85] 9 [ 86] 9 [ 87] 9 [ 88] 9 [ 89] 9 [ 90] 9 [ 91] 9 | ||
+ | [ 92] 9 [ 93] 9 [ 94] 9 [ 95] 9 [ 96] 9 [ 97] 9 [ 98] 9 [ 99] 9 [100] 10 [101] 10 | ||
+ | [102] 10 [103] 10 [104] 10 [105] 10 [106] 10 [107] 10 [108] 10 [109] 10 [110] 10 [111] 10 | ||
+ | [112] 10 [113] 10 [114] 10 [115] 10 [116] 10 [117] 10 [118] 10 [119] 10 [120] 10 [121] 11 | ||
+ | [122] 11 [123] 11 [124] 11 [125] 11 [126] 11 [127] 11 [128] 11 [129] 11 [130] 11 [131] 11 | ||
+ | [132] 11 [133] 11 [134] 11 [135] 11 [136] 11 [137] 11 [138] 11 [139] 11 [140] 11 [141] 11 | ||
+ | [142] 11 [143] 11 [144] 12 [145] 12 [146] 12 [147] 12 [148] 12 [149] 12 [150] 12 [151] 12 | ||
+ | </ | ||
- | What you want instead: | + | Square root of 81 is 9, but so is the square root of 82, 83, 84... etc. up until we hit 100. |
- | < | + | If we were checking 87 to be prime, we'd only have to check up to 9. |
- | work to determine if it is prime | + | |
- | if (show == 1) | + | |
- | { | + | |
- | if prime | + | |
- | print number | + | |
- | } | + | |
- | </ | + | |
- | there are many ways to express | + | We don't need a **sqrt()** function |
- | That also isn’t to say you can’t avoid doing a work run if you’re able to determine its non-primality with a simple pretest | + | There are some important lessons at play here: |
+ | |||
+ | * approximation | ||
+ | * approximation can result in a simpler algorithm, improving runtime | ||
+ | * **sqrt()** is more complex than you may be aware, not to mention it is in a function. By avoiding that function call, we eliminate some overhead, and that can make a big difference in runtime performance. | ||
+ | |||
+ | NOTE: Again, for comparison sake, check ALL numbers | ||
+ | |||
+ | This program should be an optimization | ||
+ | |||
+ | ====sqrt()-less odds (primesqrtoptodds)==== | ||
+ | And, to round out our analysis, enhance the optimized sqrt variant to only check odd values. | ||
+ | |||
+ | This program should be an optimization of your **primesqrtopt** program. | ||
+ | |||
+ | =====Program===== | ||
+ | It is your task to write some optimized prime number calculating programs: | ||
+ | |||
+ | - **primeodds.c**: | ||
+ | - **primesqrt.c**: | ||
+ | - **primesqrtodds.c**: | ||
+ | - **primesqrtopt.c**: | ||
+ | - **primesqrtoptodds.c**: | ||
+ | |||
+ | Your program should: | ||
+ | * obtain 1 parameter from the command-line (see **command-line arguments** section below): | ||
+ | * argv[1]: maximum value to calculate to (your program should run from (approximately) 2 through | ||
+ | * this value should be a positive integer value; you can make the assumption that the user will always do the right thing. | ||
+ | * do the specified algorithmic optimizations | ||
+ | * please take note in differences in run-time, contemplating the impact the various algorithms and approaches have on performance. | ||
+ | * start your stopwatch (see **timing** section below): | ||
+ | * perform the correct algorithm against the input | ||
+ | * display (to STDOUT) the prime numbers found in the range | ||
+ | * output the processing run-time to STDERR | ||
+ | * your output **MUST** be conformant to the example output in the **execution** section below. This is also a test to see how well you can implement to specifications. Basically: | ||
+ | * as primes are being displayed, they are space-separated | ||
+ | * the timing information will be displayed in accordance to code I will provide (in the **timing** section). | ||
=====Command-Line Arguments===== | =====Command-Line Arguments===== | ||
Line 195: | Line 169: | ||
* argv[0]: program invocation (path + program name) | * argv[0]: program invocation (path + program name) | ||
* argv[1]: our maximum / upper bound | * argv[1]: our maximum / upper bound | ||
- | * argv[2]: visibility (1 to show primes, 0 to be silent) | ||
- | * argv[3]: processes (number of child processes to fork; 2,4) | ||
- | |||
- | There are ways to do flexible argument parsing, and even to have dashed options as we have on various commands. But such things are beyond the scope of our current endeavors, so we will stick to this basic functionality for now. | ||
====Simple argument checks==== | ====Simple argument checks==== | ||
- | Although I'm not going to require extensive argument checking for this project, | + | Although I'm not going to require extensive argument |
<code c> | <code c> | ||
- | if (argc < 4) // if less than 4 arguments have been provided | + | if (argc < 2) // if less than 2 arguments have been provided |
{ | { | ||
fprintf(stderr, | fprintf(stderr, | ||
Line 211: | Line 181: | ||
</ | </ | ||
- | If you're wondering, "why 4? I thought we only had 3.", C includes the program' | + | ====Grab and convert max==== |
- | + | Finally, we need to put the argument | |
- | ====Grab and convert max and visibility==== | + | |
- | Finally, we need to put the arguments | + | |
- | I'd recommend declaring | + | I'd recommend declaring |
We will use the **atoi(3)** function to quickly convert the command-line arguments into **int** values: | We will use the **atoi(3)** function to quickly convert the command-line arguments into **int** values: | ||
Line 222: | Line 190: | ||
<code c> | <code c> | ||
max = atoi(argv[1]); | max = atoi(argv[1]); | ||
- | show = atoi(argv[2]); | ||
- | np = atoi(argv[3]); | ||
</ | </ | ||
Line 283: | Line 249: | ||
=====Execution===== | =====Execution===== | ||
- | Several operating behaviors are shown as examples. | + | Your program output should be as follows (given the specified range): |
- | + | ||
- | Brute force showing primes, 2 child processes: | + | |
<cli> | <cli> | ||
- | lab46: | + | lab46: |
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 | 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 | ||
0.000088 | 0.000088 | ||
lab46: | lab46: | ||
</ | </ | ||
- | |||
- | Brute force not showing primes, 2 child processes: | ||
- | |||
- | <cli> | ||
- | lab46: | ||
- | 0.000008 | ||
- | lab46: | ||
- | </ | ||
- | |||
- | Similarly, for the square root version (showing primes, 4 child processes): | ||
- | |||
- | <cli> | ||
- | lab46: | ||
- | 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 | ||
- | 0.000089 | ||
- | lab46: | ||
- | </ | ||
- | |||
- | And, without showing primes (4 child processes): | ||
- | |||
- | <cli> | ||
- | lab46: | ||
- | 0.000006 | ||
- | lab46: | ||
- | </ | ||
- | |||
- | Don't be alarmed by the visible square root actually seeming to take MORE time; we have to consider the range as well: 90 is barely anything, and there is overhead incurred from the **sqrt()** function call. The real savings will start to be seen once we get into the thousands (and beyond). | ||
- | |||
- | And that's another neat thing with algorithm comparison: a " | ||
- | |||
- | The same goes for your optimized solution (same parameters). | ||
The execution of the programs is short and simple- grab the parameters, do the processing, produce the output, and then terminate. | The execution of the programs is short and simple- grab the parameters, do the processing, produce the output, and then terminate. | ||
Line 330: | Line 263: | ||
If you'd like to compare your implementations, | If you'd like to compare your implementations, | ||
- | In order to work, you **MUST** be in the directory where your **primebrutefork** and **primesqrtfork**. | + | In order to work, you **MUST** be in the directory where your **primesqrt**, |
- | + | ||
- | It is recommended | + | |
- | For instance (running on my implementations): | + | For instance (running on my implementations |
<cli> | <cli> | ||
lab46: | lab46: | ||
- | ============================================ | + | ==================================================================================================== |
- | | + | range |
- | ============================================ | + | ==================================================================================================== |
- | 8 | + | |
- | 16 | + | |
- | | + | |
- | 64 | + | 1024 0.005385 |
- | 128 | + | |
- | | + | |
- | 512 | + | |
- | 1024 | + | |
- | 2048 0.001761 | + | |
- | 4096 0.006115 | + | |
- | 8192 0.021259 | + | 131072 |
- | 16384 0.077184 | + | 262144 |
- | 32768 0.281958 | + | 524288 |
- | 65536 1.046501 | + | |
- | 131072 | + | |
- | | + | 4194304 |
- | | + | 8388608 |
- | 1048576 | + | ==================================================================================================== |
- | 2097152 | + | |
- | 4194304 | + | ==================================================================================================== |
- | | + | |
- | ============================================ | + | |
- | | + | |
- | ============================================ | + | |
lab46: | lab46: | ||
</ | </ | ||
Line 374: | Line 301: | ||
If you don't feel like waiting, simply hit **CTRL-c** and the script will terminate. | If you don't feel like waiting, simply hit **CTRL-c** and the script will terminate. | ||
- | In the example output above, my **primeopt** is playing with an implementation of the **6a+/-1** algorithm. | + | I also include a validation check- to ensure your prime programs are actually producing the correct list of prime numbers. If the check is successful, you will see " |
- | + | ||
- | I also include a validation check- to ensure your prime programs are actually producing the correct list of prime numbers. If the check is successful, you will see " | + | |
- | + | ||
- | If you'd like to experiment with other variations, the script also recognizes prime variants of the following names: | + | |
- | * primeoptfork (for an additional optimization) | + | |
- | * primemapfork | + | |
- | * primesievefork | + | |
=====Submission===== | =====Submission===== | ||
Line 389: | Line 309: | ||
* Output must be correct, and match the form given in the sample output above. | * Output must be correct, and match the form given in the sample output above. | ||
* Code must be nicely and consistently indented (you may use the **indent** tool) | * Code must be nicely and consistently indented (you may use the **indent** tool) | ||
- | * Code must utilize the algorithm(s) presented above: | + | * Code must utilize the algorithm(s) presented above. |
- | * **primebrutefork.c** must do the forked unoptimized brute force method | + | * **primeodds.c** |
- | * **primesqrtfork.c** must utilize the forked brute force method along with the square root trick (no other tricks) | + | * **primesqrt.c** |
+ | * **primesqrtodds.c** | ||
+ | * **primesqrtopt.c** | ||
+ | * **primesqrtoptodds.c** | ||
* Code must be commented | * Code must be commented | ||
* have a properly filled-out comment banner at the top | * have a properly filled-out comment banner at the top | ||
Line 403: | Line 326: | ||
<cli> | <cli> | ||
- | $ submit sysprog pnc1 primebrutefork.c primesqrtfork.c | + | $ submit sysprog pnc1 primeodds.c primesqrt.c primesqrtodds.c primesqrtopt.c primesqrtoptodds.c |
Submitting sysprog project " | Submitting sysprog project " | ||
- | -> primebrutefork.c(OK) | + | -> primeodds.c(OK) |
- | -> primesqrtfork.c(OK) | + | -> primesqrt.c(OK) |
+ | -> primesqrtodds.c(OK) | ||
+ | -> primesqrtopt.c(OK) | ||
+ | -> primesqrtoptodds.c(OK) | ||
SUCCESSFULLY SUBMITTED | SUCCESSFULLY SUBMITTED | ||
Line 412: | Line 338: | ||
You should get some sort of confirmation indicating successful submission if all went according to plan. If not, check for typos and or locational mismatches. | You should get some sort of confirmation indicating successful submission if all went according to plan. If not, check for typos and or locational mismatches. | ||
+ | |||
+ | What I will be looking for: | ||
+ | |||
+ | < | ||
+ | 78: | ||
+ | *: | ||
+ | *: | ||
+ | *: | ||
+ | *: | ||
+ | *: | ||
+ | *: | ||
+ | *: | ||
+ | *: | ||
+ | *: | ||
+ | *: | ||
+ | *: | ||
+ | *: | ||
+ | *: | ||
+ | *: | ||
+ | *: | ||
+ | *: | ||
+ | *: | ||
+ | *: | ||
+ | *: | ||
+ | *: | ||
+ | *: | ||
+ | *: | ||
+ | *: | ||
+ | *: | ||
+ | *: | ||
+ | *: | ||
+ | </ |