Parcourir la source

Init first part of hw6

Michael Mächtel il y a 8 ans
Parent
révision
20c0acb2fd
6 fichiers modifiés avec 442 ajouts et 0 suppressions
  1. 27
    0
      hw6/README.md
  2. 26
    0
      hw6/simu1/QUESTIONS.md
  3. 126
    0
      hw6/simu1/README-lottery.md
  4. 119
    0
      hw6/simu1/lottery.py
  5. 72
    0
      hw6/task1/README.md
  6. 72
    0
      hw6/task1/tests/output.bats

+ 27
- 0
hw6/README.md Voir le fichier

@@ -0,0 +1,27 @@
1
+# hw6
2
+
3
+## Tasks
4
+
5
+To fulfill **hw6** you have to solve:
6
+
7
+- task1
8
+- task2
9
+- simu1
10
+
11
+## Files
12
+
13
+You find already or reuse some files for rust tasks. Please remember to use cargo to create a new project for each task and copy the given files into their dedicated directories.
14
+
15
+## Pull-Request
16
+
17
+Please merge any accepted reviews into your branch. If you are ready with the homework, all tests run, please create a pull request named **hw6**.
18
+
19
+## Credits for hw6
20
+
21
+| Task     | max. Credits | Comment |
22
+| -------- | ------------ | ------- |
23
+| task1    | 1            |         |
24
+| task2    | 2            |         |
25
+| simu1    | 1            |         |
26
+| Deadline | +1           |         |
27
+| =        | 5            |         |

+ 26
- 0
hw6/simu1/QUESTIONS.md Voir le fichier

@@ -0,0 +1,26 @@
1
+# QUESTIONS: 9-Scheduling-Lottery
2
+
3
+This program, **lottery.py**, allows you to see how a lottery scheduler works.
4
+See the README for details.
5
+
6
+## Warmup
7
+
8
+1. Compute the solutions for simulations with 3 jobs and random seeds of 1, 2,
9
+   and 3.
10
+
11
+## Questions
12
+
13
+1. Now run with two specific jobs: each of length 10, but one (job 0) with just
14
+   1 ticket and the other (job 1) with 100 (e.g., `-l 10:1,10:100`). 
15
+   - What happens when the number of tickets is so imbalanced? 
16
+   - Will job 0 ever run before job 1 completes? How often? 
17
+   - In general, what does such a ticket imbalance do to the behavior of lottery
18
+     scheduling?
19
+1. When running with two jobs of length 100 and equal ticket allocations of 100
20
+   (`-l 100:100,100:100`), how unfair is the scheduler? Run with some different
21
+   random seeds to determine the (probabilistic) answer; let unfairness be
22
+   determined by how much earlier one job finishes than the other.
23
+1. How does your answer to the previous question change as the quantum size
24
+   (`-q`) gets larger?
25
+1. Can you make a version of the graph that is found in the chapter? What else
26
+   would be worth exploring? How would the graph look with a stride scheduler?

+ 126
- 0
hw6/simu1/README-lottery.md Voir le fichier

