├── .gitignore ├── README.md ├── books ├── alice.txt ├── christmas-carol.txt ├── dracula.txt ├── moby.txt ├── pride-and-prejudice.txt ├── sherlock.txt └── tom-sawyer.txt ├── examples ├── benchfib │ └── wrong_test.go ├── benchmap │ └── benchmap_test.go ├── benchmark.go ├── concat │ └── concat_test.go ├── esc │ ├── center.go │ ├── esc_test.go │ └── sum.go ├── fib │ └── fib_test.go ├── grow.go ├── hellohttp.go ├── inuseallocs │ └── main.go ├── ioloop.go ├── max │ ├── max.go │ └── max2.go ├── pool.go ├── popcnt │ ├── popcnt2_test.go │ └── popcnt_test.go ├── reset.go ├── semaphore.go └── t.p ├── go-concur.slide ├── go-concur ├── CPU-Moores-law.png ├── Ivy-Bridge_Die_Flat-HR.jpg ├── divan-fanin.gif ├── divan-servers.gif ├── divan-servers3.gif ├── divan-workers2.gif ├── go-sched-in-motion.jpg ├── go-sched-our-cast.jpg ├── go-sched-steal.jpg ├── go-sched-syscall.jpg ├── gophermegaphones.jpg ├── guard-page.png ├── heap_stack.png ├── pipeline-results.png ├── stack-growth.png ├── threads-stack.png └── tree.png ├── go-prod-grade.slide ├── images ├── 395980-jackie-stewart.jpg ├── Mjc5MTM2Nw.png ├── Nehalem_Die_Shot_3.jpg ├── alloc_objects.svg ├── block.svg ├── c-montgomery-burns_197x282.jpg ├── cKleftN.gif ├── cpu-mysql-updated.svg ├── cpu.svg ├── download.png ├── flamegraph1.png ├── flamegraph2.png ├── flamegraph3.png ├── inuse_objects.svg ├── latency.png ├── media-20160803.jpg ├── numbers.png ├── perf.png ├── profile.svg ├── torch.svg └── xeon_e5_v4_hcc_rings.jpg ├── img ├── golang-rules-and-limitations.png ├── golang-stdlib-tag-cloud.png ├── how-web-works.png ├── logo-1.png ├── logo-2.png ├── logo-3.png ├── logo-4.png ├── logo-5.png ├── logo-6.png └── logo-7.png ├── introduction-to-go-v2.slide ├── introduction-to-go.slide ├── introduction-to-go2-v2.slide ├── introduction-to-go2.slide ├── net.slide ├── oop-in-go.slide ├── performance.slide ├── src ├── assignment │ ├── assignment1 │ │ └── main.go │ └── assignment2 │ │ └── main.go ├── comments │ └── comments1 │ │ └── main.go ├── conditionals │ ├── conditionals1 │ │ └── main.go │ ├── conditionals2 │ │ └── main.go │ └── conditionals3 │ │ └── main.go ├── const │ └── const1 │ │ └── main.go ├── defer │ └── defer1.go ├── equality │ └── equality1 │ │ └── main.go ├── exercises │ ├── countdir │ │ ├── countdir.go │ │ └── testdata │ │ │ ├── christmas-carol.txt │ │ │ ├── dont-read-me │ │ │ └── tom-sawyer.txt │ ├── countlines │ │ ├── count.go │ │ ├── count_test.go │ │ └── testdata │ │ │ └── alice.txt │ ├── countmanyfiles │ │ ├── countmanyfiles.go │ │ └── testdata │ │ │ ├── dracula.txt │ │ │ ├── pride-and-prejudice.txt │ │ │ └── sherlock.txt │ ├── errorhandling │ │ ├── errorhandling.go │ │ ├── errorhandling_test.go │ │ └── testdata │ │ │ └── alice.txt │ ├── httpget │ │ └── httpget.go │ ├── httplinecount │ │ └── .keep │ ├── input │ │ ├── input.go │ │ └── input_test.go │ ├── jsonenc │ │ ├── jsonenc.go │ │ └── jsonenc_test.go │ ├── linecount │ │ ├── linecount.go │ │ └── testdata │ │ │ └── moby.txt │ ├── methods │ │ ├── methods.go │ │ └── methods_test.go │ ├── pointers │ │ ├── pointers.go │ │ └── pointers_test.go │ ├── readers │ │ ├── ; │ │ ├── readers.go │ │ └── readers_test.go │ └── whatismyip │ │ └── whatismyip.go ├── fmt │ ├── fmt1 │ │ └── main.go │ ├── fmt2 │ │ └── main.go │ └── fmt3 │ │ └── main.go ├── functions │ ├── functions1 │ │ └── main.go │ ├── functions2 │ │ └── main.go │ ├── functions3 │ │ └── main.go │ └── functions4 │ │ └── main.go ├── hellohttp │ └── hellohttp.go ├── helloworld │ └── hello.go ├── http │ ├── http1.go │ └── http2.go ├── identifier │ ├── identifier1 │ │ └── main.go │ └── identifier2 │ │ └── main.go ├── imports │ ├── imports1 │ │ └── main.go │ ├── imports2 │ │ └── main.go │ └── imports3 │ │ └── main.go ├── increment │ ├── increment1 │ │ └── main.go │ └── increment2 │ │ └── main.go ├── jsonenc │ ├── jsonenc1.go │ └── jsonenc2.go ├── loops │ ├── loops1 │ │ └── main.go │ └── loops2 │ │ └── main.go ├── maps │ ├── maps1 │ │ └── main.go │ ├── maps2 │ │ └── main.go │ ├── maps2a │ │ └── main.go │ ├── maps3 │ │ └── main.go │ ├── maps4 │ │ └── main.go │ ├── maps5 │ │ └── main.go │ └── maps6 │ │ └── main.go ├── numbers │ └── numbers.go ├── packages │ ├── packages1 │ │ └── main.go │ └── packages2 │ │ └── main.go ├── pointers │ └── pointers1.go ├── range │ ├── range1 │ │ └── main.go │ ├── range2 │ │ └── main.go │ └── range2a │ │ └── main.go ├── scope │ ├── scope1 │ │ └── main.go │ ├── scope2 │ │ └── main.go │ ├── scope3 │ │ └── main.go │ ├── scope4 │ │ └── main.go │ └── scope5 │ │ └── main.go ├── server.go ├── simplestrings │ ├── cover.out │ ├── simplestrings.go │ └── simplestrings_test.go ├── slices │ ├── slices1 │ │ └── main.go │ ├── slices10 │ │ └── main.go │ ├── slices11 │ │ └── main.go │ ├── slices12 │ │ └── main.go │ ├── slices2 │ │ └── main.go │ ├── slices3 │ │ └── main.go │ ├── slices4 │ │ └── main.go │ ├── slices5 │ │ └── main.go │ ├── slices6 │ │ └── main.go │ ├── slices7 │ │ └── main.go │ ├── slices8 │ │ └── main.go │ └── slices9 │ │ └── main.go ├── strings │ └── strings1 │ │ └── main.go ├── switch │ └── switch1 │ │ └── main.go ├── types │ ├── types1 │ │ └── main.go │ ├── types2 │ │ └── main.go │ ├── types3 │ │ └── main.go │ └── types4 │ │ └── main.go ├── vanilla.go ├── vanilla_test.go ├── variable │ ├── variable1 │ │ └── main.go │ └── variable2 │ │ └── main.go ├── whattimeisit │ └── main.go └── zerovalue │ └── zerovalue1 │ └── main.go ├── task-list.md ├── task ├── advance.md ├── elementary.md ├── intermediate.md ├── list_slice_string.md └── workshop.md └── testing.slide /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go-training 2 | Golang training materials 3 | 4 | # Themes 5 | 6 | 1. Intoduction 7 | 1. OOP in Go 8 | 1. Testing 9 | 1. Network 10 | 1. Concurrency 11 | 1. Performance & optimization 12 | 13 | # Installation 14 | 15 | 1. Clone this code into a directory 16 | ``` 17 | git clone https://github.com/GolangUA/go-training 18 | ``` 19 | 20 | 2. Install the Go present tool 21 | ``` 22 | go get -u -v golang.org/x/tools/cmd/present 23 | ``` 24 | 25 | 3. Run the present tool 26 | ``` 27 | cd go-training && present 28 | ``` 29 | 30 | The slides will be available at [http://127.0.0.1:3999/](http://127.0.0.1:3999/) 31 | 32 | # References 33 | 34 | Books: 35 | 36 | - [Learning Go](https://miek.nl/go/) 37 | 38 | Articles: 39 | 40 | - [Resources for new go programmers by Dave Cheney](https://dave.cheney.net/resources-for-new-go-programmers) 41 | - [Golang github wiki](https://github.com/golang/go/wiki) 42 | - [golang underlying values](http://www.tapirgames.com/blog/golang-underlying-values) 43 | - [GitHub Go Wiki Article](https://github.com/golang/go/wiki/Articles) 44 | 45 | Practice: 46 | 47 | - [Go Tour](https://tour.golang.org/welcome/1) 48 | - [Go By Example](https://gobyexample.com) 49 | - [Golang Playground](https://play.golang.org/) 50 | - [Gothat & common mistakes](http://devs.cloudimmunity.com/gotchas-and-common-mistakes-in-go-golang/) 51 | - [Go Traps](https://go-traps.appspot.com) 52 | - [Go practice tasks](https://github.com/plutov/practice-go) 53 | - [Exercism](http://www.exercism.io/) 54 | 55 | Talks: 56 | 57 | - [Best Practices talk](https://talks.golang.org/2013/bestpractices.slide#1) 58 | - [dotGo Talks](https://www.dotconferences.com/conference/dotgo) 59 | 60 | Video: 61 | 62 | - [Just for func](https://www.youtube.com/playlist?list=PL64wiCrrxh4Jisi7OcCJIUpguV_f5jGnZ) 63 | 64 | Basic: 65 | 66 | - [Code](https://golang.org/doc/code.html) 67 | - [Go operators](https://www.tutorialspoint.com/go/go_operators.htm) 68 | 69 | Advance: 70 | 71 | - [Effective Go](https://golang.org/doc/effective_go.html) 72 | - [Code Style](https://github.com/golang/go/wiki/CodeReviewComments) 73 | 74 | Documentation: 75 | 76 | - [https://godoc.org](Godoc) 77 | 78 | # License and Materials 79 | This presentation is licensed under the [Creative Commons Attribution-ShareAlike 4.0 International licence](https://creativecommons.org/licenses/by-sa/4.0/). 80 | 81 | You are encouraged to remix, transform, or build upon the material, providing you give appropriate credit and distribute your contributions under the same license. 82 | -------------------------------------------------------------------------------- /books/tom-sawyer.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GolangUA/go-training/9d3a08f40b21124cdfe02e1838e8cecffddc7dd6/books/tom-sawyer.txt -------------------------------------------------------------------------------- /examples/benchfib/wrong_test.go: -------------------------------------------------------------------------------- 1 | package fib 2 | 3 | import "testing" 4 | 5 | // START OMIT 6 | func Fib(n int) int { 7 | a, b := 0, 1 8 | for i := 0; i < n; i++ { 9 | a, b = b, a+b 10 | } 11 | return a 12 | } 13 | 14 | func BenchmarkFibWrong(b *testing.B) { 15 | Fib(b.N) 16 | } 17 | 18 | func BenchmarkFibWrong2(b *testing.B) { 19 | for n := 0; n < b.N; n++ { 20 | Fib(n) 21 | } 22 | } 23 | 24 | // END OMIT 25 | -------------------------------------------------------------------------------- /examples/benchmap/benchmap_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "testing" 4 | 5 | // use go test -bench=. -benchmem 6 | 7 | var capitals = map[string]string{ 8 | "Algeria": "Algiers", 9 | "Argentina": "Buenos Aires", 10 | "Australia": "Canberra", 11 | "Austria": "Vienna", 12 | "Bahamas": "Nassau", 13 | "Belarus": "Minsk", 14 | "Bosnia and Herzegovina": "Sarajevo", 15 | "Brazil": "Brasilia", 16 | "Bulgaria": "Sofia", 17 | "Canada": "Ottawa", 18 | "China": "Beijing", 19 | "Croatia": "Zagreb", 20 | "Cuba": "Havana", 21 | "Egypt": "Cairo", 22 | "France": "Paris", 23 | "Germany": "Berlin", 24 | "Indonesia": "Jakarta", 25 | "Ireland": "Dublin", 26 | "Jamaica": "Kingston", 27 | "Japan": "Tokyo", 28 | "Luxembourg": "Luxembourg", 29 | } 30 | 31 | var sink string 32 | 33 | func BenchmarkMapLookup(b *testing.B) { 34 | var key = []byte{'F', 'r', 'a', 'n', 'c', 'e'} 35 | var r string 36 | for n := 0; n < b.N; n++ { 37 | r = capitals[string(key)] 38 | } 39 | sink = r 40 | } 41 | 42 | func BenchmarkMapLookup2(b *testing.B) { 43 | var key = []byte{'F', 'r', 'a', 'n', 'c', 'e'} 44 | var r string 45 | for n := 0; n < b.N; n++ { 46 | k := string(key) 47 | r = capitals[k] 48 | } 49 | sink = r 50 | } 51 | -------------------------------------------------------------------------------- /examples/benchmark.go: -------------------------------------------------------------------------------- 1 | package q 2 | 3 | func BenchmarkRead(b *testing.B) { 4 | b.ReportAllocs() // HL 5 | for n := 0; n < b.N; n++ { 6 | // function under test 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/concat/concat_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "net" 7 | "testing" 8 | "time" 9 | ) 10 | 11 | // sink to ensure the compiler does not optimise away dead assignments. 12 | var Result string 13 | 14 | // fake up some values for request and client. 15 | func setup(b *testing.B) (struct{ ID string }, net.Listener) { 16 | request := struct { 17 | ID string 18 | }{"9001"} 19 | client, err := net.Listen("tcp", ":0") 20 | if err != nil { 21 | b.Fatal(err) 22 | } 23 | return request, client 24 | } 25 | 26 | func BenchmarkConcatenate(b *testing.B) { 27 | request, client := setup(b) 28 | defer client.Close() 29 | 30 | b.ResetTimer() 31 | b.ReportAllocs() 32 | var r string 33 | for n := 0; n < b.N; n++ { 34 | // START1 OMIT 35 | s := request.ID 36 | s += " " + client.Addr().String() 37 | s += " " + time.Now().String() 38 | r = s 39 | // END1 OMIT 40 | } 41 | Result = r 42 | } 43 | 44 | func BenchmarkFprintf(b *testing.B) { 45 | request, client := setup(b) 46 | defer client.Close() 47 | 48 | b.ResetTimer() 49 | b.ReportAllocs() 50 | var r string 51 | for n := 0; n < b.N; n++ { 52 | // START2 OMIT 53 | var b bytes.Buffer 54 | fmt.Fprintf(&b, "%s %v %v", request.ID, client.Addr(), time.Now()) 55 | r = b.String() 56 | // END2 OMIT 57 | } 58 | Result = r 59 | } 60 | 61 | func BenchmarkSprintf(b *testing.B) { 62 | request, client := setup(b) 63 | defer client.Close() 64 | 65 | b.ResetTimer() 66 | b.ReportAllocs() 67 | var r string 68 | for n := 0; n < b.N; n++ { 69 | // START3 OMIT 70 | r = fmt.Sprintf("%s %v %v", request.ID, client.Addr(), time.Now()) 71 | // END3 OMIT 72 | } 73 | Result = r 74 | } 75 | 76 | func BenchmarkStrconv(b *testing.B) { 77 | request, client := setup(b) 78 | defer client.Close() 79 | 80 | b.ResetTimer() 81 | b.ReportAllocs() 82 | var r string 83 | for n := 0; n < b.N; n++ { 84 | // START4 OMIT 85 | b := make([]byte, 0, 40) 86 | b = append(b, request.ID...) 87 | b = append(b, ' ') 88 | b = append(b, client.Addr().String()...) 89 | b = append(b, ' ') 90 | b = time.Now().AppendFormat(b, "2006-01-02 15:04:05.999999999 -0700 MST") 91 | r = string(b) 92 | // END4 OMIT 93 | } 94 | Result = r 95 | } 96 | -------------------------------------------------------------------------------- /examples/esc/center.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // START OMIT 6 | 7 | type Point struct{ X, Y int } 8 | 9 | const Width = 640 10 | const Height = 480 11 | 12 | func Center(p *Point) { 13 | p.X = Width / 2 14 | p.Y = Height / 2 15 | } 16 | 17 | func NewPoint() { 18 | p := new(Point) 19 | Center(p) 20 | fmt.Println(p.X, p.Y) 21 | } 22 | 23 | // END OMIT 24 | -------------------------------------------------------------------------------- /examples/esc/esc_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "testing" 4 | 5 | var result int 6 | 7 | func BenchmarkSum(b *testing.B) { 8 | b.ReportAllocs() 9 | var r int 10 | for i := 0; i < b.N; i++ { 11 | r = Sum() 12 | } 13 | result = r 14 | } 15 | -------------------------------------------------------------------------------- /examples/esc/sum.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // START OMIT 6 | 7 | // Sum returns the sum of the numbers 1 to 100 8 | func Sum() int { 9 | const count = 100 10 | numbers := make([]int, 100) 11 | for i := range numbers { 12 | numbers[i] = i + 1 13 | } 14 | 15 | var sum int 16 | for _, i := range numbers { 17 | sum += i 18 | } 19 | return sum 20 | } 21 | 22 | // END OMIT 23 | 24 | func main() { 25 | fmt.Println(Sum()) 26 | } 27 | -------------------------------------------------------------------------------- /examples/fib/fib_test.go: -------------------------------------------------------------------------------- 1 | package fib 2 | 3 | // STARTBENCH OMIT 4 | import "testing" 5 | 6 | func BenchmarkFib(b *testing.B) { 7 | for n := 0; n < b.N; n++ { 8 | Fib(20) // run the Fib function b.N times 9 | } 10 | } 11 | 12 | // ENDBENCH OMIT 13 | 14 | // STARTFIB OMIT 15 | // Fib computes the n'th number in the Fibonacci series. 16 | func Fib(n int) int { 17 | if n < 2 { 18 | return n 19 | } 20 | return Fib(n-1) + Fib(n-2) 21 | } 22 | 23 | // ENDFIB OMIT 24 | 25 | func Fib2(n int) int { 26 | a, b := 0, 1 27 | for i := 0; i < n; i++ { 28 | a, b = b, a+b 29 | } 30 | return a 31 | } 32 | 33 | func TestFib(t *testing.T) { 34 | fibs := []int{0, 1, 1, 2, 3, 5, 8, 13, 21} 35 | for n, want := range fibs { 36 | got := Fib(n) 37 | if want != got { 38 | t.Errorf("Fib(%d): want %d, got %d", n, want, got) 39 | } 40 | } 41 | } 42 | 43 | func TestFib2(t *testing.T) { 44 | fibs := []int{0, 1, 1, 2, 3, 5, 8, 13, 21} 45 | for n, want := range fibs { 46 | got := Fib2(n) 47 | if want != got { 48 | t.Errorf("Fib2(%d): want %d, got %d", n, want, got) 49 | } 50 | } 51 | } 52 | 53 | func TestFibFib(t *testing.T) { 54 | for n := 0; n < 30; n++ { 55 | want := Fib(n) 56 | got := Fib2(n) 57 | if want != got { 58 | t.Errorf("Fib2(%d): want %d, got %d", n, want, got) 59 | } 60 | } 61 | } 62 | 63 | func _BenchmarkFib2(b *testing.B) { 64 | for n := 0; n < b.N; n++ { 65 | Fib2(20) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /examples/grow.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // START OMIT 6 | func main() { 7 | b := make([]int, 1024) 8 | b = append(b, 99) 9 | fmt.Println("len:", len(b), "cap:", cap(b)) 10 | } 11 | 12 | // END OMIT 13 | -------------------------------------------------------------------------------- /examples/hellohttp.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/http" 7 | ) 8 | 9 | func HelloServer(w http.ResponseWriter, req *http.Request) { 10 | log.Println(req.URL) 11 | fmt.Fprintf(w, "Hello, 世界!\nURL = %s\n", req.URL) 12 | } 13 | 14 | func main() { 15 | fmt.Println("please connect to localhost:7777/hello") 16 | http.HandleFunc("/hello", HelloServer) 17 | log.Fatal(http.ListenAndServe(":7777", nil)) 18 | } 19 | -------------------------------------------------------------------------------- /examples/inuseallocs/main.go: -------------------------------------------------------------------------------- 1 | // A simple example to demonstrate the difference between alloc_count and inuse_count 2 | package main 3 | 4 | import ( 5 | "math/rand" 6 | "runtime" 7 | 8 | "github.com/pkg/profile" 9 | ) 10 | 11 | const count = 100000 12 | 13 | var y []byte 14 | 15 | func main() { 16 | defer profile.Start(profile.MemProfile, profile.MemProfileRate(1)).Stop() 17 | y = allocate() 18 | runtime.GC() 19 | } 20 | 21 | func allocate() []byte { 22 | var x [][]byte 23 | for i := 0; i < count; i++ { 24 | x = append(x, makeByteSlice()) 25 | } 26 | return x[0] 27 | } 28 | 29 | func makeByteSlice() []byte { 30 | return make([]byte, rand.Intn(2^14)) 31 | } 32 | -------------------------------------------------------------------------------- /examples/ioloop.go: -------------------------------------------------------------------------------- 1 | package p 2 | 3 | import ( 4 | "encoding/binary" 5 | "io" 6 | ) 7 | 8 | // START OMIT 9 | type Conn struct { 10 | r io.ReadCloser 11 | ch chan uint32 12 | } 13 | 14 | func (c *Conn) Loop() { 15 | defer r.Close() 16 | var buf [512]byte 17 | for { 18 | b := buf[:] // create slice of buf 19 | n, err := c.r.Read(b) 20 | 21 | for b = b[:n]; len(b) != 0; b = b[4:] { 22 | ch <- binary.BigEndian.Uint32(b) 23 | } 24 | 25 | if err != nil { 26 | return 27 | } 28 | } 29 | } 30 | 31 | // END OMIT 32 | -------------------------------------------------------------------------------- /examples/max/max.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // START OMIT 4 | func Max(a, b int) int { 5 | if a > b { 6 | return a 7 | } 8 | return b 9 | } 10 | 11 | func F() { 12 | const a, b = 100, 20 13 | if Max(a, b) == b { 14 | panic(b) 15 | } 16 | } 17 | 18 | // END OMIT 19 | 20 | func main() { 21 | F() 22 | } 23 | -------------------------------------------------------------------------------- /examples/max/max2.go: -------------------------------------------------------------------------------- 1 | // +build none 2 | package main 3 | 4 | // START OMIT 5 | func F() { 6 | const a, b = 100, 20 7 | if a > b { 8 | return 9 | } 10 | panic(b) 11 | } 12 | 13 | // END OMIT 14 | 15 | func main() { 16 | F() 17 | } 18 | -------------------------------------------------------------------------------- /examples/pool.go: -------------------------------------------------------------------------------- 1 | package p 2 | 3 | import "sync" 4 | 5 | // START OMIT 6 | var pool = sync.Pool{New: func() interface{} { return make([]byte, 4096) }} 7 | 8 | func fn() { 9 | buf := pool.Get().([]byte) // takes from pool or calls New 10 | // do work 11 | pool.Put(buf) // returns buf to the pool 12 | } 13 | 14 | // END OMIT 15 | -------------------------------------------------------------------------------- /examples/popcnt/popcnt2_test.go: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | 3 | package main 4 | 5 | import "testing" 6 | 7 | const m1 = 0x5555555555555555 8 | const m2 = 0x3333333333333333 9 | const m4 = 0x0f0f0f0f0f0f0f0f 10 | const h01 = 0x0101010101010101 11 | 12 | func popcnt(x uint64) int { 13 | x -= (x >> 1) & m1 14 | x = (x & m2) + ((x >> 2) & m2) 15 | x = (x + (x >> 4)) & m4 16 | return int((x * h01) >> 56) 17 | } 18 | 19 | // START OMIT 20 | func BenchmarkPopcnt(b *testing.B) { 21 | for i := 0; i < b.N; i++ { 22 | // optimised away 23 | } 24 | } 25 | 26 | // END OMIT 27 | -------------------------------------------------------------------------------- /examples/popcnt/popcnt_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "testing" 4 | 5 | // START OMIT 6 | const m1 = 0x5555555555555555 7 | const m2 = 0x3333333333333333 8 | const m4 = 0x0f0f0f0f0f0f0f0f 9 | const h01 = 0x0101010101010101 10 | 11 | func popcnt(x uint64) uint64 { 12 | x -= (x >> 1) & m1 13 | x = (x & m2) + ((x >> 2) & m2) 14 | x = (x + (x >> 4)) & m4 15 | return (x * h01) >> 56 16 | } 17 | 18 | var n uint64 19 | 20 | func BenchmarkPopcnt(b *testing.B) { 21 | for i := 0; i < b.N; i++ { 22 | n = popcnt(uint64(i)) 23 | } 24 | } 25 | 26 | // END OMIT 27 | -------------------------------------------------------------------------------- /examples/reset.go: -------------------------------------------------------------------------------- 1 | package q 2 | 3 | // START1 OMIT 4 | func BenchmarkExpensive(b *testing.B) { 5 | boringAndExpensiveSetup() 6 | b.ResetTimer() // HL 7 | for n := 0; n < b.N; n++ { 8 | // function under test 9 | } 10 | } 11 | 12 | // END1 OMIT 13 | 14 | // START2 OMIT 15 | func BenchmarkComplicated(b *testing.B) { 16 | for n := 0; n < b.N; n++ { 17 | b.StopTimer() // HL 18 | complicatedSetup() 19 | b.StartTimer() // HL 20 | // function under test 21 | } 22 | } 23 | 24 | // END2 OMIT 25 | -------------------------------------------------------------------------------- /examples/semaphore.go: -------------------------------------------------------------------------------- 1 | package p 2 | 3 | type Work int 4 | 5 | // START OMIT 6 | var semaphore = make(chan struct{}, 10) 7 | 8 | func processRequest(work *Work) { 9 | semaphore <- struct{}{} // acquire semaphore 10 | // process request 11 | <-semaphore // release semaphore 12 | } 13 | 14 | // END OMIT 15 | -------------------------------------------------------------------------------- /examples/t.p: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GolangUA/go-training/9d3a08f40b21124cdfe02e1838e8cecffddc7dd6/examples/t.p -------------------------------------------------------------------------------- /go-concur.slide: -------------------------------------------------------------------------------- 1 | Go Concurrency 2 | 11 May 2017 3 | Tags: Go, Concurrency, principles, patterns 4 | 5 | Ivan Kutuzov 6 | SE, SoftServe 7 | http://discuss.7insyde.com 8 | https://golang.org.ua 9 | @arbrix 10 | 11 | * What we will talk about 12 | 13 | - From parallel to concurrent programming 14 | - Go Concurrency Basic 15 | - Principles 16 | - Patterns 17 | - Go Concurrency model 18 | 19 | * End of Moore's Law 20 | 21 | .image ./go-concur/CPU-Moores-law.png _ 700 22 | 23 | Economist, Technology Quarterl, [[http://www.economist.com/technology-quarterly/2016-03-12/after-moores-law][After Moore's law]], 12 March 2016 24 | 25 | * CPUs are not getting faster, but they are getting wider 26 | 27 | *Dave*Cheney*, [[http://dave.cheney.net/2015/08/08/performance-without-the-event-loop][Performance without the event loop]], 8 August 2015 28 | 29 | .image ./go-concur/Ivy-Bridge_Die_Flat-HR.jpg 30 | Image credit: Intel 31 | 32 | * Processes (what we remember from history) 33 | 34 | - batch processing model. 35 | - development of multiprocessing, or time sharing, operating systems. 36 | 37 | The operating systems maintain the illusion of concurrency by rapidly switching the attention of the CPU between active processes by recording the state of the current process, then restoring the state of another. This is called context switching. 38 | 39 | * Threads 40 | 41 | - have a share address space 42 | - lighter to schedule than processes -> faster to create and faster to switch between 43 | 44 | OS scheduler is universal but not optimal for each technology. 45 | OS can't make informed scheduling decisions, based on the Go model. 46 | 47 | * Communicating sequential processes 48 | [[https://en.wikipedia.org/wiki/Communicating_sequential_processes][Antony Hoare, 1978]] 49 | 50 | - Occam (May, 1983), 51 | - Erlang (Armstrong, 1986), 52 | - Newsqueak (Pike, 1988), 53 | - Concurrent ML (Reppy, 1993), 54 | - Alef (Winterbottom, 1995), 55 | - Limbo (Dorward, Pike, Winterbottom, 1996). 56 | - Go (Robert Griesemer, Rob Pike, Ken Thompson, 2007) 57 | - Crystal (Ary Borenszweig and Juan Wajnerman, 2011) 58 | - RaftLib (Jonathan Beard, 2014) 59 | 60 | * Golang Concurrency Basic 61 | 62 | * Goroutines and Channels 63 | Goroutines are independently executing functions in the same address space. 64 | 65 | go f() 66 | go g(1, 2) 67 | 68 | Channels are a typed conduit through which you can send and receive values with the channel operator, *<-* 69 | 70 | c := make(chan int) // Like maps and slices, channels must be created before use 71 | go func() { 72 | c <- 3 // Send 3 to channel c. 73 | }() 74 | n := <-c // Receive from c, and assign value to n. 75 | 76 | Channels can be buffered. Provide the buffer length as the second argument to make to initialize a _buffered_ channel: 77 | 78 | ch := make(chan int, 100) 79 | 80 | For more on the basics look at: *Rob*Pike*, [[http://talks.golang.org/2012/concurrency.slide#1][Go Concurrency Patterns]] 2012. 81 | 82 | * Range and Close 83 | 84 | A sender can *close* a channel to indicate that no more values will be sent. Receivers can test whether a channel has been closed by assigning a second parameter to the receive expression: afte 85 | 86 | v, ok := <-ch 87 | 88 | *ok* is false if there are no more values to receive and the channel is *closed* 89 | 90 | The loop *for*i*:=*range*c* receives values from the channel repeatedly until it is closed 91 | 92 | * Select 93 | 94 | The *select* statement lets a goroutine wait on multiple communication operations 95 | 96 | A *select* blocks until one of its cases can run, then it executes that case. It chooses one at random if multiple are ready. 97 | 98 | select { 99 | case c <- x: 100 | x, y = y, x+y 101 | case <-quit: 102 | fmt.Println("quit") 103 | return 104 | } 105 | 106 | * Default Selection 107 | 108 | The *default* case in a *select* is run if no other case is ready. 109 | 110 | Use a *default* case to try a send or receive without blocking: 111 | 112 | select { 113 | case i := <-c: 114 | // use i 115 | default: 116 | // receiving from c would block 117 | } 118 | 119 | * Exercise: Equivalent Binary Trees 120 | 121 | There can be many different binary trees with the same sequence of values stored at the leaves. For example, here are two binary trees storing the sequence 1, 1, 2, 3, 5, 8, 13. 122 | 123 | .image ./go-concur/tree.png _ 800 124 | 125 | A function to check whether two binary trees store the same sequence is quite complex in most languages. We'll use Go's concurrency and channels to write a simple solution. 126 | 127 | * Continue exercise: 128 | 129 | This example uses the tree package, which defines the type: 130 | 131 | type Tree struct { 132 | Left *Tree 133 | Value int 134 | Right *Tree 135 | } 136 | 137 | .link https://tour.golang.org/concurrency/8 exercise: https://tour.golang.org/concurrency/8 138 | 139 | * sync.Mutex 140 | 141 | But what if we don't need communication? What if we just want to make sure only one goroutine can access a variable at a time to avoid conflicts? 142 | 143 | This concept is called _mutual_exclusion_, and the conventional name for the data structure that provides it is _mutex_ 144 | 145 | Go's standard library provides mutual exclusion with *sync.Mutex* and its two methods: 146 | 147 | Lock 148 | Unlock 149 | 150 | * sync.WaitGroup 151 | 152 | A *WaitGroup* waits for a collection of goroutines to finish. The main goroutine calls *Add* to set the number of goroutines to wait for. Then each of the goroutines runs and calls *Done* when finished. At the same time, *Wait* can be used to block until all goroutines have finished. 153 | 154 | var wg sync.WaitGroup 155 | var urls = []string{ 156 | "http://www.golang.org/", 157 | "http://www.google.com/", 158 | "http://www.somestupidname.com/", 159 | } 160 | for _, url := range urls { 161 | wg.Add(1) // Increment the WaitGroup counter. 162 | go func(url string) {// Launch a goroutine to fetch the URL. 163 | defer wg.Done()// Decrement the counter when the goroutine completes. 164 | http.Get(url)// Fetch the URL. 165 | }(url) 166 | } 167 | wg.Wait()// Wait for all HTTP fetches to complete. 168 | 169 | * Deadlock 170 | 171 | [[https://youtu.be/3EW1hZ8DVyw][Richard Fliam - A Practical Guide to Preventing Deadlocks and Leaks in Go]] 172 | 173 | A deadlock happens when a group of goroutines are waiting for each other and none of them is able to proceed. 174 | 175 | func main() { 176 | ch := make(chan int) 177 | ch <- 1 178 | fmt.Println(<-ch) 179 | } 180 | 181 | * Semaphore 182 | 183 | func execute() { 184 | s := make(chan struct{}, 3) 185 | 186 | for i := 0; i < 4; i++ { 187 | go doStuff(s) 188 | } 189 | } 190 | 191 | //.... 192 | func doStuff(s chan struct{}) { 193 | s <- struct{}{} 194 | defer func() { <-s }() 195 | //.. DO STUFF 196 | } 197 | 198 | * Timeout 199 | 200 | select { 201 | case <-ch: 202 | // a read from ch has occurred 203 | case <-time.After(5 * time.Second): // HL 204 | // the read from ch has timed out 205 | } 206 | 207 | * Moving on 208 | 209 | func findFirstResult(conns []Conn, query string) Result { 210 | ch := make(chan Result) 211 | searchReplica := func(i int) { c <- conns[i].Search(query) } 212 | for i := range replicas { 213 | go searchReplica(i) 214 | } 215 | return <-ch 216 | } 217 | 218 | * Let's write some functions 219 | 220 | func gen(nums ...int) <-chan int { 221 | out := make(chan int) 222 | go func() { 223 | for _, n := range nums { 224 | out <- n 225 | } 226 | close(out) 227 | }() 228 | return out 229 | } 230 | 231 | * And one more 232 | 233 | func sq(in <-chan int) <-chan int { 234 | out := make(chan int) 235 | go func() { 236 | for n := range in { 237 | out <- n * n 238 | } 239 | close(out) 240 | }() 241 | return out 242 | } 243 | 244 | * Merge func 245 | 246 | func merge(cs ...<-chan int) <-chan int { 247 | var wg sync.WaitGroup 248 | out := make(chan int) 249 | 250 | // Start an output goroutine for each input channel in cs. output 251 | // copies values from c to out until c is closed, then calls wg.Done. 252 | output := func(c <-chan int) { 253 | for n := range c { 254 | out <- n 255 | } 256 | wg.Done() 257 | } 258 | wg.Add(len(cs)) 259 | for _, c := range cs { 260 | go output(c) 261 | } 262 | 263 | // Start a goroutine to close out once all the output goroutines are 264 | // done. This must start after the wg.Add call. 265 | go func() { 266 | wg.Wait() 267 | close(out) 268 | }() 269 | return out 270 | } 271 | 272 | * Stopping short 273 | 274 | - stages close their outbound channels when all the send operations are done. 275 | - stages keep receiving values from inbound channels until those channels are closed or the senders are unblocked. 276 | 277 | // Consume the first value from output. 278 | out := merge(c1, c2) 279 | fmt.Println(<-out) // 4 or 9 280 | return 281 | // Since we didn't receive the second value from out, 282 | // one of the output goroutines is hung attempting to send it. 283 | 284 | c := make(chan int, 2) // buffer size 2 285 | c <- 1 // succeeds immediately 286 | c <- 2 // succeeds immediately 287 | c <- 3 // blocks until another goroutine does <-c and receives 1 288 | 289 | * Explicit cancellation 290 | 291 | func main() { 292 | // Set up a done channel that's shared by the whole pipeline, 293 | // and close that channel when this pipeline exits, as a signal 294 | // for all the goroutines we started to exit. 295 | done := make(chan struct{}) // HL 296 | defer close(done) // HL 297 | 298 | in := gen(done, 2, 3) 299 | 300 | // Distribute the sq work across two goroutines that both read from in. 301 | c1 := sq(done, in) 302 | c2 := sq(done, in) 303 | 304 | // Consume the first value from output. 305 | out := merge(done, c1, c2) 306 | fmt.Println(<-out) // 4 or 9 307 | 308 | // done will be closed by the deferred call. // HL 309 | } 310 | 311 | * Home materials 312 | 313 | This page links to resources for learning about concurrency in Go. The items are presented in order, from beginner material to advanced topics. 314 | 315 | .link https://github.com/golang/go/wiki/LearnConcurrency Learn Concurrency by Golang Wiki 316 | 317 | * Golang Concurrency Principles & Patterns 318 | 319 | * Pipelines 320 | 321 | *@Gmarik*, [[http://www.gmarik.info/blog/2016/experimenting-with-golang-pipelines/][Experimenting with Go pipelines]], 27 May 2016 322 | 323 | Why pipelines are great 324 | 325 | - gives a high-level overview what a system does 326 | - composable: allows swapping/injecting new “stages” relatively simple. 327 | 328 | Also, advice to view: 329 | 330 | *John*Graham-Cumming*, [[https://youtu.be/woCg2zaIVzQ][I came for the easy concurrency I stayed for the easy composition]], dotGo 2014 331 | 332 | * In reality, building a pipeline isn’t trivial: 333 | 334 | - handling errors isn’t always obvious: ignore or stop the whole process? 335 | 336 | * Adding concurrency takes it to the next level of complexity: 337 | 338 | - distributing expensive computation across available computational units 339 | - stages coordination 340 | - efficient resource usage: start/stop processing as soon as needed, pooling 341 | 342 | * Process composition 343 | 344 | {generator |> heavyCalculations |> consume} 345 | 346 | * More complex example (workers) 347 | 348 | |> {heavyCalculations} | 349 | |> {heavyCalculations} | 350 | |> {heavyCalculations} | 351 | ... 352 | {generator(N)} >|> {heavyCalculations} |> {consume} 353 | |> {heavyCalculations} | 354 | |> {heavyCalculations} | 355 | |> {heavyCalculations} | 356 | 357 | * Even more complex example: Parallel 358 | 359 | |> {heavyCalculations} | 360 | |> {heavyCalculations} | 361 | |> {heavyCalculations} | 362 | {generator(0..N/2)} >|> {heavyCalculations} |> {consume} 363 | |> {heavyCalculations} | 364 | |> {heavyCalculations} | 365 | |> {heavyCalculations} | 366 | ------------------------------------------------- 367 | |> {heavyCalculations} | 368 | |> {heavyCalculations} | 369 | |> {heavyCalculations} | 370 | ... 371 | {generator(N/2..N)} >|> {heavyCalculations} |> {consume} 372 | |> {heavyCalculations} | 373 | |> {heavyCalculations} | 374 | |> {heavyCalculations} | 375 | 376 | * Benchmarks 377 | 378 | .image ./go-concur/pipeline-results.png 500 1000 379 | 380 | * Patterns 381 | 382 | *Ivan*Danyliuk*, [[https://divan.github.io/posts/go_concurrency_visualize][Visualizing Concurrency in Go]], 24 January 2016 383 | Slides from GopherCon'16: [[http://divan.github.io/talks/2016/gophercon][gophercon]] 384 | *Rob*Pike*, [[http://www.youtube.com/watch?v=f6kdp27TYZs][Go Concurrency Patterns]] 385 | *Rob*Pike*, [[https://vimeo.com/49718712][Concurrency is not parallelism]] 386 | *Sameer*Ajmani*, [[https://youtu.be/QDDwwePbDtw][Advanced Go Concurrency Patterns]] 387 | 388 | 389 | * Simple Generator 390 | 391 | func boring(msg string) <-chan string { // Returns receive-only channel of strings. // HL 392 | c := make(chan string) 393 | go func() { // We launch the goroutine from inside the function. // HL 394 | for i := 0; ; i++ { 395 | c <- fmt.Sprintf("%s: %d", msg, i) 396 | time.Sleep(time.Duration(rand.Intn(2e3)) * time.Millisecond) 397 | } 398 | }() 399 | return c // Return the channel to the caller. // HL 400 | } 401 | 402 | * Fan-In code 403 | 404 | A function can read from multiple inputs and proceed until all are closed by multiplexing the input channels onto a single channel that's closed when all the inputs are closed. This is called fan-in. 405 | 406 | func fanIn(input1, input2 <-chan string) <-chan string { // HL 407 | c := make(chan string) 408 | go func() { for { c <- <-input1 }}() 409 | go func() { for { c <- <-input2 }}() 410 | return c 411 | } 412 | func main() { 413 | c := fanIn(boring("Joe"), boring("Ann")) // HL 414 | for i := 0; i < 10; i++ { 415 | fmt.Println(<-c) // HL 416 | } 417 | fmt.Println("You're both boring; I'm leaving.") 418 | } 419 | 420 | * Fan-In image 421 | 422 | .image ./go-concur/gophermegaphones.jpg _ 800 423 | 424 | * Fan-In 425 | 426 | .image ./go-concur/divan-fanin.gif 500 500 427 | 428 | * Workers or Fan-Out 429 | 430 | Multiple functions can read from the same channel until that channel is closed; this is called fan-out. This provides a way to distribute work amongst a group of workers to parallelize CPU use and I/O. 431 | 432 | 433 | const ( 434 | WORKERS = 5 435 | SUBWORKERS = 3 436 | TASKS = 20 437 | SUBTASKS = 10 438 | ) 439 | 440 | func main() { 441 | var wg sync.WaitGroup 442 | wg.Add(WORKERS) 443 | tasks := make(chan int) 444 | for i := 0; i < WORKERS; i++ { 445 | go worker(tasks, &wg) 446 | } 447 | for i := 0; i < TASKS; i++ { 448 | tasks <- i 449 | } 450 | close(tasks) 451 | wg.Wait() 452 | } 453 | 454 | * Workers or Fan-Out 455 | 456 | func worker(tasks <-chan int, wg *sync.WaitGroup) { 457 | defer wg.Done() 458 | for { 459 | task, ok := <-tasks 460 | if !ok { 461 | return 462 | } 463 | 464 | subtasks := make(chan int) 465 | for i := 0; i < SUBWORKERS; i++ { 466 | go subworker(subtasks) 467 | } 468 | for i := 0; i < SUBTASKS; i++ { 469 | task1 := task * i 470 | subtasks <- task1 471 | } 472 | close(subtasks) 473 | } 474 | } 475 | 476 | * Workers or Fan-Out 477 | 478 | func subworker(subtasks chan int) { 479 | for { 480 | task, ok := <-subtasks 481 | if !ok { 482 | return 483 | } 484 | time.Sleep(time.Duration(task) * time.Millisecond) 485 | fmt.Println(task) 486 | } 487 | } 488 | 489 | * Workers or Fan-Out 490 | 491 | .image ./go-concur/divan-workers2.gif 500 500 492 | 493 | * Server Code 494 | 495 | import "net" 496 | 497 | func handler(c net.Conn) { 498 | c.Write([]byte("ok")) 499 | c.Close() 500 | } 501 | 502 | func main() { 503 | l, err := net.Listen("tcp", ":5000") 504 | if err != nil { 505 | panic(err) 506 | } 507 | for { 508 | c, err := l.Accept() 509 | if err != nil { 510 | continue 511 | } 512 | go handler(c) 513 | } 514 | } 515 | 516 | * Server 517 | 518 | .image ./go-concur/divan-servers.gif 500 500 519 | 520 | * Server + Worker 521 | 522 | .image ./go-concur/divan-servers3.gif 500 500 523 | 524 | * Subscription 525 | 526 | type Fetcher interface { 527 | Fetch() (items []Item, next time.Time, err error) 528 | } 529 | 530 | func Fetch(domain string) Fetcher { /*...*/ } // fetches Items from domain 531 | 532 | type Subscription interface { 533 | Updates() <-chan Item // stream of Items 534 | Close() error // shuts down the stream 535 | } 536 | 537 | func Subscribe(fetcher Fetcher) Subscription { /*...*/ } // converts Fetches to a stream 538 | 539 | func Merge(subs ...Subscription) Subscription { /*...*/ } // merges several streams 540 | 541 | * Context 542 | 543 | // A Context carries a deadline, cancelation signal, and request-scoped values 544 | // across API boundaries. Its methods are safe for simultaneous use by multiple 545 | // goroutines. 546 | type Context interface { 547 | // Done returns a channel that is closed when this Context is canceled 548 | // or times out. 549 | Done() <-chan struct{} // HL 550 | 551 | // Err indicates why this context was canceled, after the Done channel 552 | // is closed. 553 | Err() error // HL 554 | 555 | // Deadline returns the time when this Context will be canceled, if any. 556 | Deadline() (deadline time.Time, ok bool) // HL 557 | 558 | // Value returns the value associated with key or nil if none. 559 | Value(key interface{}) interface{} // HL 560 | } 561 | 562 | * Share Memory By Mutex Struct 563 | 564 | type Resource struct { 565 | url string 566 | polling bool 567 | lastPolled int64 568 | } 569 | type Resources struct { 570 | sync.Mutex // HL 571 | data []*Resource 572 | } 573 | 574 | func Poller(res *Resources) { 575 | for { 576 | // get the least recently-polled Resource 577 | // and mark it as being polled 578 | res.Lock() // HL 579 | var r *Resource 580 | for _, v := range res.data { 581 | if v.polling { 582 | continue 583 | } 584 | if r == nil || v.lastPolled < r.lastPolled { 585 | r = v 586 | } 587 | } 588 | //... -> 589 | 590 | * Share Memory By Mutex Func 591 | 592 | //->... 593 | if r != nil { 594 | r.polling = true 595 | } 596 | res.Unlock() // HL 597 | if r == nil { 598 | continue 599 | } 600 | // poll the URL 601 | // update the Resource's polling and lastPolled 602 | res.Lock() // HL 603 | r.polling = false 604 | r.lastPolled = time.Nanoseconds() 605 | res.Unlock() // HL 606 | 607 | * Share Memory By Communicating 608 | 609 | type Resource string 610 | 611 | func Poller(in, out chan *Resource) { 612 | for r := range in { 613 | // poll the URL 614 | 615 | // send the processed Resource to out 616 | out <- r 617 | } 618 | } 619 | 620 | * Data Races 621 | 622 | Data races are among the most common and hardest to debug types of bugs in concurrent systems. A data race occurs when two goroutines access the same variable concurrently and at least one of the accesses is a write. See the [[https://golang.org/ref/mem/][The Go Memory Model]] for details. 623 | 624 | func main() { 625 | c := make(chan bool) 626 | m := make(map[string]string) 627 | go func() { 628 | m["1"] = "a" // First conflicting access. 629 | c <- true 630 | }() 631 | m["2"] = "b" // Second conflicting access. 632 | <-c 633 | for k, v := range m { 634 | fmt.Println(k, v) 635 | } 636 | } 637 | 638 | * Data race detector 639 | 640 | To help diagnose such bugs, Go includes a built-in data race detector. To use it, add the -race flag to the go command: 641 | 642 | $ go test -race mypkg // to test the package 643 | $ go run -race mysrc.go // to run the source file 644 | $ go build -race mycmd // to build the command 645 | $ go install -race mypkg // to install the package 646 | 647 | * Typical Data Races 648 | 649 | .link https://golang.org/doc/articles/race_detector.html#Race_on_loop_counter Race_on_loop_counter 650 | .link https://golang.org/doc/articles/race_detector.html#Accidentally_shared_variable Accidentally_shared_variable 651 | .link https://golang.org/doc/articles/race_detector.html#Unprotected_global_variable Unprotected_global_variable 652 | .link https://golang.org/doc/articles/race_detector.html#Primitive_unprotected_variable Primitive_unprotected_variable 653 | 654 | * Exercise: Web Crawler 655 | 656 | In this exercise you'll use Go's concurrency features to parallelize a web crawler. 657 | 658 | Modify the *Crawl* function to fetch URLs in parallel without fetching the same URL twice. 659 | 660 | .link https://tour.golang.org/concurrency/10 exercise https://tour.golang.org/concurrency/10 661 | 662 | _Hint_: you can keep a cache of the URLs that have been fetched on a map, but maps alone are not safe for concurrent use! 663 | 664 | * Conclusions 665 | 666 | - Distributing work onto available computational units can lead to increased performance 667 | - There are many ways to distribute the work across the units through various process-compositions 668 | - Performance depends on the size of the data set and the composition 669 | - Experiment, measure and pick the best one 670 | - Go provides powerful means to create simple and complex compositions 671 | 672 | * Golang Concurrency Mechanics 673 | 674 | * Go Scheduler 675 | *Daniel*Morsing*, [[http://morsmachine.dk/go-scheduler][Go Scheduler]] 676 | 677 | 3 usual models for threading 678 | - N:1 - several userspace threads (UT) are run on one OS thread (OST) 679 | - 1:1 - one UT of execution matches one OST 680 | - M:N - (Go use it): It schedules an arbitrary number of goroutines onto an arbitrary number of OST 681 | 682 | * Go Scheduler 3 main entities 683 | 684 | .image ./go-concur/go-sched-our-cast.jpg _ 800 685 | 686 | * Go Scheduler common example 687 | 688 | .image ./go-concur/go-sched-in-motion.jpg _ 600 689 | 690 | 691 | * Go Scheduler (syscall) 692 | 693 | .image ./go-concur/go-sched-syscall.jpg _ 800 694 | 695 | * Go Scheduler (Stealing work) 696 | 697 | .image ./go-concur/go-sched-steal.jpg _ 800 698 | 699 | * Goroutines blocking cases 700 | 701 | - Channel send and receive operations if those operations would block. 702 | - The go statement, although there is no guarantee that new goroutine will be scheduled immediately. 703 | - Blocking syscalls like file and network operations. 704 | - After being stopped for a garbage collection cycle. 705 | 706 | In Go, all I/O is blocking. The Go ecosystem is built around the idea that you write against a blocking interface and then handle concurrency through goroutines and channels rather than callbacks and futures. 707 | 708 | * Goroutines, stack management, and an integrated network poller 709 | 710 | - In conclusion, goroutines provide a powerful abstraction that frees the programmer from worrying about thread pools or event loops. 711 | - The stack of a goroutine is as big as it needs to be without being concerned about sizing thread stacks or thread pools. 712 | - The integrated network poller lets Go programmers avoid convoluted callback styles while still leveraging the most efficient IO completion logic available from the operating system. 713 | - The runtime makes sure that there will be just enough threads to service all your goroutines and keep your cores active. 714 | 715 | * Videos 716 | 717 | .link https://vimeo.com/115309491 Cancellation, Context, and Plumbing by Sameer Ajmani 718 | .link https://youtu.be/_YK0viplIl4 Sameer Ajmani - Simulating a real-world system in Go 719 | .link https://www.youtube.com/watch?v=cN_DpYBzKso&list=PL-wOZc0M0HQFs1vv792YeQCqBKzzaqq9r Go Concurrency Playlist 720 | .link https://youtu.be/HxaD_trXwRE Lexical Scanning in Go - Rob Pike 721 | 722 | * References 723 | 724 | [[http://golang.org/doc/effective_go.html][Effective Go]] 725 | [[https://blog.golang.org/share-memory-by-communicating][Share Memory By Communicating]]: Andrew Gerrand 726 | [[https://blog.golang.org/go-concurrency-patterns-timing-out-and][Go Concurrency Patterns: Timing out, moving on]]: Andrew Gerrand 727 | [[https://blog.golang.org/concurrency-is-not-parallelism][Concurrency is not parallelism]]: Rob Pike 728 | [[https://talks.golang.org/2012/concurrency.slide][Go Concurrency Patterns]]: Rob Pike 729 | [[https://blog.golang.org/pipelines][Go Concurrency Patterns: Pipelines and cancellation]]: Sameer Ajmani 730 | [[https://blog.golang.org/advanced-go-concurrency-patterns][Advanced Go Concurrency Patterns]]: Sameer Ajmani 731 | [[https://blog.golang.org/context][Go Concurrency Patterns: Context]]: Sameer Ajmani 732 | [[https://blog.golang.org/race-detector][Introducing the Go Race Detector]]: Dmitry Vyukov and Andrew Gerrand 733 | [[http://dave.cheney.net/2015/08/08/performance-without-the-event-loop][Performance without the event loop]]: Dave Cheney 734 | [[https://divan.github.io/posts/go_concurrency_visualize][Visualizing Concurrency in Go]]: Ivan Danyliuk 735 | [[http://morsmachine.dk/go-scheduler][Go Scheduler]]: Daniel Morsing 736 | [[http://www.gmarik.info/blog/2016/experimenting-with-golang-pipelines/][Experimenting with Go pipelines]]: @Gmarik 737 | [[https://github.com/golang/go/wiki/LearnConcurrency][LearnConcurrency]]: Golang Wiki 738 | 739 | * So now you better understand the words 740 | 741 | _Parallelism_is_simply_running_things_in_parallel._ 742 | _Concurrency_is_a_way_to_structure_your_program._ 743 | -------------------------------------------------------------------------------- /go-concur/CPU-Moores-law.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GolangUA/go-training/9d3a08f40b21124cdfe02e1838e8cecffddc7dd6/go-concur/CPU-Moores-law.png -------------------------------------------------------------------------------- /go-concur/Ivy-Bridge_Die_Flat-HR.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GolangUA/go-training/9d3a08f40b21124cdfe02e1838e8cecffddc7dd6/go-concur/Ivy-Bridge_Die_Flat-HR.jpg -------------------------------------------------------------------------------- /go-concur/divan-fanin.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GolangUA/go-training/9d3a08f40b21124cdfe02e1838e8cecffddc7dd6/go-concur/divan-fanin.gif -------------------------------------------------------------------------------- /go-concur/divan-servers.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GolangUA/go-training/9d3a08f40b21124cdfe02e1838e8cecffddc7dd6/go-concur/divan-servers.gif -------------------------------------------------------------------------------- /go-concur/divan-servers3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GolangUA/go-training/9d3a08f40b21124cdfe02e1838e8cecffddc7dd6/go-concur/divan-servers3.gif -------------------------------------------------------------------------------- /go-concur/divan-workers2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GolangUA/go-training/9d3a08f40b21124cdfe02e1838e8cecffddc7dd6/go-concur/divan-workers2.gif -------------------------------------------------------------------------------- /go-concur/go-sched-in-motion.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GolangUA/go-training/9d3a08f40b21124cdfe02e1838e8cecffddc7dd6/go-concur/go-sched-in-motion.jpg -------------------------------------------------------------------------------- /go-concur/go-sched-our-cast.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GolangUA/go-training/9d3a08f40b21124cdfe02e1838e8cecffddc7dd6/go-concur/go-sched-our-cast.jpg -------------------------------------------------------------------------------- /go-concur/go-sched-steal.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GolangUA/go-training/9d3a08f40b21124cdfe02e1838e8cecffddc7dd6/go-concur/go-sched-steal.jpg -------------------------------------------------------------------------------- /go-concur/go-sched-syscall.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GolangUA/go-training/9d3a08f40b21124cdfe02e1838e8cecffddc7dd6/go-concur/go-sched-syscall.jpg -------------------------------------------------------------------------------- /go-concur/gophermegaphones.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GolangUA/go-training/9d3a08f40b21124cdfe02e1838e8cecffddc7dd6/go-concur/gophermegaphones.jpg -------------------------------------------------------------------------------- /go-concur/guard-page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GolangUA/go-training/9d3a08f40b21124cdfe02e1838e8cecffddc7dd6/go-concur/guard-page.png -------------------------------------------------------------------------------- /go-concur/heap_stack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GolangUA/go-training/9d3a08f40b21124cdfe02e1838e8cecffddc7dd6/go-concur/heap_stack.png -------------------------------------------------------------------------------- /go-concur/pipeline-results.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GolangUA/go-training/9d3a08f40b21124cdfe02e1838e8cecffddc7dd6/go-concur/pipeline-results.png -------------------------------------------------------------------------------- /go-concur/stack-growth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GolangUA/go-training/9d3a08f40b21124cdfe02e1838e8cecffddc7dd6/go-concur/stack-growth.png -------------------------------------------------------------------------------- /go-concur/threads-stack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GolangUA/go-training/9d3a08f40b21124cdfe02e1838e8cecffddc7dd6/go-concur/threads-stack.png -------------------------------------------------------------------------------- /go-concur/tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GolangUA/go-training/9d3a08f40b21124cdfe02e1838e8cecffddc7dd6/go-concur/tree.png -------------------------------------------------------------------------------- /go-prod-grade.slide: -------------------------------------------------------------------------------- 1 | How to create production grade solutions with Golang 2 | Tags: GolangUA, Go, GoBootCamp 3 | 4 | Ivan Kutuzov 5 | SoftServe 6 | https://golang.org.ua 7 | @GolangUA 8 | @arbrix 9 | 10 | * Agenda: 11 | 12 | - Recap 13 | - Production Grade 14 | - QA 15 | 16 | * Go Syntax 17 | 18 | - basic types 19 | - user types 20 | - buildin functions 21 | - funcions, methods, interfaces 22 | - packages, build, dependencies 23 | - tests, profile, performance, stress testing 24 | - concurrency 25 | 26 | .link http://blog.tamizhvendan.in/blog/2017/05/01/using-golang-in-production-my-experiences/ Using Golang in Production - My Experiences by Tamizhvendan 27 | 28 | * Stdlib 29 | 30 | ~140 Packages 31 | 32 | .image ./img/golang-stdlib-tag-cloud.png _ 900 33 | 34 | * Variety of packages 35 | 36 | .link https://awesome-go.com 37 | .link https://godoc.org 38 | .link http://go-search.org/search go-search.org - 1.015.598 packages 39 | 40 | * Nice toolset 41 | 42 | - *go*build*, which builds Go binaries using only information in the source files themselves, no separate makefiles 43 | - *go*test*, for unit testing and microbenchmarks 44 | - *go*fmt*, for formatting code 45 | - *go*get*, for retrieving and installing remote packages 46 | - *go*vet*, a static analyzer looking for potential errors in code 47 | - *go*run*, a shortcut for building and executing code 48 | - *godoc*, for displaying documentation or serving it via HTTP 49 | - *gorename*, for renaming variables, functions, and so on in a type-safe way 50 | - *go*generate*, a standard way to invoke code generators 51 | .caption source: [[https://github.com/golang/go/wiki/CodeTools][Code Tools]] 52 | .caption source: [[https://github.com/golang/go/wiki/GoGenerateTools][Generate Tools]] 53 | .caption source: [[https://github.com/golang/go/wiki/PackageManagementTools][Package Management Tools]] 54 | 55 | * From the theory to practice 56 | 57 | * Site Reliability Engineering 58 | 59 | [[https://landing.google.com/sre/book.html][Site Reliability Engineering]] 60 | .caption Edited by Betsy Beyer, Chris Jones, Jennifer Petoff and Niall Richard Murphy 61 | 62 | .link https://medium.com/@copyconstruct/logs-and-metrics-6d34d3026e38 Logs and Metrics by Cindy Sridharan 63 | 64 | * Production-grade Go 65 | 66 | The big difference between your code and your code in production is all the ways it can fail; production-grade code is code that recognizes that difference, and prevents or plans for it. 67 | 68 | .link https://www.oreilly.com/ideas/how-to-ship-production-grade-go How to ship production-grade Go by Kavya Joshi 69 | 70 | * What to think about at first: 71 | 72 | - Package Management 73 | - Project Organization / Isolation Layers 74 | - Testing 75 | - Cross Compilation Bliss & Multi-OS Mistakes 76 | - Error Handling 77 | 78 | * Wrap Errors 79 | 80 | _, err := ioutil.ReadAll(r) 81 | if err != nil { 82 | return errors.Wrap(err, "read failed") // "read failed: improperly formatted data" 83 | } 84 | 85 | 86 | 87 | // would be good to have: "starting app: querying database: loading migrations: run migration #12: pq: invalid syntax near 'JOIN'" 88 | 89 | * Report Panics 90 | 91 | To report panics of goroutines your application creates, use the inbuilt recover function. 92 | 93 | // postToSlack creates a message with the captured panic, timestamp, and hostname, 94 | // and posts it to the configured Slack channel. 95 | func postToSlack(panic interface{}) { 96 | ... 97 | } 98 | 99 | func reportPanics() { 100 | // Capture the panic if there is one, and report it. 101 | if panic := recover(); panic != nil { 102 | postToSlack(panic) 103 | } 104 | } 105 | 106 | // Version without panic reporting; this is not what you want!: 107 | func runWithoutPanicReporting() { 108 | // myFunc encapsulates the core logic to do something. 109 | // Run it in a separate goroutine. 110 | go myFunc() 111 | } 112 | 113 | * Continue... 114 | 115 | // Version with panic reporting; this is what you want: 116 | func runMyFuncPanicReporting() { 117 | go func() { 118 | // Defer reportPanics before calling myFunc -- 119 | // if myFunc panics, reportPanic will capture and report the panic right before 120 | // the goroutine exits. 121 | defer reportPanics() 122 | myFunc() 123 | }() 124 | } 125 | 126 | * Use structured logging 127 | 128 | {"level":"info","msg":"Redirecting user","server":"www.google.com", 129 | "time":"2017-03-25T17:00:00-08:00","userId":1} 130 | 131 | For example, you could arrange for a request handler's logs to always include the *userId*, *requestId* and *endpoint* fields. 132 | 133 | * Ship application metrics 134 | 135 | - What metrics should you collect? 136 | - How would you instrument your application to collect these metrics, and where would you send them? 137 | 138 | * Test more than you think you should 139 | 140 | - If your package provides a public interface — and it likely does, if only to be used by the rest of your code — test the interface as a consumer would. 141 | - Write integration tests. 142 | - Use the right tools for testing. 143 | - Ensure your tests cover all the important code-paths. 144 | 145 | * What about containers 146 | 147 | 148 | package main 149 | import ( 150 | "fmt" 151 | "io/ioutil" 152 | "net/http" 153 | "os" 154 | ) 155 | func main() { 156 | resp, err := http.Get("https://google.com") 157 | check(err) 158 | body, err := ioutil.ReadAll(resp.Body) 159 | check(err) 160 | fmt.Println(len(body)) 161 | } 162 | func check(err error) { 163 | if err != nil { 164 | fmt.Println(err) 165 | os.Exit(1) 166 | } 167 | } 168 | 169 | * Dockerize 170 | 171 | FROM golang:latest 172 | RUN mkdir /app 173 | ADD . /app/ 174 | WORKDIR /app 175 | RUN go build -o main . 176 | CMD ["/app/main"] 177 | 178 | But wait, `golang:latest` 267 MB 179 | .caption source: [[https://hub.docker.com/r/library/golang/tags/][docker hub]] 180 | 181 | * Docker from sratch 182 | 183 | FROM scratch 184 | ADD main / 185 | CMD ["/main"] 186 | 187 | But we should compile it first. 188 | 189 | GOOS=linux go build -o main . 190 | 191 | * Check 192 | 193 | $ docker run -it example-scratch 194 | no such file or directory 195 | 196 | Why? 197 | 198 | Go looking for the libraries that were used at compilation time at the host system, but it isn't exists at *docker*from*sratch* 199 | 200 | * How to compile with all dependencies 201 | 202 | CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main . 203 | 204 | One more check... 205 | 206 | $ docker run -it example-scratch 207 | Get https://google.com: x509: failed to load system roots and no roots provided 208 | 209 | * Add CA that exists at the systems 210 | 211 | FROM scratch 212 | ADD ca-certificates.crt /etc/ssl/certs/ 213 | ADD main / 214 | CMD ["/main"] 215 | 216 | Done! 217 | 218 | .caption source1: [[https://blog.codeship.com/building-minimal-docker-containers-for-go-applications/][Building Minimal Docker Containers for Go Applications]] 219 | .caption source2: [[http://www.jeffsloyer.io/post/cross-compiling-docker-alpine-golang/][Cross Compiling Golang With A Docker Alpine Container]] 220 | 221 | * Resources 222 | 223 | [[https://www.oreilly.com/ideas/how-to-ship-production-grade-go][How to ship production-grade Go]] 224 | [[https://npf.io/2017/03/3.5yrs-500k-lines-of-go/][3.5 Years, 500k Lines of Go (Part 1)]] 225 | [[https://peter.bourgon.org/go-in-production/][Go: Best Practices for Production Environments]] 226 | [[https://www.iron.io/go-after-2-years-in-production/][Go After 2 Years in Production (2013)]] 227 | [[https://gotocon.com/dl/goto-chicago-2016/slides/TravisReeder_GOInProduction.pdf][Go in production 2016]] 228 | 229 | 230 | [[https://golang.org/doc/code.html][How to Write Go Code]] 231 | [[https://golang.org/doc/effective_go.html][Effective Go]] 232 | [[https://github.com/golang/go/wiki/CodeReviewComments][Go Code Review Comments]] 233 | 234 | * Questions? 235 | -------------------------------------------------------------------------------- /images/395980-jackie-stewart.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GolangUA/go-training/9d3a08f40b21124cdfe02e1838e8cecffddc7dd6/images/395980-jackie-stewart.jpg -------------------------------------------------------------------------------- /images/Mjc5MTM2Nw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GolangUA/go-training/9d3a08f40b21124cdfe02e1838e8cecffddc7dd6/images/Mjc5MTM2Nw.png -------------------------------------------------------------------------------- /images/Nehalem_Die_Shot_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GolangUA/go-training/9d3a08f40b21124cdfe02e1838e8cecffddc7dd6/images/Nehalem_Die_Shot_3.jpg -------------------------------------------------------------------------------- /images/c-montgomery-burns_197x282.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GolangUA/go-training/9d3a08f40b21124cdfe02e1838e8cecffddc7dd6/images/c-montgomery-burns_197x282.jpg -------------------------------------------------------------------------------- /images/cKleftN.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GolangUA/go-training/9d3a08f40b21124cdfe02e1838e8cecffddc7dd6/images/cKleftN.gif -------------------------------------------------------------------------------- /images/download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GolangUA/go-training/9d3a08f40b21124cdfe02e1838e8cecffddc7dd6/images/download.png -------------------------------------------------------------------------------- /images/flamegraph1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GolangUA/go-training/9d3a08f40b21124cdfe02e1838e8cecffddc7dd6/images/flamegraph1.png -------------------------------------------------------------------------------- /images/flamegraph2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GolangUA/go-training/9d3a08f40b21124cdfe02e1838e8cecffddc7dd6/images/flamegraph2.png -------------------------------------------------------------------------------- /images/flamegraph3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GolangUA/go-training/9d3a08f40b21124cdfe02e1838e8cecffddc7dd6/images/flamegraph3.png -------------------------------------------------------------------------------- /images/latency.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GolangUA/go-training/9d3a08f40b21124cdfe02e1838e8cecffddc7dd6/images/latency.png -------------------------------------------------------------------------------- /images/media-20160803.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GolangUA/go-training/9d3a08f40b21124cdfe02e1838e8cecffddc7dd6/images/media-20160803.jpg -------------------------------------------------------------------------------- /images/numbers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GolangUA/go-training/9d3a08f40b21124cdfe02e1838e8cecffddc7dd6/images/numbers.png -------------------------------------------------------------------------------- /images/perf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GolangUA/go-training/9d3a08f40b21124cdfe02e1838e8cecffddc7dd6/images/perf.png -------------------------------------------------------------------------------- /images/xeon_e5_v4_hcc_rings.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GolangUA/go-training/9d3a08f40b21124cdfe02e1838e8cecffddc7dd6/images/xeon_e5_v4_hcc_rings.jpg -------------------------------------------------------------------------------- /img/golang-rules-and-limitations.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GolangUA/go-training/9d3a08f40b21124cdfe02e1838e8cecffddc7dd6/img/golang-rules-and-limitations.png -------------------------------------------------------------------------------- /img/golang-stdlib-tag-cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GolangUA/go-training/9d3a08f40b21124cdfe02e1838e8cecffddc7dd6/img/golang-stdlib-tag-cloud.png -------------------------------------------------------------------------------- /img/how-web-works.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GolangUA/go-training/9d3a08f40b21124cdfe02e1838e8cecffddc7dd6/img/how-web-works.png -------------------------------------------------------------------------------- /img/logo-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GolangUA/go-training/9d3a08f40b21124cdfe02e1838e8cecffddc7dd6/img/logo-1.png -------------------------------------------------------------------------------- /img/logo-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GolangUA/go-training/9d3a08f40b21124cdfe02e1838e8cecffddc7dd6/img/logo-2.png -------------------------------------------------------------------------------- /img/logo-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GolangUA/go-training/9d3a08f40b21124cdfe02e1838e8cecffddc7dd6/img/logo-3.png -------------------------------------------------------------------------------- /img/logo-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GolangUA/go-training/9d3a08f40b21124cdfe02e1838e8cecffddc7dd6/img/logo-4.png -------------------------------------------------------------------------------- /img/logo-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GolangUA/go-training/9d3a08f40b21124cdfe02e1838e8cecffddc7dd6/img/logo-5.png -------------------------------------------------------------------------------- /img/logo-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GolangUA/go-training/9d3a08f40b21124cdfe02e1838e8cecffddc7dd6/img/logo-6.png -------------------------------------------------------------------------------- /img/logo-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GolangUA/go-training/9d3a08f40b21124cdfe02e1838e8cecffddc7dd6/img/logo-7.png -------------------------------------------------------------------------------- /net.slide: -------------------------------------------------------------------------------- 1 | Network, Net package 2 | 16 May 2017 3 | 4 | Ivan Kutuzov 5 | ikut@softserveinc.com 6 | https://discuss.7insyde.com 7 | https://golang.org.ua 8 | @arbrix 9 | 10 | * License and Materials 11 | 12 | This presentation is licensed under the [[https://creativecommons.org/licenses/by-sa/4.0/][Creative Commons Attribution-ShareAlike 4.0 International]] license. 13 | 14 | You are encouraged to remix, transform, or build upon the material, providing you give appropriate credit and distribute your contributions under the same license. 15 | 16 | If you have suggestions or corrections to this presentation, please raise [[https://github.com/GolangUA/go-training/issues][an issue on the GitHub project]]. 17 | 18 | * Agenda 19 | 20 | - Recap 21 | - Networking tasks 22 | - Net package 23 | - Net/http package 24 | - Other net/... packages 25 | - Testing Network 26 | 27 | * Recap 28 | 29 | - Goals of programming 30 | 31 | * Recap 32 | 33 | - Goals of programming 34 | - Reduce costs 35 | - Increase revenue 36 | 37 | * Recap 38 | 39 | - Goals of programming 40 | - Reduce costs 41 | - Increase revenue 42 | 43 | How to achieve this goals? 44 | 45 | * How to achieve programming goals 46 | 47 | - Understanding the problem (problem defenition) 48 | 49 | * How to achieve programming goals 50 | 51 | - Understanding the problem (problem defenition) 52 | - Understanding the domain (all related technologies and existing solutions) 53 | 54 | * How to achieve programming goals 55 | 56 | - Understanding the problem (problem defenition) 57 | - Understanding the domain (all related technologies and existing solutions) 58 | - Understanding general theory (data structures and algorithms / patterns) 59 | 60 | * How to achieve programming goals 61 | 62 | - Understanding the problem (problem defenition) 63 | - Understanding the domain (all related technologies and existing solutions) 64 | - Understanding general theory (data structures and algorithms / patterns) 65 | - Understanding toolset (programmin language) 66 | 67 | * Network 68 | 69 | * Concepts in web principles 70 | 71 | - Request: request data from users, including POST, GET, Cookie and URL.+ 72 | - Response: response data from server to clients. 73 | - Conn: connections between clients and servers. 74 | - Handler: Request handling logic and response generation. 75 | 76 | * Standard http workflow 77 | 78 | .image ./img/how-web-works.png _ 800 79 | 80 | * Two general tasks 81 | 82 | - Client 83 | 84 | resp, err := http.Get("http://example.com/") 85 | ... 86 | resp, err := http.Post("http://example.com/upload", "image/jpeg", &buf) 87 | ... 88 | resp, err := http.PostForm("http://example.com/form", 89 | url.Values{"key": {"Value"}, "id": {"123"}}) 90 | 91 | - Server 92 | 93 | http.Handle("/foo", fooHandler) 94 | 95 | http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) { 96 | fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path)) 97 | }) 98 | 99 | log.Fatal(http.ListenAndServe(":8080", nil)) 100 | 101 | * Net package 102 | 103 | Package net provides a portable interface for network I/O, including TCP/IP, UDP, domain name resolution, and Unix domain sockets. 104 | 105 | Let's go deeply. 106 | 107 | * Net general 108 | 109 | Although the package provides access to low-level networking primitives, most clients will need only the basic interface provided by the Dial, Listen, and Accept functions and the associated Conn and Listener interfaces. The crypto/tls package uses the same interfaces and similar Dial and Listen functions. 110 | 111 | * Net Dial 112 | 113 | The Dial function connects to a server: 114 | 115 | conn, err := net.Dial("tcp", "golang.org:80") 116 | if err != nil { 117 | // handle error 118 | } 119 | fmt.Fprintf(conn, "GET / HTTP/1.0\r\n\r\n") 120 | status, err := bufio.NewReader(conn).ReadString('\n') 121 | // ... 122 | 123 | * Net Listen 124 | 125 | The Listen function creates servers: 126 | 127 | ln, err := net.Listen("tcp", ":8080") 128 | if err != nil { 129 | // handle error 130 | } 131 | for { 132 | conn, err := ln.Accept() 133 | if err != nil { 134 | // handle error 135 | } 136 | go handleConnection(conn) 137 | } 138 | 139 | * Net IP 140 | 141 | An IP is a single IP address, a slice of bytes. Functions in this package accept either 4-byte (IPv4) or 16-byte (IPv6) slices as input. 142 | 143 | type IP []byte 144 | func IPv4(a, b, c, d byte) IP 145 | 146 | * net/url 147 | 148 | Package url parses URLs and implements query escaping. [[https://golang.org/pkg/net/url/][link]] 149 | 150 | u, err := url.Parse("http://bing.com/search?q=dotnet") 151 | if err != nil { 152 | log.Fatal(err) 153 | } 154 | u.Scheme = "https" 155 | u.Host = "google.com" 156 | q := u.Query() 157 | q.Set("q", "golang") 158 | u.RawQuery = q.Encode() 159 | fmt.Println(u) 160 | 161 | * net/http 162 | 163 | Package http provides HTTP client and server implementations. [[https://golang.org/pkg/net/http/][link]] 164 | 165 | Get, Head, Post, and PostForm make HTTP (or HTTPS) requests: 166 | 167 | resp, err := http.Get("http://example.com/") 168 | ... 169 | resp, err := http.Post("http://example.com/upload", "image/jpeg", &buf) 170 | ... 171 | resp, err := http.PostForm("http://example.com/form", 172 | url.Values{"key": {"Value"}, "id": {"123"}}) 173 | 174 | The client must close the response body when finished with it: 175 | 176 | resp, err := http.Get("http://example.com/") 177 | if err != nil { 178 | // handle error 179 | } 180 | defer resp.Body.Close() 181 | body, err := ioutil.ReadAll(resp.Body) 182 | // ... 183 | 184 | * net/http (continue) 185 | 186 | For control over HTTP client headers, redirect policy, and other settings, create a Client: 187 | 188 | client := &http.Client{ 189 | CheckRedirect: redirectPolicyFunc, 190 | } 191 | 192 | resp, err := client.Get("http://example.com") 193 | // ... 194 | 195 | req, err := http.NewRequest("GET", "http://example.com", nil) 196 | // ... 197 | req.Header.Add("If-None-Match", `W/"wyzzy"`) 198 | resp, err := client.Do(req) 199 | // ... 200 | 201 | * net/http (continue 2) 202 | 203 | For control over proxies, TLS configuration, keep-alives, compression, and other settings, create a Transport: 204 | 205 | tr := &http.Transport{ 206 | TLSClientConfig: &tls.Config{RootCAs: pool}, 207 | DisableCompression: true, 208 | } 209 | client := &http.Client{Transport: tr} 210 | resp, err := client.Get("https://example.com") 211 | // ... 212 | 213 | *Important* Clients and Transports are safe for concurrent use by multiple goroutines and for efficiency should only be created once and re-used. 214 | 215 | * net/http (continue 3) 216 | 217 | ListenAndServe starts an HTTP server with a given address and handler. The handler is usually nil, which means to use DefaultServeMux. Handle and HandleFunc add handlers to DefaultServeMux: 218 | 219 | http.Handle("/foo", fooHandler) 220 | 221 | http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) { 222 | fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path)) 223 | }) 224 | 225 | log.Fatal(http.ListenAndServe(":8080", nil)) 226 | 227 | More control over the server's behavior is available by creating a custom Server: 228 | 229 | s := &http.Server{ 230 | Addr: ":8080", 231 | Handler: myHandler, 232 | ReadTimeout: 10 * time.Second, 233 | WriteTimeout: 10 * time.Second, 234 | MaxHeaderBytes: 1 << 20, 235 | } 236 | log.Fatal(s.ListenAndServe()) 237 | 238 | * net/http (handler) 239 | 240 | type Handler interface { 241 | ServeHTTP(ResponseWriter, *Request) 242 | } 243 | 244 | A Handler responds to an HTTP request. 245 | 246 | ServeHTTP should write reply headers and data to the ResponseWriter and then return. Returning signals that the request is finished; it is not valid to use the ResponseWriter or read from the Request.Body after or concurrently with the completion of the ServeHTTP call. 247 | 248 | * net/http (handler continue) 249 | 250 | func FileServer(root FileSystem) Handler 251 | 252 | FileServer returns a handler that serves HTTP requests with the contents of the file system rooted at root. 253 | 254 | To use the operating system's file system implementation, use http.Dir 255 | 256 | func NotFoundHandler() Handler 257 | 258 | NotFoundHandler returns a simple request handler that replies to each request with a “404 page not found” reply. 259 | 260 | func RedirectHandler(url string, code int) Handler 261 | 262 | RedirectHandler returns a request handler that redirects each request it receives to the given url using the given status code. 263 | 264 | * net/http (handler continue2) 265 | 266 | func StripPrefix(prefix string, h Handler) Handler 267 | 268 | StripPrefix returns a handler that serves HTTP requests by removing the given prefix from the request URL's Path and invoking the handler h. 269 | 270 | func TimeoutHandler(h Handler, dt time.Duration, msg string) Handler 271 | 272 | TimeoutHandler returns a Handler that runs h with the given time limit. 273 | 274 | * net/http type ServeMux 275 | 276 | ServeMux is an HTTP request multiplexer. It matches the URL of each incoming request against a list of registered patterns and calls the handler for the pattern that most closely matches the URL. 277 | 278 | Patterns name fixed, rooted paths, like "/favicon.ico", or rooted subtrees, like "/images/" (note the trailing slash). Longer patterns take precedence over shorter ones, so that if there are handlers registered for both "/images/" and "/images/thumbnails/", the latter handler will be called for paths beginning "/images/thumbnails/" and the former will receive requests for any other paths in the "/images/" subtree. 279 | 280 | Note that since a pattern ending in a slash names a rooted subtree, the pattern "/" matches all paths not matched by other registered patterns, not just the URL with Path == "/". 281 | 282 | Third party packages 283 | - [[http://github.com/gorill/mux][gorilla mux]] 284 | - [[https://github.com/julienschmidt/httprouter][HttpRouter]] 285 | 286 | * A web server 287 | 288 | [[https://golang.org/doc/effective_go.html#web_server][Effective Go]] 289 | 290 | var addr = flag.String("addr", ":1718", "http service address") // Q=17, R=18 291 | 292 | var templ = template.Must(template.New("qr").Parse(templateStr)) 293 | 294 | func main() { 295 | flag.Parse() 296 | http.Handle("/", http.HandlerFunc(QR)) 297 | err := http.ListenAndServe(*addr, nil) 298 | if err != nil { 299 | log.Fatal("ListenAndServe:", err) 300 | } 301 | } 302 | 303 | func QR(w http.ResponseWriter, req *http.Request) { 304 | templ.Execute(w, req.FormValue("s")) 305 | } 306 | 307 | const templateStr = `... 308 | 309 | * Variety of techniques 310 | 311 | - TCP / UDP 312 | - HTTP / HTTP/2 313 | - Socket 314 | - RPC 315 | - REST 316 | 317 | * WebSockets 318 | 319 | - Only one TCP connection for a single web client. 320 | - WebSocket servers can push data to web clients. 321 | - Lightweight header to reduce data transmission overhead. 322 | 323 | .link https://astaxie.gitbooks.io/build-web-application-with-golang/en/08.2.html Build web application with Golang 324 | 325 | * REST 326 | 327 | REpresentational State Transfer 328 | 329 | - Every URI represents a resource. 330 | - There is a representation layer for transferring resources between clients and servers. 331 | - Clients use four HTTP methods to implement "Presentation Layer State Transfer", allowing them to operate on remote resources. 332 | 333 | * RPC 334 | 335 | RPC is based around the idea of defining a service, specifying the methods that can be called remotely with their parameters and return types. On the server side, the server implements this interface and runs a RPC server to handle client calls. On the client side, the client has a stub (referred to as just a client in some languages) that provides the same methods as the server. 336 | 337 | - Functions are exported (capitalized). 338 | - Functions must have two arguments with exported types. 339 | - The first argument is for receiving from the client, and the second one has to be a pointer and is for replying to the client. 340 | - Functions must have a return value of error type. 341 | 342 | func (t *T) MethodName(argType T1, replyType *T2) error 343 | 344 | * Not only existing protocol 345 | 346 | We are able to create a new ones. 347 | 348 | * net/textproto 349 | 350 | Package textproto implements generic support for text-based request/response protocols in the style of HTTP, NNTP, and SMTP. 351 | 352 | The package provides: 353 | - Error, which represents a numeric error response from a server. 354 | - Pipeline, to manage pipelined requests and responses in a client. 355 | - Reader, to read numeric response code lines, key: value headers, lines wrapped with leading spaces on continuation lines, and whole text blocks ending with a dot on a line by itself. 356 | - Writer, to write dot-encoded text blocks. 357 | - Conn, a convenient packaging of Reader, Writer, and Pipeline for use with a single network connection. 358 | 359 | * net/textproto Conn 360 | 361 | A Conn represents a textual network protocol connection. It consists of a Reader and Writer to manage I/O and a Pipeline to sequence concurrent requests on the connection. These embedded types carry methods with them; see the documentation of those types for details. 362 | 363 | type Conn struct { 364 | Reader 365 | Writer 366 | Pipeline 367 | // contains filtered or unexported fields 368 | } 369 | 370 | * net/textproto Cmd 371 | 372 | Cmd is a convenience method that sends a command after waiting its turn in the pipeline. The command text is the result of formatting format with args and appending \r\n. Cmd returns the id of the command, for use with StartResponse and EndResponse. 373 | 374 | For example, a client might run a HELP command that returns a dot-body by using: 375 | 376 | id, err := c.Cmd("HELP") 377 | if err != nil { 378 | return nil, err 379 | } 380 | 381 | c.StartResponse(id) 382 | defer c.EndResponse(id) 383 | 384 | if _, _, err = c.ReadCodeLine(110); err != nil { 385 | return nil, err 386 | } 387 | text, err := c.ReadDotBytes() 388 | if err != nil { 389 | return nil, err 390 | } 391 | return c.ReadCodeLine(250) 392 | 393 | * net/textproto Pipeline 394 | 395 | A Pipeline manages a pipelined in-order request/response sequence. 396 | 397 | To use a Pipeline p to manage multiple clients on a connection, each client should run: 398 | 399 | id := p.Next() // take a number 400 | 401 | p.StartRequest(id) // wait for turn to send request 402 | «send request» 403 | p.EndRequest(id) // notify Pipeline that request is sent 404 | 405 | p.StartResponse(id) // wait for turn to read response 406 | «read response» 407 | p.EndResponse(id) // notify Pipeline that response is read 408 | 409 | A pipelined server can use the same calls to ensure that responses computed in parallel are written in the correct order. 410 | 411 | * Network Summary 412 | 413 | - Standard packages in Go provide all necessaries for working with network 414 | - as client 415 | - as server 416 | - using TCP or UDP 417 | - using WebSockets 418 | - using REST 419 | - using RPC 420 | - using HTTP or HTTP/2 421 | - possability of control on each level 422 | - transport 423 | - client 424 | - listener 425 | - connection 426 | - server 427 | 428 | 429 | 430 | * Testing & Profiling 431 | 432 | * net/http/httptest 433 | 434 | Package httptest provides utilities for HTTP testing. [[https://golang.org/pkg/net/http/httptest][link]] 435 | 436 | Constants 437 | func NewRequest(method, target string, body io.Reader) *http.Request 438 | type ResponseRecorder 439 | func NewRecorder() *ResponseRecorder 440 | func (rw *ResponseRecorder) Flush() 441 | func (rw *ResponseRecorder) Header() http.Header 442 | func (rw *ResponseRecorder) Result() *http.Response 443 | func (rw *ResponseRecorder) Write(buf []byte) (int, error) 444 | func (rw *ResponseRecorder) WriteHeader(code int) 445 | func (rw *ResponseRecorder) WriteString(str string) (int, error) 446 | type Server 447 | func NewServer(handler http.Handler) *Server 448 | func NewTLSServer(handler http.Handler) *Server 449 | func NewUnstartedServer(handler http.Handler) *Server 450 | func (s *Server) Close() 451 | func (s *Server) CloseClientConnections() 452 | func (s *Server) Start() 453 | func (s *Server) StartTLS() 454 | 455 | * net/http/httptest ResponseRecorder 456 | 457 | handler := func(w http.ResponseWriter, r *http.Request) { 458 | http.Error(w, "something failed", http.StatusInternalServerError) 459 | } 460 | 461 | req := httptest.NewRequest("GET", "http://example.com/foo", nil) 462 | w := httptest.NewRecorder() 463 | handler(w, req) 464 | 465 | fmt.Printf("%d - %s", w.Code, w.Body.String()) 466 | 467 | * net/http/httptest Server 468 | 469 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 470 | fmt.Fprintln(w, "Hello, client") 471 | })) 472 | defer ts.Close() 473 | 474 | res, err := http.Get(ts.URL) 475 | if err != nil { 476 | log.Fatal(err) 477 | } 478 | greeting, err := ioutil.ReadAll(res.Body) 479 | res.Body.Close() 480 | if err != nil { 481 | log.Fatal(err) 482 | } 483 | 484 | fmt.Printf("%s", greeting 485 | 486 | * net/http/pprof 487 | 488 | Package pprof serves via its HTTP server runtime profiling data in the format expected by the pprof visualization tool. 489 | 490 | The package is typically only imported for the side effect of registering its HTTP handlers. The handled paths all begin with /debug/pprof/. 491 | 492 | To use pprof, link this package into your program: 493 | 494 | import _ "net/http/pprof" 495 | 496 | If your application is not already running an http server, you need to start one. Add "net/http" and "log" to your imports and the following code to your main function: 497 | 498 | go func() { 499 | log.Println(http.ListenAndServe("localhost:6060", nil)) 500 | }() 501 | 502 | * net/http/pprof how to use 503 | 504 | Then use the pprof tool to look at the heap profile: 505 | 506 | go tool pprof http://localhost:6060/debug/pprof/heap 507 | 508 | Or to look at a 30-second CPU profile: 509 | 510 | go tool pprof http://localhost:6060/debug/pprof/profile 511 | 512 | Or to look at the goroutine blocking profile, after calling runtime.SetBlockProfileRate in your program: 513 | 514 | go tool pprof http://localhost:6060/debug/pprof/block 515 | 516 | Or to collect a 5-second execution trace: 517 | 518 | wget http://localhost:6060/debug/pprof/trace?seconds=5 519 | 520 | To view all available profiles, open http://localhost:6060/debug/pprof/ in your browser. 521 | 522 | * Tasks 523 | 524 | 1. Create TCP Proxy server 525 | 2. Create HTTP Proxy server 526 | 3. Create simple CRUD server (with saving data in DB (PostgreSQL, MySQL, MongoDB)) 527 | 4. Create simple chat server on TCP base 528 | 529 | * Resources 530 | 531 | - [[http://thenewstack.io/understanding-golang-packages/][Understanding Golang Packages]] 532 | - [[https://golang.org/pkg/][Golang std pkg]] 533 | - [[https://golang.org/src][Golang std src]] 534 | - Alan A. A. Donovan and Brian W. Kernighan 'The Go Programming Language' 535 | - [[http://networkstatic.net/golang-network-ops/][Golang for Network Ops]] 536 | - [[http://www.golang-book.com/books/intro][An Introduction to Programming in Go]] 537 | - [[http://blog.scottlowe.org/2015/01/26/using-git-with-github/][Using Git with Github]] 538 | - [[http://www.minaandrawos.com/2016/05/14/udp-vs-tcp-in-golang/][UDP vs TCP in Go]] 539 | - [[https://astaxie.gitbooks.io/build-web-application-with-golang/en/03.1.html][Web App with Golang]] 540 | - [[http://soryy.com/blog/2014/not-another-go-net-http-tutorial/][Not Another Go/Golang net/http Tutorial]] 541 | 542 | 543 | * Questions? 544 | -------------------------------------------------------------------------------- /oop-in-go.slide: -------------------------------------------------------------------------------- 1 | Object Oriented Principles in Go 2 | 26 Apr 2017 3 | 4 | Ivan Kutuzov 5 | ikut@softserveinc.com 6 | https://discuss.7insyde.com 7 | https://golang.org.ua 8 | @arbrix 9 | 10 | * License and Materials 11 | 12 | This presentation is licensed under the [[https://creativecommons.org/licenses/by-sa/4.0/][Creative Commons Attribution-ShareAlike 4.0 International]] licence. 13 | 14 | The materials for this presentation are available on GitHub: 15 | 16 | .link https://github.com/GolangUA/go-training 17 | 18 | You are encouraged to remix, transform, or build upon the material, providing you give appropriate credit and distribute your contributions under the same license. 19 | 20 | If you have suggestions or corrections to this presentation, please raise [[https://github.com/GolangUA/go-training/issues][an issue on the GitHub project]]. 21 | 22 | * Agenda 23 | 24 | - Recap / Homework 25 | - Basic Principles 26 | - Golang difference 27 | - SOLID 28 | 29 | * Previous Sessions 30 | 31 | * On the previous session 32 | 33 | - Who use Go 34 | - Imperative programs 35 | - Go Environment (Workspace) 36 | - Go Operators 37 | - Basic Syntax 38 | - Advanced Syntax 39 | - Homework 40 | 41 | * The Go programming language 42 | 43 | - Modern 44 | - Compact, concise, general-purpose 45 | - Imperative, statically type-checked, dynamically type-safe 46 | - Garbage-collected 47 | - Compiles to native code, statically linked 48 | - Fast compilation, efficient execution 49 | 50 | * Go Operators 51 | 52 | - Arithmetic Operators 53 | - Relational Operators 54 | - Logical Operators 55 | - Bitwise Operators 56 | - Assignment Operators 57 | - Misc Operators 58 | 59 | [[https://www.tutorialspoint.com/go/go_operators.htm][Go Operators]] 60 | 61 | * Basic Syntax 62 | 63 | - Declarations 64 | - Loops and conditions 65 | - Control Flow 66 | - User Types 67 | - User Function 68 | - Packages 69 | 70 | * Advanced Syntax 71 | 72 | - How Go code is formatted. 73 | - How what the zero value is and how it works. 74 | - Equality and type conversions 75 | - The short declaration syntax 76 | - Multiple assignment. 77 | - How slices, maps work. 78 | - How the `fmt` package works. 79 | - Scope 80 | - Struct, Pointers, Nil 81 | - Interfaces 82 | - Defer 83 | - Documenting 84 | 85 | * Homework 86 | 87 | * Object Oriented Principles 88 | 89 | * Object Oriented Principles 90 | 91 | Software design is about representation: how do we represent the solution to a problem in code that can be executed on the machine of our choice? How do we represent the problem domain to the user? 92 | 93 | Creating a software representation that can be usefully viewed as isomorphic with some aspect of the real world is a common software design task. 94 | 95 | .link https://www.activestate.com/blog/2017/03/go-object-oriented-developers source 96 | 97 | * Object Oriented Principles 98 | 99 | Object-oriented programming (OOP) is a programming paradigm based on the concept of "objects", which may contain data, in the form of fields, often known as attributes; and code, in the form of procedures, often known as methods. A feature of objects is that an object's procedures can access and often modify the data fields of the object with which they are associated. 100 | 101 | .link https://en.wikipedia.org/wiki/Object-oriented_programming source 102 | 103 | Object orientation is an approach to software development in which we focus on objects and their attributes and responsibilities. All business problems involve things. These things can be mapped to a set of objects, such as customers, invoices, etc. In any given business problem the various things have attributes like name, color, weight, etc and they have responsibilities such as initiate and order, fulfill an order, pay an invoice, etc. 104 | 105 | .link http://www.jamesbooth.com/OOPBasics.htm source 106 | 107 | * Object Oriented Principles 108 | 109 | - Abstraction 110 | - Specialization 111 | - Encapsulation 112 | - Inheritance 113 | - Polymorphism 114 | 115 | * Attribution 116 | 117 | This is the ability to represent a complex problem in simple terms. In the object oriented approach it is seen in the ability to create a high level class definition that has little or no detail included in it. We are able to “abstract” the problem to a simple class definition and only add detail alter in the process. 118 | 119 | The ability to support abstraction is instrumental in being able to model very complex business problems in our designs. 120 | 121 | * Specialization 122 | 123 | Seen in class structures, specialization is the ability to make lower level subclasses more detailed than their parent classes. Technically, specialization is defined as the ability of an object to inherit operations and attributes from a superclass (parent class) with possible restrictions and additions. 124 | 125 | * Encapsulation 126 | 127 | Defined as hiding the implementation of the object, encapsulation is really the process of making an object as self sufficient as is possible. An object contains not only code but also data. Encapsulation, in the structured design, is the process of enclosing all of the code necessary to a particular operation into a single module and limiting the external dependencies of that module. 128 | 129 | - There are two levels of access - within the package alone, and public. 130 | - tarts with a capital letter it is exported outside the package and is public. If instead it starts with a small letter, it is visible only within the package. 131 | - Exported/public items: MyStruct, MyMethod, MyField 132 | - Items with package visibility: myStruct, myMethod, myField 133 | - You can tie in methods/behavior to a type by defining functions associated with it. func (m my_type) my_func() int { } 134 | - You cannot attach methods to a type if it is not defined in the local package. 135 | 136 | * Inheritance 137 | 138 | Just as you and I have inherited certain traits from our ancestors, so do classes in an object oriented system. There are superclasses (classes that have subclasses) and there are subclasses (classes that inherit form another class). These terms, superclass and subclass, are relative in that a given class may be a subclass of one class and a superclass for another. 139 | 140 | _Inheritance_: the ability for one type to obtain the features of a type above it in a hierarchy. For Go, some of the salient features are: 141 | 142 | - Inheritance is obtained through anonymous fields - anonymous fields appear to attach its behavior to the composing class. 143 | - Both data fields and methods are available to derived types. Outside of the package, only the types, fields, and methods named with a starting capital letter is inherited. Inside a package, everything is inherited. 144 | - Multiple inheritance is possible - by including an anonymous field of each of the parent types. `type Child struct { Father; Mother }` 145 | 146 | * Polymorphism 147 | 148 | _Polymorphism_: when a type seems to exhibit different behaviors when linked to different instances, the type can be said to exhibit polymorphism. 149 | 150 | - Interfaces in Go can be used to implement polymorphism. A variable of a type can be assigned to a variable of any interface it implements. 151 | 152 | * Representation in Go 153 | 154 | Golang makes a number of design choices that distinguish it from other languages and make it a flexible, powerful, and relatively safe tool for representing a wide range of things. 155 | 156 | - ability to add methods to any type except primitives 157 | - interfaces 158 | - structs 159 | 160 | * Methods in Go 161 | 162 | Go is a very strongly typed language, to the extent that even ints and floats won't interoperate without explicit casting. Types have a number of different aspects, however, that make them very flexible. 163 | 164 | In Go, because methods are not part of the type and can be defined anywhere, a separate discipline must be practiced to ensure that the methods (verbs) we define on types (nouns) add up to a more-or-less coherent representation of whatever aspect of the world it is that is being represented by the type. 165 | 166 | .link https://www.activestate.com/blog/2017/03/go-object-oriented-developers Methods in Go 167 | 168 | * Interfaces 169 | 170 | The next interesting bit of the language is interfaces, which have two roles in Go: enforcing duck-like typing at compile time and allowing dynamic dispatch. 171 | 172 | Duck typing is the Pythonic concept that if it walks and talks like a duck, it is a duck: if an object has an interface that conforms to something, it is a thing of that type. 173 | 174 | This might also be called "syntactical typing", as all that matters is the syntax of the call. 175 | 176 | The problem with the Pythonic way of doing things is that there is no way of telling if a type conforms to an interface until it doesn't. 177 | 178 | Interfaces allow Go programs to be checked at compile time to see if an object has all the methods required for a given interface, without further constraining the type. 179 | 180 | * Structs 181 | 182 | As the name suggests, a struct is more like a C struct than a C++ class. Structs allow data to be grouped together in convenient representational form, and permit a limited form of inheritance. 183 | 184 | 185 | Structs take subtypes by declaring anonymous members rather than an explicit inheritance syntax. 186 | 187 | Go simply recognizes anonymous members at the start of a struct declaration as base types for the struct, and allows the fields of the base types to be accessed directly. 188 | 189 | 190 | * OOP in Golang 191 | 192 | - Interface Contract 193 | - Interface Type 194 | - Interface Satisfaction 195 | - Interface Value 196 | - Type Assertation 197 | - Type Switches 198 | - Interface with Nil Pointer is Non-Nil 199 | 200 | * Interface with Nil Pointer is Non-Nil 201 | 202 | A nil interface value, which contains no value at all, is not the same as an interface value con- taining a pointer that happens to be nil. This subtle distinction creates a trap into which every Go programmer has stumbled. 203 | 204 | const debug = true 205 | func main() { 206 | var buf *bytes.Buffer 207 | if debug { 208 | buf = new(bytes.Buffer) // enable collection of output 209 | } 210 | f(buf) // NOTE: subtly incorrect! 211 | } 212 | // If out is non-nil, output will be written to it. 213 | func f(out io.Writer) { 214 | // ...do something... 215 | if out != nil { 216 | out.Write([]byte("done!\n")) 217 | } 218 | } 219 | 220 | * How to fix that? 221 | 222 | * Use Interface for avoiding the assigment 223 | 224 | var buf io.Writer 225 | if debug { 226 | buf = new(bytes.Buffer) // enable collection of output 227 | } 228 | f(buf) // OK 229 | 230 | * Type Conversion 231 | 232 | func sum(f float32) int32 { 233 | return int32(f) + 10 234 | } 235 | 236 | * Type Assertation 237 | 238 | func sum(i interface{}) int32 { 239 | a, ok := i.(int32) 240 | if !ok { 241 | return errors.New("parameter should have int32 type") 242 | } 243 | return a + 10 244 | } 245 | 246 | * Type Switching 247 | 248 | func (var interface{}) { 249 | switch v := var.(type) { 250 | case string: 251 | s, _ := strconv.Atoi(v) 252 | fmt.Println("sum: %d", s + 10) 253 | case int: 254 | fmt.Println("%d", v+10) 255 | } 256 | } 257 | 258 | * Object-Oriented Programming in Golang 259 | 260 | .link http://golangtutorials.blogspot.com/2011/06/structs-in-go-instead-of-classes-in.html Struct in Go 261 | .link http://golangtutorials.blogspot.com/2011/06/anonymous-fields-in-structs-like-object.html Anonymous fields in structs - like object composition 262 | .link http://golangtutorials.blogspot.com/2011/06/methods-on-structs.html Methods on structs 263 | .link http://golangtutorials.blogspot.com/2011/06/inheritance-and-subclassing-in-go-or.html Inheritance and subclassing in Go 264 | .link http://golangtutorials.blogspot.com/2011/06/interfaces-in-go.html Interfaces in Go 265 | .link http://golangtutorials.blogspot.com/2011/06/polymorphism-in-go.html Polymorphism in Go 266 | .link https://code.tutsplus.com/tutorials/lets-go-object-oriented-programming-in-golang--cms-26540 Object-Oriented Programming in Golang 267 | 268 | * SOLID 269 | 270 | In 2002 Robert Martin published his book, [[https://www.amazon.co.uk/dp/0135974445/ref=pd_lpo_sbs_dp_ss_2/253-1946330-6751666?pf_rd_m=A3P5ROKL5A1OLE&pf_rd_s=lpo-top-stripe&pf_rd_r=23C4AHYV7EXGYHKD6G8Q&pf_rd_t=201&pf_rd_p=569136327&pf_rd_i=0132760584][Agile Software Development, Principles, Patterns, and Practices.]] In it he described five principles of reusable software design, which he called the SOLID principles, after the first letters in their names. 271 | 272 | - Single Responsibility Principle 273 | - Open / Closed Principle 274 | - Liskov Substitution Principle 275 | - Interface Segregation Principle 276 | - Dependency Inversion Principle 277 | 278 | * Single Responsibility Principle 279 | 280 | A class should have one, and only one, reason to change. 281 | –Robert C Martin 282 | 283 | Now Go obviously doesn’t have classes—instead we have the far more powerful notion of composition—but if you can look past the use of the word class. 284 | 285 | Code that has a single responsibility therefore has the fewest reasons to change. 286 | 287 | Doug McIlroy’s Unix philosophy; small, sharp tools which combine to solve larger tasks, oftentimes tasks which were not envisioned by the original authors. 288 | 289 | * Open / Closed Principle 290 | 291 | Software entities should be open for extension, but closed for modification. 292 | –Bertrand Meyer, Object-Oriented Software Construction 293 | 294 | Embedding isn’t just for methods, it also provides access to an embedded type’s fields. As you see, because both `A` and `B` are defined in the same package, `B` can access `A`‘s private year field as if it were declared inside `B`. 295 | 296 | So embedding is a powerful tool which allows Go’s types to be open for extension. 297 | 298 | .link https://dave.cheney.net/2016/08/20/solid-go-design source 299 | 300 | * Liskov Substitution Principle 301 | 302 | Coined by Barbara Liskov, the Liskov substitution principle states, roughly, that two types are substitutable if they exhibit behaviour such that the caller is unable to tell the difference. 303 | 304 | In a class based language, Liskov’s substitution principle is commonly interpreted as a specification for an abstract base class with various concrete subtypes. But Go does not have classes, or inheritance, so substitution cannot be implemented in terms of an abstract class hierarchy. 305 | 306 | Require no more, promise no less. 307 | –Jim Weirich 308 | 309 | * Interface Segregation Principle 310 | 311 | Clients should not be forced to depend on methods they do not use. 312 | –Robert C. Martin 313 | 314 | The application of the interface segregation principle can refer to a process of isolating the behaviour required for a function to do its job. 315 | pplying the interface segregation principle to our Save function, the results has simultaneously been a function which is the most specific in terms of its requirements–it only needs a thing that is writable–and the most general in its function, we can now use Save to save our data to anything which implements io.Writer. 316 | 317 | A great rule of thumb for Go is accept interfaces, return structs. 318 | –Jack Lindamood 319 | 320 | * Dependency Inversion Principle 321 | 322 | High-level modules should not depend on low-level modules. Both should depend on abstractions. 323 | Abstractions should not depend on details. Details should depend on abstractions. 324 | –Robert C. Martin 325 | 326 | If you’ve applied all the principles we’ve talked about up to this point then your code should already be factored into discrete packages, each with a single well defined responsibility or purpose. Your code should describe its dependencies in terms of interfaces, and those interfaces should be factored to describe only the behaviour those functions require. In other words, there shouldn’t be much left to do. 327 | 328 | So what I think Martin is talking about here, certainly the context of Go, is the structure of your import graph. 329 | 330 | In Go, your import graph must be acyclic. A failure to respect this acyclic requirement is grounds for a compilation failure, but more gravely represents a serious error in design. 331 | 332 | * Recap 333 | 334 | - Object Oriented Principle 335 | - Struct, Interface 336 | - SOLID 337 | 338 | * Questions? 339 | 340 | * Resources 341 | 342 | .link https://www.activestate.com/blog/2017/03/go-object-oriented-developers GO FOR OBJECT-ORIENTED DEVELOPERS 343 | .link https://en.wikipedia.org/wiki/Object-oriented_programming Wiki 344 | .link http://www.golangbootcamp.com/book Golang Bootcamp 345 | .link https://www.youtube.com/watch?v=zzAdEt3xZ1M SOLID by Dave Cheney (Golang UK Conference 2016) 346 | .link https://dave.cheney.net/2016/08/20/solid-go-design SOLID Go Design by Dave Cheney 347 | .link https://code.tutsplus.com/tutorials/lets-go-object-oriented-programming-in-golang--cms-26540 Object Oriented Programming in Golang 348 | .link https://www.goinggo.net/2013/07/object-oriented-programming-in-go.html Object Oriented Programming in Go 349 | 350 | 351 | .link https://golang.org/doc/effective_go.html Effective Go 352 | .link https://gobyexample.com/ Go by Example 353 | .link https://miek.nl/go/ LEARNING GO online book 354 | .link https://habrahabr.ru/hub/go/ Habrahabr 355 | -------------------------------------------------------------------------------- /performance.slide: -------------------------------------------------------------------------------- 1 | Performance in Go 2 | Lviv, UA 3 | 18 May 2017 4 | 5 | Ivan Kutuzov 6 | http://golang.org.ua/ 7 | http://golang-ua.slack.com/ 8 | http://gophers.in.ua/ 9 | @arbrix 10 | 11 | * License and Materials 12 | 13 | This presentation is licensed under the [[https://creativecommons.org/licenses/by-sa/4.0/][Creative Commons Attribution-ShareAlike 4.0 International]] licence. 14 | 15 | This materials was prepared by Dave Cheney. 16 | 17 | The materials for this presentation are available on GitHub: 18 | 19 | .link https://github.com/davecheney/high-performance-go-workshop 20 | 21 | You are encouraged to remix, transform, or build upon the material, providing you give appropriate credit and distribute your contributions under the same license. 22 | 23 | If you have suggestions or corrections to this presentation, please raise [[https://github.com/davecheney/high-performance-go-workshop/isues][an issue on the GitHub project]]. 24 | 25 | * Agenda 26 | 27 | Today we are going to cover three areas: 28 | 29 | - Recap 30 | - What does performance mean, what is possible? 31 | - Benchmarking 32 | - Performance measurement and profiling 33 | 34 | * Recap 35 | 36 | We need more practice 37 | .link http://adventofcode.com 38 | .link http://gocode.io/ 39 | .link https://www.codingame.com/ 40 | .link https://coderbyte.com/ 41 | .link http://users.csc.calpoly.edu/~jdalbey/103/Projects/ProgrammingPractice.html 42 | .link http://www.w3resource.com/c-programming-exercises/ 43 | .link http://www.codeabbey.com/index/task_list 44 | 45 | * Tasks 46 | 47 | - Elementary 48 | - Array, Strings 49 | - Intermediate 50 | - Advance 51 | 52 | * What does performance mean? 53 | 54 | Before we talk about writing high performance code, we need to talk about the hardware that will execute this code. 55 | 56 | What are its properties and how have they changed over time? 57 | 58 | As software authors we have benefited from Moore's Law, the doubling of the number of available transistors on a chip every 18 months, for 50 years. 59 | 60 | No other industry has experienced a _six_order_of_magnitude_ improvement in their tools in the space of a lifetime. 61 | 62 | * Network and disk I/O are still expensive 63 | 64 | Network and disk I/O are still expensive, so expensive that the Go runtime will schedule something else while those operations are in progress. 65 | 66 | .image images/media-20160803.jpg 67 | 68 | * Benchmarking 69 | 70 | * Benchmarking 71 | 72 | Before you can begin to tune your application, you need to establish a reliable baseline to measure the impact of your change to know if you're making things better, or worse. 73 | 74 | In other words, _"Don't_guess,_measure"_ 75 | 76 | This section focuses on how to construct useful benchmarks using the Go testing framework, and gives practical tips for avoiding the pitfalls. 77 | 78 | Benchmarking is closely related to profiling, which we'll touch on during this section, then cover it in detail in the next. 79 | 80 | * Benchmarking ground rules 81 | 82 | Before you benchmark, you must have a stable environment to get repeatable results. 83 | 84 | - The machine must be idle—don't profile on shared hardware, don't browse the web while waiting for a long benchmark to run. 85 | - Watch out for power saving and thermal scaling. 86 | - Avoid virtual machines and shared cloud hosting; they are too noisy for consistent measurements. 87 | 88 | If you can afford it, buy dedicated performance test hardware. Rack it, disable all the power management and thermal scaling and never update the software on those machines. 89 | 90 | For everyone else, have a before and after sample and run them multiple times to get consistent results. 91 | 92 | * Using the testing package for benchmarking 93 | 94 | The `testing` package has built in support for writing benchmarks. 95 | 96 | // Fib computes the n'th number in the Fibonacci series. 97 | func Fib(n int) int { 98 | if n < 2 { 99 | return n 100 | } 101 | return Fib(n-1) + Fib(n-2) 102 | } 103 | .caption fib.go 104 | 105 | func BenchmarkFib(b *testing.B) { 106 | for n := 0; n < b.N; n++ { 107 | Fib(20) // run the Fib function b.N times 108 | } 109 | } 110 | .caption fib_test.go 111 | 112 | DEMO: `go`test`-bench=.`./examples/fib` 113 | 114 | * How benchmarks work 115 | 116 | Each benchmark is run `b.N` times until it takes longer than 1 second. 117 | 118 | `b.N` starts at 1, if the benchmark completes in under 1 second `b.N` is increased and the benchmark run again. 119 | 120 | `b.N` increases in the approximate sequence; 1, 2, 3, 5, 10, 20, 30, 50, 100, ... 121 | 122 | % go test -bench=. ./examples/fib 123 | BenchmarkFib-4 30000 46408 ns/op 124 | PASS 125 | ok _/Users/dfc/devel/high-performance-go-workshop/examples/fib 1.910s 126 | 127 | _Beware:_ below the μs mark you will start to see the relativistic effects of instruction reordering and code alignment. 128 | 129 | - Run benchmarks longer to get more accuracy; `go`test`-benchtime=10s` 130 | - Run benchmarks multiple times; `go`test`-count=10` 131 | 132 | _Tip:_ If this is required, codify it in a `Makefile` so everyone is comparing apples to apples. 133 | 134 | # Setups costs usually amortized. Reset timer better than stop and start timer. 135 | 136 | * Comparing benchmarks 137 | 138 | For repeatable results, you should run benchmarks multiple times. 139 | 140 | You can do this manually, or use the `-count=` flag. 141 | 142 | Determining the performance delta between two sets of benchmarks can be tedious and error prone. 143 | 144 | Tools like [[https://godoc.org/rsc.io/benchstat][rsc.io/benchstat]] are useful for comparing results. 145 | 146 | % go test -c 147 | % mv fib.test fib.golden 148 | 149 | DEMO: Improve `Fib` 150 | 151 | % go test -c 152 | % ./fib.golden -test.bench=. -test.count=5 > old.txt 153 | % ./fib.test -test.bench=. -test.count=5 > new.txt 154 | % benchstat old.txt new.txt 155 | 156 | DEMO: `benchstat`{old,new}.txt` 157 | 158 | * Avoid benchmarking start up costs 159 | 160 | Sometimes your benchmark has a once per run setup cost. `b.ResetTimer()` will can be used to ignore the time accrued in setup. 161 | 162 | func BenchmarkExpensive(b *testing.B) { 163 | boringAndExpensiveSetup() 164 | b.ResetTimer() // HL 165 | for n := 0; n < b.N; n++ { 166 | // function under test 167 | } 168 | } 169 | 170 | If you have some expensive setup logic _per_loop_iteration, use `b.StopTimer()` and `b.StartTimer()` to pause the benchmark timer. 171 | 172 | func BenchmarkComplicated(b *testing.B) { 173 | for n := 0; n < b.N; n++ { 174 | b.StopTimer() // HL 175 | complicatedSetup() 176 | b.StartTimer() // HL 177 | // function under test 178 | } 179 | } 180 | 181 | * Benchmarking allocations 182 | 183 | Allocation count and size is strongly correlated with benchmark time. 184 | 185 | You can tell the `testing` framework to record the number of allocations made by code under test with 186 | 187 | func BenchmarkRead(b *testing.B) { 188 | b.ReportAllocs() // HL 189 | for n := 0; n < b.N; n++ { 190 | // function under test 191 | } 192 | } 193 | 194 | DEMO: `go`test`-run=^$`-bench=.`bufio` 195 | 196 | _Note:_ you can also use the `go`test`-benchmem` flag to do the same for _all_ benchmarks. 197 | 198 | DEMO: `go`test`-run=^$`-bench=.`-benchmem`bufio` 199 | 200 | * Watch out for compiler optimisations 201 | 202 | This example comes from [[https://github.com/golang/go/issues/14813#issue-140603392][issue 14813]]. How fast will this function benchmark? 203 | 204 | const m1 = 0x5555555555555555 205 | const m2 = 0x3333333333333333 206 | const m4 = 0x0f0f0f0f0f0f0f0f 207 | const h01 = 0x0101010101010101 208 | 209 | func popcnt(x uint64) uint64 { 210 | x -= (x >> 1) & m1 211 | x = (x & m2) + ((x >> 2) & m2) 212 | x = (x + (x >> 4)) & m4 213 | return (x * h01) >> 56 214 | } 215 | 216 | func BenchmarkPopcnt(b *testing.B) { 217 | for i := 0; i < b.N; i++ { 218 | popcnt(uint64(i)) 219 | } 220 | } 221 | 222 | * What happened? 223 | 224 | % go test -bench=. ./examples/popcnt 225 | BenchmarkPopcnt-8 2000000000 0.29 ns/op 226 | PASS 227 | ok github.com/davecheney/high-performance-go-workshop/examples/popcnt 0.625s 228 | 229 | `popcnt` is a leaf function, so the compiler can inline it. 230 | 231 | Because the function is inlined, the compiler can see it has no side effects, so the call is eliminated. This is what the compiler sees: 232 | 233 | func BenchmarkPopcnt(b *testing.B) { 234 | for i := 0; i < b.N; i++ { 235 | // optimised away 236 | } 237 | } 238 | 239 | The same optimisations that make real code fast, by removing unnecessary computation, are the same ones that remove benchmarks that have no observable side effects. 240 | 241 | This is only going to get more common as the Go compiler improves. 242 | 243 | * Benchmark mistakes 244 | 245 | The `for` loop is crucial to the operation of the benchmark. 246 | 247 | Here are two incorrect benchmarks, can you explain what is wrong with them? 248 | 249 | func Fib(n int) int { 250 | a, b := 0, 1 251 | for i := 0; i < n; i++ { 252 | a, b = b, a+b 253 | } 254 | return a 255 | } 256 | 257 | func BenchmarkFibWrong(b *testing.B) { 258 | Fib(b.N) 259 | } 260 | 261 | func BenchmarkFibWrong2(b *testing.B) { 262 | for n := 0; n < b.N; n++ { 263 | Fib(n) 264 | } 265 | } 266 | 267 | * Discussion 268 | 269 | Are there any questions? 270 | 271 | * Performance measurement and profiling 272 | 273 | * Performance measurement and profiling 274 | 275 | In the previous section we studied how to measure the performance of programs from the outside. 276 | 277 | In this section we'll use profiling tools built into Go to investigate the operation of the program from the inside. 278 | 279 | Don't trade performance for reliability 280 | 281 | * pprof 282 | 283 | The primary tool we're going to be talking about today is _pprof_. 284 | 285 | [[https://github.com/google/pprof][pprof]] descends from the [[https://github.com/gperftools/gperftools][Google Perf Tools]] suite of tools. 286 | 287 | `pprof` profiling is built into the Go runtime. 288 | 289 | It consists of two parts: 290 | 291 | - `runtime/pprof` package built into every Go program 292 | - `go`tool`pprof` for investigating profiles. 293 | 294 | pprof supports several types of profiling, we'll discuss three of these today: 295 | 296 | - CPU profiling. 297 | - Memory profiling. 298 | - Block (or blocking) profiling. 299 | 300 | * CPU profiling 301 | 302 | CPU profiling is the most common type of profile, and the most obvious. 303 | 304 | When CPU profiling is enabled the runtime will interrupt itself every 10ms and record the stack trace of the currently running goroutines. 305 | 306 | Once the profile is complete we can analyse it to determine the hottest code paths. 307 | 308 | The more times a function appears in the profile, the more time that code path is taking as a percentage of the total runtime. 309 | 310 | * Memory profiling 311 | 312 | Memory profiling records the stack trace when a _heap_ allocation is made. 313 | 314 | Stack allocations are assumed to be free and are _not_tracked_ in the memory profile. 315 | 316 | Memory profiling, like CPU profiling is sample based, by default memory profiling samples 1 in every 1000 allocations. This rate can be changed. 317 | 318 | Because of memory profiling is sample based and because it tracks _allocations_ not _use_, using memory profiling to determine your application's overall memory usage is difficult. 319 | 320 | Block profiling is quite unique. 321 | 322 | A block profile is similar to a CPU profile, but it records the amount of time a goroutine spent waiting for a shared resource. 323 | 324 | This can be useful for determining _concurrency_ bottlenecks in your application. 325 | 326 | Block profiling can show you when a large number of goroutines _could_ make progress, but were _blocked_. Blocking includes: 327 | 328 | - Sending or receiving on a unbuffered channel. 329 | - Sending to a full channel, receiving from an empty one. 330 | - Trying to `Lock` a `sync.Mutex` that is locked by another goroutine. 331 | 332 | Block profiling is a very specialised tool, it should not be used until you believe you have eliminated all your CPU and memory usage bottlenecks. 333 | 334 | * One profile at at time 335 | 336 | Profiling is not free. 337 | 338 | Profiling has a moderate, but measurable impact on program performance—especially if you increase the memory profile sample rate. 339 | 340 | Most tools will not stop you from enabling multiple profiles at once. 341 | 342 | If you enable multiple profile's at the same time, they will observe their own interactions and throw off your results. 343 | 344 | *Do*not*enable*more*than*one*kind*of*profile*at*a*time.* 345 | 346 | * Using pprof 347 | 348 | [[https://www.youtube.com/watch?v=xxDZuPEgbBU&t=2533s][Profiling & Optimizing in Go / Brad Fitzpatrick]] 349 | Now that I've talked about what pprof can measure, I will talk about how to use pprof to analyse a profile. 350 | 351 | pprof should always be invoked with _two_ arguments. 352 | 353 | go tool pprof /path/to/your/binary /path/to/your/profile 354 | 355 | The `binary` argument *must* be the binary that produced this profile. 356 | 357 | The `profile` argument *must* be the profile generated by this binary. 358 | 359 | *Warning*: Because pprof also supports an online mode where it can fetch profiles from a running application over http, the pprof tool can be invoked without the name of your binary ([[https://github.com/golang/go/issues/10863][issue 10863]]): 360 | 361 | go tool pprof /tmp/c.pprof 362 | 363 | *Do*not*do*this*or*pprof*will*report*your*profile*is*empty.* 364 | 365 | * Using pprof (cont.) 366 | 367 | This is a sample CPU profile: 368 | 369 | % go tool pprof $BINARY /tmp/c.p 370 | Entering interactive mode (type "help" for commands) 371 | (pprof) top 372 | Showing top 15 nodes out of 63 (cum >= 4.85s) 373 | flat flat% sum% cum cum% 374 | 21.89s 9.84% 9.84% 128.32s 57.71% net.(*netFD).Read 375 | 17.58s 7.91% 17.75% 40.28s 18.11% runtime.exitsyscall 376 | 15.79s 7.10% 24.85% 15.79s 7.10% runtime.newdefer 377 | 12.96s 5.83% 30.68% 151.41s 68.09% test_frame/connection.(*ServerConn).readBytes 378 | 11.27s 5.07% 35.75% 23.35s 10.50% runtime.reentersyscall 379 | 10.45s 4.70% 40.45% 82.77s 37.22% syscall.Syscall 380 | 9.38s 4.22% 44.67% 9.38s 4.22% runtime.deferproc_m 381 | 9.17s 4.12% 48.79% 12.73s 5.72% exitsyscallfast 382 | 8.03s 3.61% 52.40% 11.86s 5.33% runtime.casgstatus 383 | 7.66s 3.44% 55.85% 7.66s 3.44% runtime.cas 384 | 7.59s 3.41% 59.26% 7.59s 3.41% runtime.onM 385 | 6.42s 2.89% 62.15% 134.74s 60.60% net.(*conn).Read 386 | 6.31s 2.84% 64.98% 6.31s 2.84% runtime.writebarrierptr 387 | 6.26s 2.82% 67.80% 32.09s 14.43% runtime.entersyscall 388 | 389 | Often this output is hard to understand. 390 | 391 | * Using pprof (cont.) 392 | 393 | A better way to understand your profile is to visualise it. 394 | 395 | % go tool pprof application /tmp/c.p 396 | Entering interactive mode (type "help" for commands) 397 | (pprof) web 398 | 399 | Opens a web page with a graphical display of the profile. 400 | 401 | .link images/profile.svg 402 | 403 | _Note_: visualisation requires graphviz. 404 | 405 | I find this method to be superior to the text mode, I strongly recommend you try it. 406 | 407 | pprof also supports these modes in a non interactive form with flags like `-svg`, `-pdf`, etc. See `go`tool`pprof`-help` for more details. 408 | 409 | .link http://blog.golang.org/profiling-go-programs Further reading: Profiling Go programs 410 | .link https://software.intel.com/en-us/blogs/2014/05/10/debugging-performance-issues-in-go-programs Further reading: Debugging performance issues in Go programs 411 | 412 | * Using pprof (cont.) 413 | 414 | The output of a memory profile can be similarly visualised. 415 | 416 | % go build -gcflags='-memprofile=/tmp/m.p' 417 | % go tool pprof --alloc_objects -svg $(go tool -n compile) /tmp/m.p > alloc_objects.svg 418 | % go tool pprof --inuse_objects -svg $(go tool -n compile) /tmp/m.p > inuse_objects.svg 419 | 420 | Memory profiles come in two varieties 421 | 422 | - Alloc objects reports the call site where each allocation was made 423 | 424 | .link images/alloc_objects.svg 425 | 426 | - Inuse objects reports the call site where an allocation was made _iff_ it was reachable at the end of the profile 427 | 428 | .link images/inuse_objects.svg 429 | 430 | * Using pprof (cont.) 431 | 432 | Here is a visualisation of a block profile: 433 | 434 | % go test -run=XXX -bench=ClientServer -blockprofile=/tmp/b.p net/http 435 | % go tool pprof -svg http.test /tmp/b.p > block.svg 436 | 437 | .link images/block.svg 438 | 439 | * Profiling benchmarks 440 | 441 | The `testing` package has built in support for generating CPU, memory, and block profiles. 442 | 443 | - `-cpuprofile=$FILE` writes a CPU profile to `$FILE`. 444 | - `-memprofile=$FILE`, writes a memory profile to `$FILE`, `-memprofilerate=N` adjusts the profile rate to `1/N`. 445 | - `-blockprofile=$FILE`, writes a block profile to `$FILE`. 446 | 447 | Using any of these flags also preserves the binary. 448 | 449 | % go test -run=XXX -bench=. -cpuprofile=c.p bytes 450 | % go tool pprof bytes.test c.p 451 | 452 | _Note:_ use `-run=XXX` to disable tests, you only want to profile benchmarks. You can also use `-run=^$` to accomplish the same thing. 453 | 454 | * Profiling applications 455 | 456 | Profiling `testing` benchmarks is useful for _microbenchmarks_. 457 | 458 | We use microbenchmarks inside the standard library to make sure individual packages do not regress, but what if you want to profile a complete application? 459 | 460 | The Go runtime's profiling interface is in the `runtime/pprof` package. 461 | 462 | `runtime/pprof` is a very low level tool, and for historic reasons the interfaces to the different kinds of profile are not uniform. 463 | 464 | A few years ago I wrote a small package, [[https://github.com/pkg/profile][github.com/pkg/profile]], to make it easier to profile an application. 465 | 466 | import "github.com/pkg/profile" 467 | 468 | func main() { 469 | defer profile.Start().Stop() 470 | ... 471 | } 472 | 473 | * Framepointers 474 | 475 | Go 1.7 has been released and along with a new compiler for amd64, the compiler now enables frame pointers by default. 476 | 477 | The frame pointer is a register that always points to the top of the current stack frame. 478 | 479 | Framepointers enable tools like `gdb(1)`, and `perf(1)` to understand the Go call stack. 480 | 481 | Further reading: 482 | 483 | .link talks.godoc.org/github.com/davecheney/presentations/seven.slide Seven ways to profile a Go program (slides) 484 | .link https://www.youtube.com/watch?v=2h_NFBFrciI Video (30 mins) 485 | .link https://www.bigmarker.com/remote-meetup-go/Seven-ways-to-profile-a-Go-program Recording (60 mins) 486 | 487 | * Always use the latest released version of Go 488 | 489 | Old versions of Go will never get better. They will never get bug fixes or optimisations. 490 | 491 | - Go 1.4 should not be used. 492 | - Go 1.5 and 1.6 had a slower compiler, but it produces faster code, and has a faster GC. 493 | - Go 1.7 delivered roughly a 30% improvement in compilation speed over 1.6, a 2x improvement in linking speed (better than any previous version of Go). 494 | - Go 1.8 will deliver a smaller improvement in compilation speed (at this point), but a significant improvement in code quality for non Intel architectures. 495 | 496 | Old version of Go receive no updates. Do not use them. Use the latest and you will get the best performance. 497 | 498 | .link http://dave.cheney.net/2016/04/02/go-1-7-toolchain-improvements Go 1.7 toolchain improvements 499 | .link http://dave.cheney.net/2016/09/18/go-1-8-performance-improvements-one-month-in Go 1.8 performance improvements 500 | 501 | * Conclusion 502 | 503 | * Always write the simplest code you can 504 | 505 | Start with the simplest possible code. 506 | 507 | Measure. 508 | 509 | If performance is good, _stop_. You don't need to optimise everything, only the hottest parts of your code. 510 | 511 | As your application grows, or your traffic pattern evolves, the performance hot spots will change. 512 | 513 | Don't leave complex code that is not performance critical, rewrite it with simpler operations if the bottleneck moves elsewhere. 514 | 515 | * Don't trade performance for reliability 516 | 517 | "I can make things very fast if they don't have to be correct." 518 | .caption Russ Cox 519 | 520 | "Readable means reliable" 521 | .caption Rob Pike 522 | 523 | Performance and reliability are equally important. 524 | 525 | I see little value in having a very fast server that panics, deadlocks or OOMs on a regular basis. 526 | 527 | * In conclusion 528 | 529 | Profile your code to identify the bottlenecks, _do_not_guess_. 530 | 531 | Always write the simplest code you can, the compiler is optimised for _normal_ code. 532 | 533 | Shorter code is faster code; Go is not C++, do not expect the compiler to unravel complicated abstractions. 534 | 535 | Shorter code is _smaller_ code; which is important for the CPU's cache. 536 | 537 | Pay very close attention to allocations, avoid unnecessary allocation where possible. 538 | 539 | Don't trade performance for reliability. 540 | 541 | -------------------------------------------------------------------------------- /src/assignment/assignment1/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | // START OMIT 5 | var x = 1 6 | println(x) 7 | 8 | x = 2 9 | println(x) 10 | 11 | var y = x + 2 12 | println(y) 13 | // END OMIT 14 | } 15 | -------------------------------------------------------------------------------- /src/assignment/assignment2/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | // START OMIT 5 | const x = 1 // HL 6 | x = x + 1 7 | println(x) 8 | // END OMIT 9 | } 10 | -------------------------------------------------------------------------------- /src/comments/comments1/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | // START OMIT 5 | // const a = 1 // HL 6 | /* // HL 7 | const b = 2 8 | const c = 3 9 | */ // HL 10 | 11 | println(a, b, c) 12 | // END OMIT 13 | } 14 | -------------------------------------------------------------------------------- /src/conditionals/conditionals1/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | // START OMIT 5 | var i = 0 6 | for i = 1; i < 11; i++ { 7 | // if i%2 == 0 { // HL 8 | println(i) 9 | // } // HL 10 | } 11 | // END OMIT 12 | } 13 | -------------------------------------------------------------------------------- /src/conditionals/conditionals2/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | // START OMIT 5 | var i = 0 6 | for i = 1; i < 11; i++ { 7 | // if i%2 == 1 { // HL 8 | // continue // HL 9 | // } // HL 10 | println(i) 11 | } 12 | // END OMIT 13 | } 14 | -------------------------------------------------------------------------------- /src/conditionals/conditionals3/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | // START OMIT 5 | var i = 1 6 | for { 7 | // if i > 10 { // HL 8 | // break // HL 9 | // } // HL 10 | if i%2 == 0 { 11 | println(i) 12 | } 13 | i++ 14 | } 15 | // END OMIT 16 | } 17 | -------------------------------------------------------------------------------- /src/const/const1/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | // START OMIT 5 | const name = "David" 6 | println(name) 7 | // END OMIT 8 | } 9 | -------------------------------------------------------------------------------- /src/defer/defer1.go: -------------------------------------------------------------------------------- 1 | package count 2 | 3 | import ( 4 | "bufio" 5 | "log" 6 | "os" 7 | ) 8 | 9 | // START OMIT 10 | func CountLines(path string) int { 11 | f, err := os.Open(path) 12 | if err != nil { 13 | log.Fatal(err) 14 | } 15 | defer f.Close() // HL 16 | 17 | sc := bufio.NewScanner(f) 18 | var lines int 19 | for sc.Scan() { 20 | lines++ 21 | } 22 | if err := sc.Err(); err != nil { 23 | log.Fatal(err) 24 | } 25 | return lines 26 | } 27 | 28 | // END OMIT 29 | -------------------------------------------------------------------------------- /src/equality/equality1/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | var x uint = 700 7 | var y int = 700 8 | 9 | fmt.Println(x == y) 10 | } 11 | -------------------------------------------------------------------------------- /src/exercises/countdir/countdir.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io" 5 | "os" 6 | ) 7 | 8 | // CountLines - count the lines in from Reader 9 | func CountLines(r io.Reader) (int, error) { 10 | return 0, nil 11 | } 12 | 13 | // CountFile write to stdout number of lines in the file 14 | func CountFile(path string) { 15 | } 16 | 17 | // CountDir - write to stdout numbers of lines for each files in directory 18 | func CountDir(dir string) { 19 | } 20 | 21 | func main() { 22 | args := os.Args[1:] 23 | for _, arg := range args { 24 | CountDir(arg) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/exercises/countdir/testdata/dont-read-me: -------------------------------------------------------------------------------- 1 | don't read me by accident 2 | -------------------------------------------------------------------------------- /src/exercises/countdir/testdata/tom-sawyer.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GolangUA/go-training/9d3a08f40b21124cdfe02e1838e8cecffddc7dd6/src/exercises/countdir/testdata/tom-sawyer.txt -------------------------------------------------------------------------------- /src/exercises/countlines/count.go: -------------------------------------------------------------------------------- 1 | package count 2 | 3 | // ContLines count lines of content in the file 4 | func ContLines(path string) int { 5 | var lines int 6 | // TODO: count lines in file 7 | return lines 8 | } 9 | -------------------------------------------------------------------------------- /src/exercises/countlines/count_test.go: -------------------------------------------------------------------------------- 1 | package count 2 | 3 | import "testing" 4 | 5 | func TestCountLines(t *testing.T) { 6 | got := CountLines("testdata/alice.txt") 7 | want := 3736 8 | if got != want { 9 | t.Fatalf("got %d, want %d", got, want) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/exercises/countmanyfiles/countmanyfiles.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io" 5 | "os" 6 | ) 7 | 8 | // CountLines count lines in the reader 9 | func CountLines(r io.Reader) (int, error) { 10 | return 0, nil 11 | } 12 | 13 | // CountFile write count of lines in the file to the stdout 14 | func CountFile(path string) { 15 | } 16 | 17 | func main() { 18 | args := os.Args[1:] 19 | for _, arg := range args { 20 | CountFile(arg) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/exercises/errorhandling/errorhandling.go: -------------------------------------------------------------------------------- 1 | package errorhandling 2 | 3 | // CountLines count lines in the file 4 | func CountLines(path string) (int, error) { 5 | var lines int 6 | var err error 7 | return lines, nil 8 | } 9 | -------------------------------------------------------------------------------- /src/exercises/errorhandling/errorhandling_test.go: -------------------------------------------------------------------------------- 1 | package errorhandling 2 | 3 | import "testing" 4 | 5 | func TestCountLines(t *testing.T) { 6 | got, err := CountLines("testdata/alice.txt") 7 | if err != nil { 8 | t.Fatalf("got error %v, expected nil", err) 9 | } 10 | want := 3736 11 | if got != want { 12 | t.Fatalf("got %d, want %d", got, want) 13 | } 14 | } 15 | 16 | func TestMissingFile(t *testing.T) { 17 | got, err := CountLines("testdata/missing") 18 | want := 0 19 | if err == nil { 20 | t.Fatalf("got error nil, expected non nil") 21 | } 22 | if got != want { 23 | t.Fatalf("got %d, want %d", got, want) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/exercises/httpget/httpget.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | // TODO: write content of the page to stdout 5 | } 6 | -------------------------------------------------------------------------------- /src/exercises/httplinecount/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GolangUA/go-training/9d3a08f40b21124cdfe02e1838e8cecffddc7dd6/src/exercises/httplinecount/.keep -------------------------------------------------------------------------------- /src/exercises/input/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "io" 4 | 5 | // ReadAll reads all the lines of text from r and returns 6 | // all the data read as a string 7 | func ReadAll(r io.Reader) string { 8 | // TODO: add solution here 9 | return "" 10 | } 11 | -------------------------------------------------------------------------------- /src/exercises/input/input_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | ) 7 | 8 | func TestReadAll(t *testing.T) { 9 | const lines = `Line 1 10 | Line 2 11 | Line 3 12 | Line 4 13 | Line 5` 14 | 15 | r := strings.NewReader(lines) 16 | got := ReadAll(r) 17 | want := lines 18 | if got != want { 19 | t.Fatalf("got %q, expected %q", got, want) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/exercises/jsonenc/jsonenc.go: -------------------------------------------------------------------------------- 1 | package jsonenc 2 | 3 | type Person struct { 4 | Firstname string // should be encoded as 'first' 5 | Middlename string // should be encoded as 'middle', and not present if blank 6 | Lastname string // should be encoded as 'last' 7 | 8 | SSID int64 // should not be encoded 9 | 10 | City string // should be encoded as 'city' and not present if missing 11 | Country string // should be encoded as 'country' 12 | 13 | Telephone int64 // should be encoded as 'tel', the value should be a string, not a number 14 | } 15 | -------------------------------------------------------------------------------- /src/exercises/jsonenc/jsonenc_test.go: -------------------------------------------------------------------------------- 1 | package jsonenc 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "strings" 7 | "testing" 8 | ) 9 | 10 | func encode(t *testing.T, p *Person) string { 11 | var b bytes.Buffer 12 | enc := json.NewEncoder(&b) 13 | err := enc.Encode(p) 14 | if err != nil { 15 | t.Fatal(err) 16 | } 17 | return strings.TrimSpace(b.String()) // remove newlines 18 | } 19 | 20 | func TestPerson(t *testing.T) { 21 | p := &Person{ 22 | Firstname: "Bill", 23 | Lastname: "Gates", 24 | SSID: 235897234582, 25 | City: "Seattle", 26 | Country: "USA", 27 | Telephone: 18556149487, 28 | } 29 | 30 | got := encode(t, p) 31 | want := `{"first":"Bill","last":"Gates","city":"Seattle","country":"USA","tel":"18556149487"}` 32 | if got != want { 33 | t.Fatalf("encode(%v): got %s, want %s", p, got, want) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/exercises/linecount/linecount.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "io" 7 | "log" 8 | "os" 9 | ) 10 | 11 | func CountLines(r io.Reader) (int, error) { 12 | sc := bufio.NewScanner(r) 13 | var lines int 14 | for sc.Scan() { 15 | lines++ 16 | } 17 | return lines, sc.Err() 18 | } 19 | 20 | func main() { 21 | lines, err := CountLines(os.Stdin) 22 | if err != nil { 23 | log.Fatal(err) 24 | } 25 | fmt.Println(lines) 26 | } 27 | -------------------------------------------------------------------------------- /src/exercises/methods/methods.go: -------------------------------------------------------------------------------- 1 | package methods 2 | 3 | type Point struct { 4 | X, Y int 5 | } 6 | -------------------------------------------------------------------------------- /src/exercises/methods/methods_test.go: -------------------------------------------------------------------------------- 1 | package methods 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestPointString(t *testing.T) { 9 | p := Point{X: 300, Y: 60} 10 | got := fmt.Sprintf("%v", p) 11 | want := "point: x=300, y=60" 12 | if got != want { 13 | t.Fatalf("got %q, expected %q", got, want) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/exercises/pointers/pointers.go: -------------------------------------------------------------------------------- 1 | package pointers 2 | 3 | import "fmt" 4 | 5 | type Point struct{ X, Y int } 6 | 7 | func (p Point) String() string { 8 | return fmt.Sprintf("point: x=%d, y=%d", p.X, p.Y) 9 | } 10 | 11 | func (p Point) Move(x, y int) { 12 | p.X += x 13 | p.Y += y 14 | } 15 | -------------------------------------------------------------------------------- /src/exercises/pointers/pointers_test.go: -------------------------------------------------------------------------------- 1 | package pointers 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestPointString(t *testing.T) { 9 | p := Point{X: 300, Y: 60} 10 | p.Move(-200, 40) 11 | got := fmt.Sprintf("%v", p) 12 | want := "point: x=100, y=100" 13 | if got != want { 14 | t.Fatalf("got %q, expected %q", got, want) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/exercises/readers/;: -------------------------------------------------------------------------------- 1 | package readers 2 | 3 | import "io" 4 | 5 | // Combine returns an io.Reader which represents 6 | // the contents of a and b. 7 | func Combine(a, b io.Reader) io.Reader { 8 | return a 9 | } 10 | 11 | // always reader always fills the read buffer with 12 | // the byte ch. 13 | type alwaysReader struct { 14 | ch byte 15 | } 16 | 17 | func (a *alwaysReader) Read(buf []byte) (int, error) { 18 | for i := range buf { 19 | buf[i] = a.ch 20 | } 21 | return len(buf), nil 22 | } 23 | 24 | // AReader returns an io.Reader which returns n 'A' characters 25 | func AReader(n int) io.Reader { 26 | return strings.NewReader("AAAAAAAAAA") 27 | } 28 | -------------------------------------------------------------------------------- /src/exercises/readers/readers.go: -------------------------------------------------------------------------------- 1 | package readers 2 | 3 | import ( 4 | "io" 5 | "strings" 6 | ) 7 | 8 | // Combine returns an io.Reader which represents 9 | // the contents of a and b. 10 | func Combine(a, b io.Reader) io.Reader { 11 | return a 12 | } 13 | 14 | // always reader always fills the read buffer with 15 | // the byte ch. 16 | type alwaysReader struct { 17 | ch byte 18 | } 19 | 20 | func (a *alwaysReader) Read(buf []byte) (int, error) { 21 | for i := range buf { 22 | buf[i] = a.ch 23 | } 24 | return len(buf), nil 25 | } 26 | 27 | // AReader returns an io.Reader which returns n 'A' characters 28 | func AReader(n int) io.Reader { 29 | return strings.NewReader("AAAAAAAAAA") 30 | } 31 | -------------------------------------------------------------------------------- /src/exercises/readers/readers_test.go: -------------------------------------------------------------------------------- 1 | package readers 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | "strings" 7 | "testing" 8 | ) 9 | 10 | func TestCombineReaders(t *testing.T) { 11 | a := strings.NewReader("first reader\n") 12 | b := strings.NewReader("second reader\n") 13 | r := Combine(a, b) 14 | 15 | var w bytes.Buffer 16 | io.Copy(&w, r) 17 | got := w.String() 18 | want := "first reader\nsecond reader\n" 19 | if got != want { 20 | t.Fatalf("combine: got %q, want %q", got, want) 21 | } 22 | } 23 | 24 | func TestAReader10(t *testing.T) { 25 | r := AReader(10) 26 | var w bytes.Buffer 27 | io.Copy(&w, r) 28 | got := w.String() 29 | want := "AAAAAAAAAA" 30 | if got != want { 31 | t.Fatalf("AReader(10): got %q, want %q", got, want) 32 | } 33 | } 34 | 35 | func TestAReader20(t *testing.T) { 36 | r := AReader(20) 37 | var w bytes.Buffer 38 | io.Copy(&w, r) 39 | got := w.String() 40 | want := "AAAAAAAAAAAAAAAAAAAA" 41 | if got != want { 42 | t.Fatalf("AReader(20): got %q, want %q", got, want) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/exercises/whatismyip/whatismyip.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | // TODO: read info from "http://httpbin.org/ip" and write origin IP to stdout 5 | } 6 | -------------------------------------------------------------------------------- /src/fmt/fmt1/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // START OMIT 6 | func main() { 7 | name := "David" 8 | age := 27 9 | 10 | fmt.Printf("Hello my name is %s, my age is %d", name, age) 11 | } 12 | 13 | // END OMIT 14 | -------------------------------------------------------------------------------- /src/fmt/fmt2/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // START OMIT 6 | func main() { 7 | name := "David" 8 | age := 27 9 | 10 | fmt.Printf("Hello my name is %d, my age is %d", name) 11 | 12 | _ = age // keep the compiler happy 13 | } 14 | 15 | // END OMIT 16 | -------------------------------------------------------------------------------- /src/fmt/fmt3/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // START OMIT 6 | func main() { 7 | name := "David" 8 | age := 27 9 | 10 | fmt.Printf("Hello my name is %v, my age is %v", name, age) 11 | } 12 | 13 | // END OMIT 14 | -------------------------------------------------------------------------------- /src/functions/functions1/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // START OMIT 4 | func main() { // HL 5 | var x int8 = 400 6 | var y uint = -7 7 | 8 | println(x, y) 9 | } // HL 10 | 11 | // END OMIT 12 | -------------------------------------------------------------------------------- /src/functions/functions2/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // START OMIT 4 | func hello() { 5 | // println("Go BootCamp") // HL 6 | } 7 | 8 | func main() { 9 | var i int = 0 10 | for i = 1; i < 4; i++ { 11 | hello() 12 | } 13 | } 14 | 15 | // END OMIT 16 | -------------------------------------------------------------------------------- /src/functions/functions3/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // START OMIT 4 | func hello(name int) { 5 | println("Hello " + name) 6 | } 7 | 8 | func main() { 9 | hello("David") 10 | } 11 | 12 | // END OMIT 13 | -------------------------------------------------------------------------------- /src/functions/functions4/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // Double returns a number two times larger than i. 6 | func A(i int) int { 7 | return i * 2 8 | } 9 | 10 | // Swap switches the values of a and b 11 | func Swap(a int, b int) (int, int) { 12 | return b, a 13 | } 14 | 15 | func main() { 16 | fmt.Println(A(200)) 17 | 18 | a, b := Swap(7, 9) 19 | fmt.Println(a, b) 20 | } 21 | -------------------------------------------------------------------------------- /src/hellohttp/hellohttp.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "html/template" 5 | "log" 6 | "net/http" 7 | ) 8 | 9 | const templ = `