-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathflowfib.go
198 lines (152 loc) · 5.25 KB
/
flowfib.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
/*
Copyright (C) 2019 Thomas W. Young, [email protected]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file or its derivitaves except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
#flowfib.go
##Name
flowfib - computes Fibonacci numbers using a pipeline of goroutines
##Synopsys
flowfib [N:default=10 [R:default=1 [F:default=1]
will create and run N Go routines and N+1 channels. The extra channel allows main to communicate with the pipeline.
##Description
Flowfib was created to help understand and benchmark large Go pipeline
networks, goroutines, and channels. Flowfib helps find the limitations
of Go facilities and the hardware it runs on.
Flowfib builds a pipeline of re-entrant goroutines and channels.
The pipeline computes Fibonacci numbers, fib(N). [fib(0) = 0.]
Flowfib imports Go's "math/big" to handle large integers.
Each routine reads a dataflow(Information Packet) containing the last two
Fibonacci numbers from the previous routine's channel, then
computes the next Fibonacci number, and writes the updated dataflow
to the next channel.
Flowfib writes a flow containing the first two numbers(0,1) to the first
channel, then reads the result from the last channel and displays it.
This procedure will be run R(round) times. On each round,
F(number of flows)
will be injected into the pipeline. Setting F larger than one increases
parallelism on multi-core processors. Flowfib forces F to be no larger than
N (else deadlock would result). Increasing the number of rounds(R)
extends the running time without affecting the output. Flowfib ignores
all but the first pipeline result.
Flowfib does not require any special mutex's, signalling, semaphones,
threading etc. to safely coordinate processing,
just Go's builtin channels and goroutines.
##Timing Notes
Flowfib computes the more than 200,000 digits of fib(1000000)
in less than 7.2 seconds on a laptop running Ubuntu-18.04.2,
consuming about 14 seconds total cpu time on eight CPUs.
(NOTE: tested on Linux 64bit only.)
fib(3000000)
nearly maxes out16GB memory, running for about 48 seconds.
fib(4000000)
thrashes mightily, swapping out more than half of memory, finally
struggling through to a finish after 3 minutes and eleven seconds.
The lower order digits are 7091204718905974245428xxxxxx5.
x's mark some digits redacted to intrigue the reader. (Note that fib(10^N)
always ends in five.)
fib(300000) ran in less than 21 seconds on a Raspberry Pi(very nice cross compilation) (
1.3 seconds on the eight core laptop). fib(400000) never finished on the Pi.
##Example
$ ./flowfib 1000
Outputs:
fib(1000) = 43466557686937456435688527675040625802564660517371780402481729089536555417949051890403879840079255169295922593080322634775209689623239873322471161642996440906533187938298969649928516003704476137795166849228875
##Author
Tom Young, [email protected]
*/
package main
import (
"fmt"
"math/big"
"os"
"strconv"
)
const S int = 12
type Flow struct {
f0 big.Int
f1 big.Int
id int
}
// Number of goroutines and channels
var N int = 10 /* Number of goroutines (compute fib(N) ) */
var R int = 1 /* Number of rounds (compute fib(N) R*F times) */
var F int = 1 /* Number of flows injected by main into the pipeline
in each round */
// Install test string. ?? Fix this
var Foo string = "my test string"
func doit(i int, in chan Flow, out chan Flow) {
a := 1
for a == 1 {
f, _ := <-in
f0 := f.f0
f1 := f.f1
f0.Add(&f0, &f1) // Add f1 to f0
f.f0, f.f1 = f1, f0 // swap f0 and f1
f.id = i
out <- f
}
}
/* Set command line parameters
NOTE: init() is special -- runs first(before main).
*/
func init() {
a := len(os.Args)
if a > 1 {
N, _ = strconv.Atoi(os.Args[1]) // Number of goroutines
if a > 2 {
R, _ = strconv.Atoi(os.Args[2]) // Number of rounds
if a > 3 {
F, _ = strconv.Atoi(os.Args[3]) // Number of flows
}
}
}
if N < 1 { // 0 deadlocks.
N = 1000
}
if R < 1 {
R = 1
}
if F > N {
F = N
}
}
/* BUG(twy):
If N is too large (>5500000 ?), many will thrash
*/
func main() {
// Build a pipe line of N+1 channels passing the Flow struct
// ?? Change to pass the struct pointer??
p := make([]chan Flow, 0, N+1)
for i := 0; i <= N; i++ {
p = append(p, make(chan Flow))
}
for i := 0; i < N; i++ { // Invoke N goroutines
go doit(i+1, p[i], p[i+1])
}
for j := 0; j < R; j++ {
for i := 0; i < F; i++ {
f0 := new(Flow)
f0.f0 = *big.NewInt(0)
f0.f1 = *big.NewInt(1)
// Send Flow IP to first routine
p[0] <- *f0
}
for i := 0; i < F; i++ {
fl1, _ := <-p[N] // Get Flow IP from last routine
if j == 0 && i == 0 {
fmt.Printf("\nfib(%d) = ", fl1.id)
res := &fl1.f0
fmt.Println(res)
}
}
}
//fmt.Printf("%d routines processed %d flows in each of %d rounds.\n", N, F, R)
// panic("Show stack")
// ? Shutdown all channels & routines??
}