@@ -0,0 +1,126 @@
1
+# README Scheduler: Lottery
2
+
3
+This program, lottery.py, allows you to see how a lottery scheduler works. As
4
+always, there are two steps to running the program. First, run without the -c
5
+flag: this shows you what problem to solve without revealing the answers. 
6
+
7
+```text
8
+prompt> ./lottery.py -j 2 -s 0
9
+...
10
+Here is the job list, with the run time of each job: 
11
+  Job 0 ( length = 8, tickets = 75 )
12
+  Job 1 ( length = 4, tickets = 25 )
13
+
14
+Here is the set of random numbers you will need (at most):
15
+Random 511275
16
+Random 404934
17
+Random 783799
18
+Random 303313
19
+Random 476597
20
+Random 583382
21
+Random 908113
22
+Random 504687
23
+Random 281838
24
+Random 755804
25
+Random 618369
26
+Random 250506
27
+```
28
+
29
+When you run the simulator in this manner, it first assigns you some random jobs
30
+(here of lengths 8, and 4), each with some number of tickets (here 75 and 25,
31
+respectively). The simulator also gives you a list of random numbers, which you
32
+will need to determine what the lottery scheduler will do. The random numbers
33
+are chosen to be between 0 and a large number; thus, you'll have to use the
34
+modulo operator to compute the lottery winner (i.e., winner should equal this
35
+random number modulo the total number of tickets in the system). 
36
+
37
+Running with -c shows exactly what you are supposed to calculate:
38
+
39
+```text
40
+prompt> ./lottery.py -j 2 -s 0 -c
41
+...
42
+** Solutions **
43
+Random 511275 -> Winning ticket 75 (of 100) -> Run 1
44
+  Jobs:  (  job:0 timeleft:8 tix:75 ) (* job:1 timeleft:4 tix:25 )
45
+Random 404934 -> Winning ticket 34 (of 100) -> Run 0
46
+  Jobs:  (* job:0 timeleft:8 tix:75 ) (  job:1 timeleft:3 tix:25 )
47
+Random 783799 -> Winning ticket 99 (of 100) -> Run 1
48
+  Jobs:  (  job:0 timeleft:7 tix:75 ) (* job:1 timeleft:3 tix:25 )
49
+Random 303313 -> Winning ticket 13 (of 100) -> Run 0
50
+  Jobs:  (* job:0 timeleft:7 tix:75 ) (  job:1 timeleft:2 tix:25 )
51
+Random 476597 -> Winning ticket 97 (of 100) -> Run 1
52
+  Jobs:  (  job:0 timeleft:6 tix:75 ) (* job:1 timeleft:2 tix:25 )
53
+Random 583382 -> Winning ticket 82 (of 100) -> Run 1
54
+  Jobs:  (  job:0 timeleft:6 tix:75 ) (* job:1 timeleft:1 tix:25 )
55
+--> JOB 1 DONE at time 6
56
+Random 908113 -> Winning ticket 13 (of 75) -> Run 0
57
+  Jobs:  (* job:0 timeleft:6 tix:75 ) (  job:1 timeleft:0 tix:--- )
58
+Random 504687 -> Winning ticket 12 (of 75) -> Run 0
59
+  Jobs:  (* job:0 timeleft:5 tix:75 ) (  job:1 timeleft:0 tix:--- )
60
+Random 281838 -> Winning ticket 63 (of 75) -> Run 0
61
+  Jobs:  (* job:0 timeleft:4 tix:75 ) (  job:1 timeleft:0 tix:--- )
62
+Random 755804 -> Winning ticket 29 (of 75) -> Run 0
63
+  Jobs:  (* job:0 timeleft:3 tix:75 ) (  job:1 timeleft:0 tix:--- )
64
+Random 618369 -> Winning ticket 69 (of 75) -> Run 0
65
+  Jobs:  (* job:0 timeleft:2 tix:75 ) (  job:1 timeleft:0 tix:--- )
66
+Random 250506 -> Winning ticket 6 (of 75) -> Run 0
67
+  Jobs:  (* job:0 timeleft:1 tix:75 ) (  job:1 timeleft:0 tix:--- )
68
+--> JOB 0 DONE at time 12
69
+```
70
+
71
+As you can see from this trace, what you are supposed to do is use the random
72
+number to figure out which ticket is the winner. Then, given the winning ticket,
73
+figure out which job should run. Repeat this until all of the jobs are finished
74
+running. It's as simple as that -- you are just emulating what the lottery
75
+scheduler does, but by hand!
76
+
77
+Just to make this absolutely clear, let's look at the first decision made in the
78
+example above. At this point, we have two jobs (job 0 which has a runtime of 8
79
+and 75 tickets, and job 1 which has a runtime of 4 and 25 tickets). The first
80
+random number we are given is 511275. As there are 100 tickets in the system,
81
+511275 \% 100 is 75, and thus 75 is our winning ticket.
82
+
83
+If ticket 75 is the winner, we simply search through the job list until we find
84
+it. The first entry, for job 0, has 75 tickets (0 through 74), and thus does not
85
+win; the next entry is for job 1, and thus we have found our winner, so we run
86
+job 1 for the quantum length (1 in this example). All of this is shown in the
87
+print out as follows:
88
+
89
+```text
90
+Random 511275 -> Winning ticket 75 (of 100) -> Run 1
91
+  Jobs:  (  job:0 timeleft:8 tix:75 ) (* job:1 timeleft:4 tix:25 )
92
+```
93
+
94
+As you can see, the first line summarizes what happens, and the second simply
95
+shows the entire job queue, with an * denoting which job was chosen.
96
+
97
+The simulator has a few other options, most of which should be self-explanatory.
98
+Most notably, the -l/--jlist flag can be used to specify an exact set of jobs
99
+and their ticket values, instead of always using randomly-generated job lists.
100
+
101
+```text
102
+prompt> ./lottery.py -h
103
+Usage: lottery.py [options]
104
+
105
+Options:
106
+  -h, --help            
107
+      show this help message and exit
108
+  -s SEED, --seed=SEED  
109
+      the random seed
110
+  -j JOBS, --jobs=JOBS  
111
+      number of jobs in the system
112
+  -l JLIST, --jlist=JLIST
113
+      instead of random jobs, provide a comma-separated list
114
+      of run times and ticket values (e.g., 10:100,20:100
115
+      would have two jobs with run-times of 10 and 20, each
116
+      with 100 tickets)
117
+  -m MAXLEN, --maxlen=MAXLEN
118
+      max length of job
119
+  -T MAXTICKET, --maxtick=MAXTICKET
120
+      maximum ticket value, if randomly assigned
121
+  -q QUANTUM, --quantum=QUANTUM
122
+      length of time slice
123
+  -c, --compute
124
+      compute answers for me
125
+
126
+```

