Slide 1

Slide 1 text

Simulating a real-world system in Go Sameer Ajmani Go team lead, Google dotGo, Paris November 2017

Slide 2

Slide 2 text

By Sajibabu79 (Own work) [CC BY-SA 3.0 (https://creativecommons.org/licenses/by-sa/3.0)], via Wikimedia Commons

Slide 3

Slide 3 text

By Mtattrain (Own work) [CC BY-SA 4.0 (https://creativecommons.org/licenses/by-sa/4.0)], via Wikimedia Commons

Slide 4

Slide 4 text

By Eden, Janine and Jim from New York City (Voting Line IX) [CC BY 2.0 (http://creativecommons.org/licenses/by/2.0)], via Wikimedia Commons

Slide 5

Slide 5 text

https://www.goodfreephotos.com/people/people-in-a-coffee-shop.jpg.php

Slide 6

Slide 6 text

The simulator Generate customers Collect latency distribution Measure throughput and utilization Order coffee & wait Order coffee & wait Order coffee & wait Order coffee & wait Order coffee & wait Order coffee & wait Customers Wait times

Slide 7

Slide 7 text

An ideal latte Grind beans 1ms Make espresso 1ms Steam milk 1ms Make latte 1ms Time

Slide 8

Slide 8 text

The ideal function Grind beans 1ms Make espresso 1ms Steam milk 1ms Make latte 1ms func idealBrew() latte { grounds := grindCoffee(grinder) coffee := makeEspresso(espressoMachine, grounds) milk := steamMilk(steamer) return makeLatte(coffee, milk) } Time

Slide 9

Slide 9 text

Ideal performance Ideal Ideal Better Better

Slide 10

Slide 10 text

Reality strikes func grindCoffee(grinder *machine) grounds { grinder.add(runPhase(*grindTime)) return grounds(0) } func makeEspresso(espressoMachine *machine, grounds grounds) coffee { espressoMachine.add(runPhase(*pressTime)) return coffee(grounds) } func steamMilk(steamer *machine) milk { steamer.add(runPhase(*steamTime)) return milk(0) } By Tijuana Brass at en.wikipedia [GFDL (http://www.gnu.org/copyleft/fdl.html) or CC-BY- SA-3.0 (http://creativecommons.org/licenses/by-sa/3.0/)], from Wikimedia Commons

Slide 11

Slide 11 text

Lesson: Use the race detector ================== WARNING: DATA RACE Read at 0x00c42009c328 by goroutine 9: main.(*sampler).add() /usr/local/google/home/sameer/gocode/src/github.com/Sajmani/dotgo/coffee/perf.go:53 +0x72 main.grindCoffee() /usr/local/google/home/sameer/gocode/src/github.com/Sajmani/dotgo/coffee/main.go:151 +0x99 main.idealBrew() /usr/local/google/home/sameer/gocode/src/github.com/Sajmani/dotgo/coffee/main.go:130 +0x4e main.main.func2() /usr/local/google/home/sameer/gocode/src/github.com/Sajmani/dotgo/coffee/main.go:596 +0x3d main.perfTest.func2() /usr/local/google/home/sameer/gocode/src/github.com/Sajmani/dotgo/coffee/perf.go:182 +0xe4 Previous write at 0x00c42009c328 by goroutine 8: main.(*sampler).add() /usr/local/google/home/sameer/gocode/src/github.com/Sajmani/dotgo/coffee/perf.go:57 +0xba main.grindCoffee() /usr/local/google/home/sameer/gocode/src/github.com/Sajmani/dotgo/coffee/main.go:151 +0x99 main.idealBrew() /usr/local/google/home/sameer/gocode/src/github.com/Sajmani/dotgo/coffee/main.go:130 +0x4e main.main.func2() /usr/local/google/home/sameer/gocode/src/github.com/Sajmani/dotgo/coffee/main.go:596 +0x3d main.perfTest.func2() /usr/local/google/home/sameer/gocode/src/github.com/Sajmani/dotgo/coffee/perf.go:182 +0xe4

Slide 12

Slide 12 text

var kitchen sync.Mutex func lockingBrew() latte { kitchen.Lock() defer kitchen.Unlock() grounds := grindCoffee(grinder) coffee := makeEspresso(espressoMachine, grounds) milk := steamMilk(steamer) return makeLatte(coffee, milk) } By David Adam Kess (Own work) [CC BY-SA 4.0], via Wikimedia Commons

Slide 13

Slide 13 text

Locking performance Ideal (uncontended) Ideal (uncontended) Whole-kitchen locking Whole-kitchen locking

Slide 14

Slide 14 text

Fine-grain locking func lockingGrind() grounds { grinder.Lock() defer grinder.Unlock() return grindCoffee(grinder) } func lockingPress(grounds grounds) coffee { espressoMachine.Lock() defer espressoMachine.Unlock() return makeEspresso(espressoMachine, grounds) } func lockingSteam() milk { steamer.Lock() defer steamer.Unlock() return steamMilk(steamer) } By Tijuana Brass at en.wikipedia [GFDL (http://www.gnu.org/copyleft/fdl.html) or CC-BY- SA-3.0 (http://creativecommons.org/licenses/by-sa/3.0/)], from Wikimedia Commons

Slide 15

Slide 15 text

Fine-grain locking performance Fine-grain locking Fine-grain locking Ideal (uncontended) Ideal (uncontended) W hole-kitchen locking Whole-kitchen locking

Slide 16

Slide 16 text

Lesson: Minimize locked duration Fine-grain locking Fine-grain locking Ideal (uncontended) Ideal (uncontended) W hole-kitchen locking Whole-kitchen locking

Slide 17

Slide 17 text

Structural limits Grind beans 1ms Make espresso 1ms Steam milk 1ms Make latte 1ms Grind beans 1ms Make espresso 1ms Steam milk 1ms Make latte 1ms CPU1 Time Grind beans 1ms Make espresso 1ms Steam milk 1ms Make latte 1ms Grind beans 1ms Make espresso 1ms Steam milk 1ms CPU2 CPU3 CPU4 CPU5 CPU6 Grind beans 1ms Make espresso 1ms Grind beans 1ms

Slide 18

Slide 18 text

By dumbonyc (Brooklyn Ice Cream Factory) [CC BY-SA 2.0 (https://creativecommons.org/licenses/by-sa/2.0)], via Wikimedia Commons

Slide 19

Slide 19 text

Two sets of machines func multiGrind() grounds { m := <-grinders grounds := grindCoffee(m) grinders <- m return grounds } func multiPress(grounds grounds) coffee { m := <-espressoMachines coffee := makeEspresso(m, grounds) espressoMachines <- m return coffee } func multiSteam() milk { m := <-steamers milk := steamMilk(m) steamers <- m return milk } By Tijuana Brass at en.wikipedia [GFDL (http://www.gnu.org/copyleft/fdl.html) or CC-BY- SA-3.0 (http://creativecommons.org/licenses/by-sa/3.0/)], from Wikimedia Commons

Slide 20

Slide 20 text

Multi machine performance Ideal & Multiple machines Fine-grain locking Ideal & Multiple machines Fine-grain locking W hole-kitchen locking Whole-kitchen locking

Slide 21

Slide 21 text

Lesson: Identify the limiting factor

Slide 22

Slide 22 text

\ By HAO XING (BIZ INDIA-STARBUCKS-BIZPLUS 2 SE) [CC BY 2.0 (http://creativecommons.org/licenses/by/2.0)], via Wikimedia Commons

Slide 23

Slide 23 text

A coffee pipeline func (p *linearPipeline) grinder() { for o := range p.orders { o.grounds = grindCoffee(p.grinderMachine) p.ordersWithGrounds <- o } close(p.ordersWithGrounds) } func (p *linearPipeline) presser() { for o := range p.ordersWithGrounds { o.coffee = makeEspresso( p.espressoMachine, o.grounds) p.ordersWithCoffee <- o } close(p.ordersWithCoffee) } func (p *linearPipeline) steamer() { for o := range p.ordersWithCoffee { o.milk <- steamMilk(p.steamerMachine) } close(p.done) } By Tijuana Brass at en.wikipedia [GFDL (http://www.gnu.org/copyleft/fdl.html) or CC-BY- SA-3.0 (http://creativecommons.org/licenses/by-sa/3.0/)], from Wikimedia Commons

Slide 24

Slide 24 text

Pipeline vs. locking Unbuffered pipeline Fine-grain locking & Unbuffered pipeline Fine-grain locking Ideal (uncontended) Ideal (uncontended) W hole-kitchen locking Whole-kitchen locking

Slide 25

Slide 25 text

Buffered vs. unbuffered Buffered pipelines Buffered pipelines Unbuffered pipeline Unbuffered pipeline Ideal (uncontended) Ideal (uncontended) W hole-kitchen locking Whole-kitchen locking

Slide 26

Slide 26 text

Major vs. minor optimizations

Slide 27

Slide 27 text

Many more scenarios

Slide 28

Slide 28 text

Remove structural barriers to parallelism Study real-world systems for inspiration

Slide 29

Slide 29 text

Get the simulator More simulation modes! More controls! More data! • Americano (no “steam” stage) & Espresso (no “steam” or “make” stage) • Steam milk in parallel with grinding beans & making espresso • Controls: stage durations, jitter, bounded request queues, arrival rate • Data: latency distributions, CPU utilization, queue drop rate go get github.com/Sajmani/dotgo/coffee