konkurencia vs. paralelizmus
DESCRIPTION
Konkurencia vs. paralelizmus. k ompo zícia nezávislých výpočtov spôsob myslenia, ako výpočet (pr ácu ) rozdeliť medzi nezávislých agentov keďže okolitý svet je paralelný, je to spôsob ako lepšie interagovať s ním málo kto z nás má skutočne paralelný HW, max. tak 2-,4-, 8-jadro... - PowerPoint PPT PresentationTRANSCRIPT
Konkurentné GoPeter Borovanský, KAI, I-18, borovan(a)ii.fmph.uniba.sk
Dnes bude: štruktúry a metódy implicitný interface a typovanie konkurencia
Štruktúry a smerníkyProgram, ktorý generuje fibonacciho strom definovaný štruktúrou:type FibTree struct {
left *FibTree
right *FibTree
}
func generate(n int) *FibTree {
if n < 2 {
return nil
} else {
return &FibTree{generate(n - 1), generate(n - 2)}
alebo return &FibTree{left:generate(n-1),right:generate(n-2)}
alebo bt := new(FibTree) // alokuje krabicu pre FibTree
bt.left = generate(n - 1)
bt.right = generate(n - 2)
return bt } } BinTree.go
MetódyNie je žiadne this, ani self, ako v iných jazykoch.
Parameter označujúci objekt, na ktorý sa metóda aplikuje, je explicitne prítomný v jej hlavičke, a to v zátvorkách pred menom metódy
func (bt *FibTree) size() int {
if bt == nil { // metódu možno aplikovať na nil !!!
return 1
} else {
return bt.left.size() + bt.right.size()
}
} size = 1 nilsize = 1 nilsize = 2 <nil,nil>size = 3 <<nil,nil>,nil>size = 5 <<<nil,nil>,nil>,<nil,nil>>size = 8 <<<<nil,nil>,nil>,<nil,nil>>,<<nil,nil>,nil>>size = 13 <<<<<nil,nil>,nil>,<nil,nil>>,<<nil,nil>,nil>>,<<<nil,nil>,nil>,<nil,nil>>> BinTree.go
Fibonacci a matice(z minulej prednášky)
package main
type Matrix struct { // struct ako v C++
a11, a12, a21, a22 int} // reprezentácia matice 2x2
func (m *Matrix) multiply(n *Matrix) *Matrix {
var c = &Matrix{ // konštruktor struct
m.a11*n.a11 + m.a12*n.a21, // násobenie matíc 2x2,
m.a11*n.a12 + m.a12*n.a22, // hardcode-žiadne cykly
m.a21*n.a11 + m.a22*n.a21,
m.a21*n.a12 + m.a22*n.a22}
return c // vráti pointer na maticu
} // teda Go má pointre, operátory &, * skoro ako C++
func FibMatrix(n int) int {
m := &Matrix{a11: 1, a12: 1, a21: 1, a22: 0}
p := m.power(n)
return p.a12}
fibMatrix.go
Logaritmický power(z minulej prednášky)
A už len to nezabiť tým, že power(), alias mocninu urobíme lineárnu...
Lepšia bude logaritmická verzia
func (m *Matrix) power(n int) *Matrix {
if n == 1 {
return m
} else if n%2 == 0 { // mn = (mn/2)2, pre n párne
m2 := m.power(n / 2)
return m2.multiply(m2)
} else { // mn = m*(mn-1), pre n nepárne
return m.power(n - 1).multiply(m)
}}
fibMatrix.go
Logaritmický power(z cvičení)
type realnaFunckia /*=*/ func(float64) float64
func power(n int, f realnaFunckia) realnaFunckia {
if n == 0 {
return func(x float64) float64 { return x }
} else if n%2 == 0 {
// return kompozicia(power(n / 2, f), power(n / 2, f) )
return func(x float64) float64 {
rf := power(n / 2, f) // rf : realnaFunckia
return rf(rf(x)) // fn = (fn/2) o(fn/2),pre n párne
}
} else { // fn = f o (fn-1), pre n nepárne
// return kompozicia(f, power(n-1, f) )
rf := power(n - 1, f) // rf : realnaFunckia
return func(x float64) float64 { return f(rf(x)) }
}
} funkcionaly.go
Logaritmický power(z cvičení)
type realnaFunckia /*=*/ func(float64) float64
func power(n int, f realnaFunckia) realnaFunckia {
if n == 0 {
return func(x float64) float64 { return x }
} else if n%2 == 0 {
rf := power(n / 2, f)
return kompozicia(rf, rf )
} else {
return kompozicia(f, power(n-1, f) )
}
}
funkcionaly.go
IdentifiedPet - Dogtype IdetifiedPet interface { // interface popisuje zoznam
getName() string } // metód, ktoré implementátor
// musí spĺňať
type Dog struct {
name string
color int }
// Go nepozná pojem default-konštruktora
func NewDog(name string) *Dog { // ľub.meno konštruktora
d := new(Dog) // toto realne nalokuje pamäť
d.name = name
d.color = 0 // nejaká default-farba
return d } // return nový objekt
func (d Dog) getName() string { return d.name }
func (d Dog) hello() string { return d.name + ": haf-haf" } duck.go
Je už Dog IdentifiedPet ?type Cat struct {
name string
size int }
func (c Cat) getName() string { return c.name }
func (c Cat) hello() string { return c.name + ": mnau-mnau" }
Je…, veď už aj Cat je, lebo oba definujú predpísanú metódu getName() string
To, či typ spĺňa/nespĺňa nejaký interface, nikde nedeklarujeme, je to implicitne určené z jeho definície - na rozdiel od Javy, kde trieda implements interface
type FriendlyPet interface {
hello() string // vie odpovedať .hello()
}
func hello(z FriendlyPet) string { // tak nech povie hello()
return z.hello() } duck.go
Kto je FriendlyPet ?Dog aj Cat…
Definujme jeden objekt, ktorý nie je FriendlyPet, ale je IdentifiedPet
type Mouse struct { // unfriendly mouse
name string }
func (m Mouse) getName() string {
return m.name }
Definujme interface obsahovo ekvivalentný s IdentifiedPet, ale s iným menom
type PetWithName interface {
name string // nejde v interface predpísať aké
položky objekt má mať, ale len aké metódy
getName() string } duck.go
Malá Zoofunc main() {
doggie := Dog{} // prázdny objekt Dog
doggie := NewDog("Pluto") // objekt Dog s menom Pluto
cattie := Cat{2} // too few values in struct initializer
cattie := Cat{"Tom", 2}
cattie := Cat{size: 2, name: "Tom"}
cattie := Cat{name: "Tom"}
var zoo []FriendlyPet zoo = make([]FriendlyPet, 2)
zoo[0] = doggie
zoo[1] = cattie
zoo := []FriendlyPet{doggie, cattie} zoo := []FriendlyPet{doggie, cattie, Mouse{name: "Jerry"}}
cannot use Mouse literal (type Mouse) as type FriendlyPet
in array element // Jerry is not friendly duck.go
for _, z := range zoo { // zoo::[]FriendlyPet
fmt.Println(z.hello() + "," + hello(z))
} var fz PetWithName
fz = doggie // je doggie PetWithName ?
fz = cattie // je cattie PetWithName ?
fz = Mouse{name: "Jerry“} // je “Jerry” PetWithName ?
fmt.Println(fz.getName()) // zdá sa, že
IdentifiedPet == PetWithName, štrukturálna rovnosť typov
var iz IdetifiedPet
iz = fz // ???
fmt.Println(iz.getName()) }
IdentifiedPet =?= PetWithName
Pluto: haf-haf,Pluto: haf-hafTom: mnau-mnau,Tom: mnau-mnauJerryJerry
duck.go
Duck typing (prevzaté z prednášky Python)
je forma dynamického typovania, dynamická náhrada virtuálnych metód
class pes(): # definujeme dve triedy bez akejkoľvek defičnosti def zvuk(self): # obe definujú metódu zvuk() return "haw-haw" class macka(): def zvuk(self): return "mnau-mnau"
def zvuk(zviera): # otázkou (statického programátora) je, akého typu je
print(zviera.zvuk()) # premenná zviera, keď na ňu aplikujeme .zvuk()
# odpoveď: uvidí sa v RT podľa hodnoty premennejfarma = [pes(), macka()] # heterogénny zoznam objektov
for zviera in farma: zvuk(zviera)
If it looks like a duck and quacks like a duck, it must be a duck
haw-hawmnau-mnau
pes macka
Ako by to pascalista s dedičnosťouclass animal(): # nadtrieda def zvuk(self): # náznak virtuálnej metódy return "no sound" # neviem aký zvuk, resp. pass
class dog(animal): # dog ako podtrieda animal def zvuk(self): # override metódy zvuk z animal
return "haw-haw" class cat(animal): # cat ako podtrieda animal def zvuk(self): # override metódy zvuk z animal return "mnau-mnau"
class cow(animal): # cow ako podtrieda animal pass # nemá vlastnú metódu zvuk
# pass znamená prázdny príkazfor animal in [dog(), cat(), cow()] : print(animal.zvuk())
haw-hawmnau-mnauno sound
dog cat cow
animal
Konkurencia
http://talks.golang.org/2012/waza.slide#1
kompozícia nezávislých výpočtov spôsob myslenia, ako výpočet (prácu) rozdeliť medzi nezávislých agentov keďže okolitý svet je paralelný, je to spôsob ako lepšie interagovať s ním málo kto z nás má skutočne paralelný HW, max. tak 2-,4-, 8-jadro...
na jednom procesore paralelizmus neurobíte, ale konkurentný výpočet ánoale ... konkurentný výpočet na jednom procesore bude pravdepodobne pomalší ako
sekvenčná verzia, takže viac ide o konkurentnú paradigmu ako o čas
Go konkurencia založená na CommunicatingSequentialProcesses (T. Hoare, 1978) poskytuje: konkurentné procedúry (tzv. gorutiny) synchronizáciu a komunikáciu prostredníctvom kanálov príkaz select
http://www.youtube.com/watch?v=f6kdp27TYZs
Konkurencia vs. paralelizmus
Gorutina - príkladGorutina loopForever sa vykonáva ako funkcia loopForever len sa
nečaká na jej výsledok, resp. skončenie
package main
import ( "fmt" "math/rand" "time")
func loopForever(task string) {
for i := 1; ; i++ { // počítame do nekonečna
fmt.Printf("%s:%d\n", task, i)
time.Sleep(time.Duration(rand.Intn(500)) * time.Millisecond) }}
func main() {
go loopForever("prvy") // spustenie 1.gorutiny
go loopForever("druhy") // spustenie 2.gorutiny
var input string // toto čaká na input, v opačnom
fmt.Scanln(&input) // prípade, keď umrie hlavné vlákno
fmt.Println("main stop")} // umrie v Go všetko... concurrent1.go
GorutinaGorutina je nezávisle vykonávaná funkcia má vlastný stack, ktorý rastie/scvrkáva sa podľa jej potrieb môže ich byť veľa, aj veľmi veľa (uvidíme ~ 100.000) je to menej ako vlákno (thread), ale k nemu to má najbližšie
Anonymná gorutina je de-facto bezmenná funkcia, ktorú aj hneď zavoláme:
func main() {
go func /*tu chýba meno fcie*/ (task string) {
for i := 1; ; i++ { // počítame do nekonečna
fmt.Printf("%s:%d\n", task, i)
time.Sleep(...)
}
} ("prvy") // tu hneď voláme anonymnú fciu s argumentom
Pomocou kanálov (nebuffrovaná verzia):var ch chan int resp. ch := make(chan int)
ch = make(chan int)
zápis do kanála je blokujúca operácia, kým hodnotu niekto neprečíta z kanálach <- 123
čítanie z kanála je blokujúca operácia, až kým hodnotu niekto nezapíše do kanála
x = <-ch
takže ide o komunikáciu (prenos dát), ale aj o synchronizáciu.
V prípade buffrovaných kanálov make(chan int, 10) prídeme o synchronizáciu, tažke to skúsime neskôr...
Komunikácia a synchronizácia
Dvaja píšu, jeden čítafunc loopAndSend(task string, ch chan string) {
for i := 1; i < 100; i++ {
ch <- fmt.Sprintf("%s:%d\n", task, i)
time.Sleep(time.Duration(rand.Intn(500)) * time.Millisecond)
}}
func main() { // dve gorutiny píšu do
ch := make(chan string) // toho istého kanála ch
go loopAndSend("prvy", ch)
go loopAndSend("druhy", ch)
for { // tu to čítame for msg := range ch {
msg := <-ch // range prebieha obsahom
// celého kanála
fmt.Print(msg) fmt.Print(msg)
} }
fmt.Println("main stop") } // nikdy neskončí, prečo ?
concurrent2.go
Funkcia vráti kanálchan string je typ kanála stringov, funkcia ho môže vrátiť ako výsledok
func loopToChannel(task string) chan string {
ch := make(chan string) // vytvor kanál
go func() { // pusti nezávislú gorutinu
for i := 1; i < 100; i++ { // ktorá píše do kanála
ch <- fmt.Sprintf("%s:%d\n", task, i)
time.Sleep(...) }
}() // argumenty anonymnej funkcie
return ch } // vráť kanál ch:chan string
func main() {
ch1 := loopToChannel("prvy")
ch2 := loopToChannel("druhy")
for {
fmt.Print(<-ch1) // čo dostaneme ???
fmt.Print(<-ch2) // chápeme už synchronizáciu ??
}concurrent3.go
Kanálový sútokfunc multiplexor(ch1, ch2 chan string) chan string {
ch := make(chan string)
go func() { // prvá gorutina
for { ch <- <-ch1 } // čítaj z ch1 a píš to do ch
}()
go func() { // druhá gorutina
for { ch <- <-ch2 } // čítaj z ch2 a píš to do ch
}()
return ch}
func main() {
ch1 := loopToChannel("prvy") // tretia gorutina
ch2 := loopToChannel("druhy") // štvrtá gorutina
ch := multiplexor(ch1, ch2)
for {
fmt.Print(<-ch) }
concurrent3.go
Selectselect je príkaz syntaxou podobný switch, à la java
func multiplexorSelect(ch1, ch2 chan string) chan string {
ch := make(chan string)
go func() { // jednu gorutinu sme ušetrili :-)
for {
select { // select vykoná niektorý neblokovaný
case val := <-ch1: // komunikačný case-príkaz
ch <- val // ak niekto zapísal do ch1
case val := <-ch2: // číta sa z ch1, ak ch2,
ch <- val // tak z ch2, inak je blokovaný
}
} // select odpáli nejaká komunikačná udalosť
// (zápis/čítanie z/do kanála) v case príkazoch
}() // alebo timeout...
return ch }
concurrent3.go
Select a timeoutfunc multiplexorSelect(ch1, ch2 chan string) chan string {
ch := make(chan string)
go func() {
gameOver := time.After(10 * time.Second)
for {
select {
case val := <-ch1: ch <- val
case val := <-ch2: ch <- val
case <-gameOver:
ch <- "GAME OVER\n"
close(ch)
}
}
}()
return ch
} concurrent3.go
TimeoutgameOver := time.After(10*time.Second)
alebo vlastný kód
gameOver := make(chan bool)
go func(seconds int) {
time.Sleep(10 * time.Second)
gameOver <- true // timeout
}(10)
concurrent3.go
je kanál už zavretý ?for { fmt.Print( <-ch) }
zle skončí, ak close(ch)
for {
val, opened := <-ch
if !opened { break }
fmt.Print(val)
}
Producer-Consumerfunc producer(ch chan int) {
for i := 1; i <= 30; i++ {
ch <- i
fmt.Println("produce: " + strconv.Itoa(i)) //time.Sleep(time.Second) // lenivá produkcia
}}
func consumer(ch chan int) {
for i := 1; i <= 30; i++ {
fmt.Println("consume: ", <-ch) time.Sleep(time.Second) // lenivá spotreba
}}
func main() {
ch := make(chan int, 5) // buffrovaný kanál
go producer(ch)
go consumer(ch)
time.Sleep(100000000000)} // skoro večnosť producerconsumer.go
Čínsky šepkárivar number = 100000
func main() { start := time.Now()
prev := make(chan int)
first := prev // ľavé ucho (ľ.u.) nultého šepkára
go func() { first <- 0 }() // nultému šepneme 0 do ľ.u.
for i := 0; i < number; i++ { // 40000 číňanov
next := make(chan int) // kanál z p.u.i-teho
go func(from, to chan int) { // do ľ.u. i+1-vého
for { to <- 1 + <- from } // šepnem ďalej 1+čo
}(prev, next) // počujem
prev = next // pokračujem, i k i+1
} elapsed := time.Since(start)
fmt.Println(<-prev) fmt.Println(elapsed)}
chinees.go
Prvočísla(Eratosténovo sito)
prev := make(chan int)
first := prev
go func() {
for i := 2; ; i++ { first <- i } }() // do first sypeme 2,3,4, …
for i := 0; i < 10000; i++ { // čínski preosievači prvočísel
prime := <-prev // prvé preosiate musí byť prvočíslo
fmt.Println(prime)
next := make(chan int) // kanál pre ďalšieho preosievača
go func(prime int, from, to chan int) { // číta z from, píše do
for { // do to, vyčiarkne deliteľné prime
val := <-from // číta z from – vstupný kanál
if val%prime > 0 { // je deliteľné prime ?
to <- val // ak nie je, píš do to - výstupný
}
}
}(prime, prev, next) // spustenie nezávislého preosievača
prev = next // výsledok ide ďalšiemu osievačovi
}
chineprimes.go
Quicksort - pivotizáciaNekonkurentná pivotizácia, nepekné dvojité testy …
func pivot(pole []int) int {
i, j, pivot := 1, len(pole)-1, pole[0]
for i <= j {
// hľadanie maxiputána medzi liliputánmi
for i <= j && pole[i] <= pivot{ i++ }
// hľadanie liliputána medzi maxiputánmi
for j >= i && pole[j] >= pivot{ j-- }
if i < j { // nájdení kandidáti sa vymenia
pole[i], pole[j] = pole[j], pole[i]
}
} // pivota pichni medzi liliputánov a maxiputánov
pole[0], pole[j] = pole[j], pole[0]
return i }quicksort.go
Quicksort func cquickSort(pole []int, done chan bool) {
if len(pole) <= 1 {
} else {
index := pivot(pole)
cquickSort(pole[:(index-1)], left)
cquickSort(pole[index:], right)
}
}
func cquickSort(pole []int, done chan bool) {
if len(pole) <= 1 {
done <- true
} else {
index := pivot(pole)
left, right := make(chan bool), make(chan bool)
go cquickSort(pole[:(index-1)], left)
go cquickSort(pole[index:], right)
done <- (<-left && <-right)
}
}
func cquickSort(pole []int, done chan bool) {
if len(pole) <= 1 {
done <- true
} else if len(pole) < granularity {
squickSort(pole)
done <- true
} else {
index := pivot(pole)
left, right := make(chan bool), make(chan bool)
go cquickSort(pole[:(index-1)], left)
go cquickSort(pole[index:], right)
done <- (<-left && <-right)
}
}quicksort.go
Quicksort výsledkySize Granularity time500.000 500.000 109ms500.000 50.000 62ms500.000 5.000 78ms500.000 500 62ms500.000 50 171ms500.000 5 1375ms500.000 1 niet dosť kanálov...
Fibonacciho agentiVyrobíme niekoľko nezávislých agentov, ktorí zipf(ch1, ch2 chan int, f func(int, int) int) chan int
spája dvojice prvkov z kanála ch1 a ch2 pomocou funkcie f (u nás +) tail(ch1 chan int) chan int
číta z kanála ch1, priamo píše do výstupu, akurát prvý prvok z ch1 zabudne func fib1() chan int
podivným spôsobom generuje fibonacciho čisla...
aj to len trochu... spliter(ch chan int) (ch1 chan int, ch2 chan int)
číta z ch, a výsledky konkurentne kopíruje do ch1 aj ch2
Agent zip
func zipf(ch1, ch2 chan int, f func(int, int) int) chan int {
ch := make(chan int)
zipCount++
go func() {
for {
f1 := <-ch1 // číta dvojice f1
f2 := <-ch2 // f2 z ch1 a ch2
ch <- f(f1, f2) // píše f(f1, f2), alias f1+f2
}
}()
return ch
}
fibStream.go
Agent tailfunc tail(ch1 chan int) chan int {
ch := make(chan int)
tailCount++
<-ch1 // prvý prvok zabudne
go func() {
for {
ch <- <-ch1
}
}()
return ch
}
fibStream.go
Agent fib1(katastrofické výsledky)
func fib1() chan int {
ch := make(chan int)
fibCount++
go func() {
ch <- 1
ch <- 1
for val := range zipf(fib1(), tail(fib1()),
func(x, y int) int { return x + y }) {
ch <- val
}
}()
return ch
}
fibStream.go
fib (fibCount, zipCount, tailCount)1 (1,0,0)1 (1,0,0)2 (7,1,3)3 (23,7,11)5 (63,31,31)8 (255,71,127)13 (1023,255,511)21 (2111,1023,1055)34 (8191,4095,4095)
Agent splitterfunc spliter(ch chan int) (ch1 chan int, ch2 chan int) {
ch1 = make(chan int)
ch2 = make(chan int)
spliterCount++
go func() {
for {
val := <-ch
// ch1 <- val deadlock! why ?
// ch2 <- val
go func() { ch1 <- val }()
go func() { ch2 <- val }()
}
}()
return ch1, ch2
}fibStream.go
Agent fib(prijatelné výsledky)
func fib() chan int {
ch := make(chan int)
fibCount++
go func() {
ch <- 1
ch <- 1
ch1, ch2 := splitter(fib()) // použitie splittera
for val := range zipf(ch1, tail(ch2),
func(x, y int) int { return x + y }) {
ch <- val
}
}()
return ch
}
fibStream.go
1 (1,0,0, 0)1 (1,0,0, 0)2 (5,2,4, 4)3 (8,6,7, 7)5 (12,9,11, 11)8 (15,13,14, 14)13 (19,16,18, 18)21 (22,20,21, 21)..........40.Fibonacciho číslo165580141 (138,135,137, 137)Success: process exited with code 0.