+ 119
- 0
hw6/simu1/lottery.py Voir le fichier

@@ -0,0 +1,119 @@
1
+#! /usr/bin/env python
2
+
3
+import sys
4
+from optparse import OptionParser
5
+import random
6
+
7
+parser = OptionParser()
8
+parser.add_option('-s', '--seed', default=0, help='the random seed',              action='store', type='int', dest='seed')
9
+parser.add_option('-j', '--jobs', default=3, help='number of jobs in the system', action='store', type='int', dest='jobs')
10
+parser.add_option('-l', '--jlist', default='', help='instead of random jobs, provide a comma-separated list of run times and ticket values (e.g., 10:100,20:100 would have two jobs with run-times of 10 and 20, each with 100 tickets)',  action='store', type='string', dest='jlist')
11
+parser.add_option('-m', '--maxlen',  default=10,  help='max length of job',         action='store', type='int', dest='maxlen')
12
+parser.add_option('-T', '--maxticket', default=100, help='maximum ticket value, if randomly assigned',          action='store', type='int', dest='maxticket')
13
+parser.add_option('-q', '--quantum', default=1,   help='length of time slice', action='store', type='int', dest='quantum')
14
+parser.add_option('-c', '--compute', help='compute answers for me', action='store_true', default=False, dest='solve')
15
+
16
+(options, args) = parser.parse_args()
17
+
18
+random.seed(options.seed)
19
+
20
+print 'ARG jlist', options.jlist
21
+print 'ARG jobs', options.jobs
22
+print 'ARG maxlen', options.maxlen
23
+print 'ARG maxticket', options.maxticket
24
+print 'ARG quantum', options.quantum
25
+print 'ARG seed', options.seed
26
+print ''
27
+
28
+print 'Here is the job list, with the run time of each job: '
29
+
30
+import operator
31
+
32
+
33
+tickTotal = 0
34
+runTotal  = 0
35
+joblist = []
36
+if options.jlist == '':
37
+    for jobnum in range(0,options.jobs):
38
+        runtime = int(options.maxlen * random.random())
39
+        tickets = int(options.maxticket * random.random())
40
+        runTotal += runtime
41
+        tickTotal += tickets
42
+        joblist.append([jobnum, runtime, tickets])
43
+        print '  Job %d ( length = %d, tickets = %d )' % (jobnum, runtime, tickets)
44
+else:
45
+    jobnum = 0
46
+    for entry in options.jlist.split(','):
47
+        (runtime, tickets) = entry.split(':')
48
+        joblist.append([jobnum, int(runtime), int(tickets)])
49
+        runTotal += int(runtime)
50
+        tickTotal += int(tickets)
51
+        jobnum += 1
52
+    for job in joblist:
53
+        print '  Job %d ( length = %d, tickets = %d )' % (job[0], job[1], job[2])
54
+print '\n'
55
+
56
+if options.solve == False:
57
+    print 'Here is the set of random numbers you will need (at most):'
58
+    for i in range(runTotal):
59
+        r = int(random.random() * 1000001)
60
+        print 'Random', r
61
+
62
+if options.solve == True:
63
+    print '** Solutions **\n'
64
+
65
+    jobs  = len(joblist)
66
+    clock = 0
67
+    for i in range(runTotal):
68
+        r = int(random.random() * 1000001)
69
+        winner = int(r % tickTotal)
70
+
71
+        current = 0
72
+        for (job, runtime, tickets) in joblist:
73
+            current += tickets
74
+            if current > winner:
75
+                (wjob, wrun, wtix) = (job, runtime, tickets)
76
+                break
77
+
78
+        print 'Random', r, '-> Winning ticket %d (of %d) -> Run %d' % (winner, tickTotal, wjob)
79
+        # print 'Winning ticket %d (of %d) -> Run %d' % (winner, tickTotal, wjob)
80
+
81
+        print '  Jobs:',
82
+        for (job, runtime, tickets) in joblist:
83
+            if wjob == job:
84
+                wstr = '*'
85
+            else:
86
+                wstr = ' '
87
+
88
+            if runtime > 0:
89
+                tstr = tickets
90
+            else:
91
+                tstr = '---'
92
+            print ' (%s job:%d timeleft:%d tix:%s ) ' % (wstr, job, runtime, tstr), 
93
+        print ''
94
+
95
+        # now do the accounting
96
+        if wrun >= options.quantum:
97
+            wrun -= options.quantum
98
+        else:
99
+            wrun = 0
100
+
101
+        clock += options.quantum
102
+
103
+        # job completed!
104
+        if wrun == 0:
105
+            print '--> JOB %d DONE at time %d' % (wjob, clock)
106
+            tickTotal -= wtix
107
+            wtix = 0
108
+            jobs -= 1
109
+
110
+        # update job list
111
+        joblist[wjob] = (wjob, wrun, wtix)
112
+
113
+        if jobs == 0:
114
+            print ''
115
+            break
116
+
117
+
118
+
119
+

+ 72
- 0
hw6/task1/README.md Voir le fichier

@@ -0,0 +1,72 @@
1
+# README hw6-t1
2
+
3
+Schreiben Sie ein Programm, welches 2 Child's erzeugt. Der Parent übergibt an die beiden Childs über eine Pipe die Daten als Strings, die als Parameter beim Aufruf mit angegeben werden.
4
+
5
+Bei Aufruf des Programms geben Sie eine Zahlensequenz (`i32`) von mindestens 2 Zahlen an. Diese Zahlensequenz übergibt dann der Parent an die beiden Childs. Die Zahlen sind alle vom Typ `i32`.
6
+
7
+Werden zuwenig Parameter angegeben, so soll eine(!) Zeile 'Hilfe' ausgegeben werden, wie z.B.:
8
+
9
+```text
10
+Correct usage: number number <number> ...
11
+```
12
+
13
+> Wichtig: Nur eine Zeile 'Hilfe' ausgeben.
14
+
15
+Child1 berechnet aus der Zahlensequenz die Summe, Child2 das Produkt. Child1 und Child2 geben dann das Ergebnis auf der Konsole direkt aus.
16
+
17
+```text
18
+$ ./task1 -1 2 4 6 19 -100
19
+sending to childs: -1 2 4 6 19 -100
20
+Sum = -70
21
+Mul = 91200
22
+```
23
+
24
+> Format der Ausgabe genau einhalten. Das bedeutet, die Ausgabe des Childs mit der Summenberechnung muss immer VOR der Ausgabe des Childs mit der Multiplikation erfolgen. Welchen einfachen, allerdings Laufzeit 'schädlichen' Trick können Sie dafür benutzen, den Sie bereits kennen? Wie lässt sich der Parameter dafür tunen, um eine optimale Laufzeit (kurz!) des Programms zu erreichen. Kennen Sie eine Möglichkeit, ohne diesen Trick eine eindeutige Reihenfolge der Childs vorzugeben (muss nicht implementiert werden)?
25
+
26
+Werten Sie den Status der Childs im Elternprozess aus und beenden Sie das Programm nur im Erfolgsfall beider Childs mit dem Exit-Code 0. Erfolgsfall bedeutet dabei, dass der Child beendet wurde und den Exit Code 0 gesendet hat. Treten Fehler im Child auf, so sendet der Child z.B. den exit Code 1, so dass der Elternprozess dies auswerten kann und das Programm (Parent) ebenfalls mit exit Code 1 beendet.
27
+
28
+>Tip: waitpid() sollte dazu entsprechend ausgewertet werden.
29
+
30
+Beim Aufruf über cargo die '--' nicht vergessen, siehe dazu  **cargo help run**.
31
+
32
+Die Daten zwischen Eltern und Kindern werden als Byte-Stream gesendet. Achten Sie auf die Größe des Puffers, den die Childs zum Empfangen anlegen. Definieren Sie für diese Größe eine `const` und setzen Sie diese auf 256.
33
+
34
+Da die eingelesenen Argumente bereits als Strings vorliegen, bietet es sich an im Programm intern mit Strings zu arbeiten. Damit ergeben sich folgende Hilfsfunktionen:
35
+
36
+- `concatenate_strings()` :
37
+- `split_into_strings()`:
38
+- `sum_strings()`:
39
+- `mul_strings()`:
40
+
41
+> Achten Sie auf einen geeignet grossen Rückgabewert im Erfolgsfall, insbesondere in der Funktion, die die Multiplikation ausführt (`mul_strings()`).
42
+
43
+> Achten Sie aber bei Ihren Berechnungen auch darauf, das kein Overflow auftreten kann, der das Programm abbricht (siehe Rust Standarddoku: `overflowing`). Wenn einen Berechnung aufgrund eines auftretenden Overflows nicht durchgeführt werden kann, so gibt der Child einen Fehler aus und terminiert - wie immer im Fehlerfall -  mit dem Exitcode '1'. Die Ausgabe des Programms könnte dann z.B. folgendermassen aussehen:
44
+
45
+```text
46
+> cargo run -- 1000 1000 1000 1000 1000 1000 1000
47
+...
48
+Sum = 7000
49
+... Overflow would happen in mul_strings()
50
+...
51
+```
52
+
53
+> ... steht für mögliche andere Ausgaben
54
+
55
+Darüber hinaus sollten Sie bei Code-Wiederholungen prinzipiell immer überlegen, welche weiteren Hilfsfunktionen sich dadurch anbieten.
56
+
57
+## Externe Crates
58
+
59
+Benutzen Sie für Ihre Implementierung nur die externe Crate `nix`.
60
+
61
+## Module und Tests
62
+
63
+Ob und wie Sie den Code in Module aufteilen wollen ist Ihnen überlassen. Schreiben Sie jedoch Ihre Unit-Tests in der Datei `unit_test_pipe.rs` oder als eigenständigen Test, der von 'cargo test' aufgerufen wird, siehe auch [Testing][]. Einfache Tests können auch direkt in die Dokumentation 'codiert' werden, siehe [Documentation Tests][]
64
+
65
+Achten Sie beim Exit Code des Elternprozesses darauf, dass dieser nur 0 ist, wenn BEIDE Childs jeweils einen exit Code 0 zurücksenden.
66
+
67
+## Dokumentation
68
+
69
+Es ist ausreichend, wenn Sie Ihre Dokumentation im Code soweit ergänzen, dass dieser nachvollziehbar ist. Eine Dokumentation über `cargo doc` muss nicht erstellt werden.
70
+
71
+[Testing]: https://doc.rust-lang.org/book/testing.html
72
+[Documentation Tests]: https://doc.rust-lang.org/book/testing.html#documentation-tests

+ 72
- 0
hw6/task1/tests/output.bats Voir le fichier

@@ -0,0 +1,72 @@
1
+#!/usr/bin/env bats
2
+
3
+
4
+@test "task1: Check that we have a debug output" {
5
+    run stat "$BATS_TEST_DIRNAME/../target/debug/task1"
6
+    [ "$status" -eq 0 ]
7
+}
8
+
9
+# Check lines of output
10
+
11
+
12
+# wc output with white spaces is trimmed by xargs
13
+@test "task1: Output with to no paras must be exact 1 line long" {
14
+    run bash -c "'$BATS_TEST_DIRNAME/../target/debug/task1' 1 | wc -l | xargs"
15
+    [ "$output" = "1" ]
16
+
17
+}
18
+
19
+# wc output with white spaces is trimmed by xargs
20
+@test "task1: Output with one para must be exact 1 line long" {
21
+    run bash -c "'$BATS_TEST_DIRNAME/../target/debug/task1' -1 | wc -l | xargs"
22
+    [ "$output" = "1" ]
23
+
24
+}
25
+
26
+
27
+# wc output with white spaces is trimmed by xargs
28
+@test "task1: Output with correct para must be exact 3 line long" {
29
+    run bash -c "'$BATS_TEST_DIRNAME/../target/debug/task1' 2 3 4 5 6 7 8 9 | wc -l | xargs"
30
+    [ "$output" = "3" ]
31
+}
32
+
33
+
34
+# Check results
35
+
36
+@test "task1: 1 -2 3 -4 5" {
37
+    run "$BATS_TEST_DIRNAME/../target/debug/task1" 1 -2 3 -4 5
38
+    [[ "${lines[0]}" =~ "sending to childs: 1 -2 3 -4 5" ]]
39
+    [[ "${lines[1]}" =~ "Sum = 3" ]]
40
+    [[ "${lines[2]}" =~ "Mul = 120" ]]
41
+}
42
+
43
+@test "task1: 1 -2 3 -4 5 100 -400 5332 3290 -22 -4646 -1 -1" {
44
+    run "$BATS_TEST_DIRNAME/../target/debug/task1" 1 -2 3 -4 5 100 -400 5332 3290 -22 -4646 -1 -1
45
+    [[ "${lines[0]}" =~ "sending to childs: 1 -2 3 -4 5 100 -400 5332 3290 -22 -4646 -1 -1" ]]
46
+    [[ "${lines[1]}" =~ "Sum = 3655" ]]
47
+    [[ "${lines[2]}" =~ "Mul = -8606551312128000000" ]]
48
+}
49
+
50
+@test "task1: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20" {
51
+    run "$BATS_TEST_DIRNAME/../target/debug/task1" 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
52
+    [[ "${lines[0]}" =~ "sending to childs: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20" ]]
53
+    [[ "${lines[1]}" =~ "Sum = 210" ]]
54
+    [[ "${lines[2]}" =~ "Mul = 2432902008176640000" ]]
55
+}
56
+
57
+# Status checks
58
+@test "task1: Output with wrong args (1) does not crash" {
59
+    run bash -c "'$BATS_TEST_DIRNAME/../target/debug/task1' a b c "
60
+    [ "$status" = 1 ]
61
+}
62
+
63
+# Status checks
64
+@test "task1: Output with wrong args (2) does not crash" {
65
+    run bash -c "'$BATS_TEST_DIRNAME/../target/debug/task1' 1 - 2 "
66
+    [ "$status" = 1 ]
67
+}
68
+
69
+@test "task1: Overflow Output does not crash" {
70
+    run bash -c "'$BATS_TEST_DIRNAME/../target/debug/task1' 10000 10000 10000 10000 10000"
71
+    [ "$status" = 1 ]
72
+}

Chargement…
Annuler
Enregistrer