Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Grokking Techtalk #38: Escape Analysis in Go compiler

96 views

Published on

Trong quá trình phân tích hiệu năng, hiểu và nắm vững ngôn ngữ lập trình cũng như cách thiết kế của nó là rất hữu ích. Go là một trong những ngôn ngữ được sử dụng phổ biến trong các hệ thống phân tán có hiệu năng cao. Để hiểu rõ hơn cách mà Go compiler phân tích cách cấp phát bộ nhớ khi biên dịch chương trình, hãy nghe những chia sẻ của anh Cường về Escape Analysis trong Go compiler.

Về diễn giả:
Anh Lê Mạnh Cường là một kĩ sư phần mềm có 8 năm kinh nghiệm chuyên sâu trong backend và Quản trị hệ thống Linux. Là một OSS contributor tích cực, anh Cường đã có nhiều cống hiến vào cộng đồng mã nguồn mở, đặc biệt là Go và ecosystem của Go.

Published in: Engineering
  • Be the first to comment

Grokking Techtalk #38: Escape Analysis in Go compiler

  1. 1. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 1/57 Escape analysis in the Go compilerEscape analysis in the Go compiler Cuong Manh LeCuong Manh Le 2020-09-262020-09-26 Software EngineerSoftware Engineer
  2. 2. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 2/57 AgendaAgenda Go overviewGo overview Go compiler overviewGo compiler overview Escape Analysis overviewEscape Analysis overview How Go compiler implements escape analysisHow Go compiler implements escape analysis 22
  3. 3. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 3/57 Warm upWarm up This talk assumes you did know fundamental concepts of a compiler.This talk assumes you did know fundamental concepts of a compiler. The termThe term gcgc in this talk stands forin this talk stands for go compilergo compiler, not, not garbage collectorgarbage collector.. There are some compilers for Go: gc, gccgo,There are some compilers for Go: gc, gccgo, tinygotinygo(https://github.com/tinygo-org/tinygo)(https://github.com/tinygo-org/tinygo)... This talk is about... This talk is about gcgc, the official Go compiler., the official Go compiler. This talk use go version 1.15This talk use go version 1.15 I recommendI recommend this coursethis course(https://online.stanford.edu/courses/soe-ycscs1-compilers)(https://online.stanford.edu/courses/soe-ycscs1-compilers)for anyone interested in learning aboutfor anyone interested in learning about compilers.compilers. 33
  4. 4. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 4/57 Let's goLet's go 44
  5. 5. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 5/57 Go overviewGo overview 55
  6. 6. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 6/57 GoGo Go is an open source programming language that makes it easy to build simple, reliable, andGo is an open source programming language that makes it easy to build simple, reliable, and efficient software.efficient software. package mainpackage main import "fmt"import "fmt" func main() {func main() { fmt.Println("Hello, 世界")fmt.Println("Hello, 世界") }} Run 66
  7. 7. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 7/57 Why is Go great for modern distributed computing?Why is Go great for modern distributed computing? First class concurrency primitivesFirst class concurrency primitives All inclusive networking librariesAll inclusive networking libraries Statically Typed languageStatically Typed language Simple language to learnSimple language to learn Statically linked binariesStatically linked binaries Fast build timesFast build times Many more…Many more… 77
  8. 8. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 8/57 Go was built to solve problems at Google:Go was built to solve problems at Google: Multicore processorsMulticore processors Networked systemsNetworked systems Massive computation clustersMassive computation clusters ...... See more detailsSee more details herehere(https://talks.golang.org/2013/distsys.slide#1)(https://talks.golang.org/2013/distsys.slide#1) 88
  9. 9. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 9/57 Go Compiler OverviewGo Compiler Overview 99
  10. 10. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 10/57 What's compilerWhat's compiler A compiler is a computer program that translates high level human written computer codeA compiler is a computer program that translates high level human written computer code into machine executable code.into machine executable code. $ file main.go$ file main.go main.go: C source, UTF-8 Unicode textmain.go: C source, UTF-8 Unicode text $ go tool compile main.go$ go tool compile main.go $ ls$ ls main.go main.omain.go main.o $ file main.o$ file main.o main.o: current ar archivemain.o: current ar archive $ ar x main.o$ ar x main.o $ ls$ ls _go_.o main.go main.o __.PKGDEF_go_.o main.go main.o __.PKGDEF $ file _go_.o$ file _go_.o _go_.o: data_go_.o: data 1010
  11. 11. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 11/57 Go compiler (gc)Go compiler (gc) Translate go source code to machine code.Translate go source code to machine code. $ go tool compile main.go$ go tool compile main.go Actually, "gc" is a separate executable, invoked by the "go" command:Actually, "gc" is a separate executable, invoked by the "go" command: $ go tool -n compile$ go tool -n compile /home/cuonglm/sources/go/pkg/tool/linux_amd64/compile/home/cuonglm/sources/go/pkg/tool/linux_amd64/compile 1111
  12. 12. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 12/57 How does gc do itHow does gc do it Through many steps or phases, logically, there'reThrough many steps or phases, logically, there're fourfour phases:phases: ParsingParsing Type-checking and AST transformationsType-checking and AST transformations SSASSA Generate machine codeGenerate machine code 1212
  13. 13. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 13/57 How does gc do itHow does gc do it SourceSource(https://www.slideshare.net/moriyoshi/hacking-go-compiler-internals-gocon-2014-autumn)(https://www.slideshare.net/moriyoshi/hacking-go-compiler-internals-gocon-2014-autumn) 1313
  14. 14. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 14/57 1 - Parsing1 - Parsing Includes bothIncludes both lexinglexing(https://en.wikipedia.org/wiki/Lexical_analysis)(https://en.wikipedia.org/wiki/Lexical_analysis)andand parsingparsing(https://en.wikipedia.org/wiki/Parsing)(https://en.wikipedia.org/wiki/Parsing) 1414
  15. 15. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 15/57 Lexer comes from lexical analysis which means converting a sequence ofLexer comes from lexical analysis which means converting a sequence of characters into a sequence of tokens/stringscharacters into a sequence of tokens/strings SourceSource(https://www.slideshare.net/moriyoshi/hacking-go-compiler-internals-gocon-2014-autumn)(https://www.slideshare.net/moriyoshi/hacking-go-compiler-internals-gocon-2014-autumn) 1515
  16. 16. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 16/57 SourceSource(https://www.slideshare.net/moriyoshi/hacking-go-compiler-internals-gocon-2014-autumn)(https://www.slideshare.net/moriyoshi/hacking-go-compiler-internals-gocon-2014-autumn) 1616
  17. 17. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 17/57 ExampleExample a := 1a := 1 will be parsed/lexed and produce:will be parsed/lexed and produce: []ast.Stmt {[]ast.Stmt { &ast.AssignStmt {&ast.AssignStmt { Lhs: []ast.Expr {Lhs: []ast.Expr { &ast.Ident {Name: "a"},&ast.Ident {Name: "a"}, },}, Tok: :=,Tok: :=, Rhs: []ast.Expr {Rhs: []ast.Expr { &ast.BasicLit {&ast.BasicLit { ValuePos: 32,ValuePos: 32, Kind: INT,Kind: INT, Value: "1",Value: "1", },}, },}, },}, }} This uses "go/ast", compiler uses another parser in "cmd/compile/internal/syntax", but it'sThis uses "go/ast", compiler uses another parser in "cmd/compile/internal/syntax", but it's similar.similar. 1717
  18. 18. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 18/57 2 - Type-checking and AST transformations2 - Type-checking and AST transformations Type checking means to confirm that declared variables correctly store data with theType checking means to confirm that declared variables correctly store data with the types they are declared with.types they are declared with. To perform type checking, this pass is presented with what's called anTo perform type checking, this pass is presented with what's called an Abstract syntaxAbstract syntax treetree(https://en.wikipedia.org/wiki/Abstract_syntax_tree)(https://en.wikipedia.org/wiki/Abstract_syntax_tree) The intermediate representation (The intermediate representation (IRIR(https://en.wikipedia.org/wiki/Intermediate_representation)(https://en.wikipedia.org/wiki/Intermediate_representation)) of source code in another) of source code in another form, specifically, it's aform, specifically, it's a treetree.. package mainpackage main func main() {func main() { var x stringvar x string x = 1x = 1 println(x)println(x) }} Run 1818
  19. 19. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 19/57 3 - SSA3 - SSA Static single assignment formStatic single assignment form(https://en.wikipedia.org/wiki/Static_single_assignment_form)(https://en.wikipedia.org/wiki/Static_single_assignment_form)is another IR of the source codeis another IR of the source code (not a(not a treetree):): variable is assignedvariable is assigned exactly onceexactly once variable isvariable is defineddefined before it is usedbefore it is used b1:b1: v1 = InitMem <mem>v1 = InitMem <mem> v2 = SP <uintptr>v2 = SP <uintptr> v3 = SB <uintptr>v3 = SB <uintptr> v4 = Addr <*uint8> {type.string} v3v4 = Addr <*uint8> {type.string} v3 v5 = Addr <*string> {""..stmp_0} v3v5 = Addr <*string> {""..stmp_0} v3 v6 = IMake <interface {}> v4 v5 (~arg0[interface {}])v6 = IMake <interface {}> v4 v5 (~arg0[interface {}]) v7 = ConstInterface <interface {}>v7 = ConstInterface <interface {}> v8 = ArrayMake1 <[1]interface {}> v7v8 = ArrayMake1 <[1]interface {}> v7 v9 = VarDef <mem> {.autotmp_11} v1v9 = VarDef <mem> {.autotmp_11} v1 ...... v25 = ConstInterface <error> (fmt..autotmp_4[error], fmt.err[error]) DEADv25 = ConstInterface <error> (fmt..autotmp_4[error], fmt.err[error]) DEAD v28 = OffPtr <*io.Writer> [0] v2v28 = OffPtr <*io.Writer> [0] v2 v29 = Addr <*uint8> {go.itab.*os.File,io.Writer} v3v29 = Addr <*uint8> {go.itab.*os.File,io.Writer} v3 v30 = Addr <**os.File> {os.Stdout} v3v30 = Addr <**os.File> {os.Stdout} v3 ...... 1919
  20. 20. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 20/57 SSA helps avoiding unnecessary operations that don't affect the final form of a variable's use:SSA helps avoiding unnecessary operations that don't affect the final form of a variable's use: nil-check, bound check ...nil-check, bound check ... 2020
  21. 21. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 21/57 4 - Generate machine code4 - Generate machine code 00000 (5) TEXT "".main(SB), ABIInternal00000 (5) TEXT "".main(SB), ABIInternal 00001 (5) FUNCDATA $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)00001 (5) FUNCDATA $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) 00002 (5) FUNCDATA $1, gclocals·f207267fbf96a0178e8758c6e3e0ce28(SB)00002 (5) FUNCDATA $1, gclocals·f207267fbf96a0178e8758c6e3e0ce28(SB) 00003 (5) FUNCDATA $3, "".main.stkobj(SB)00003 (5) FUNCDATA $3, "".main.stkobj(SB) v26 00004 (6) XORPS X0, X0v26 00004 (6) XORPS X0, X0 v11 00005 (6) MOVUPS X0, ""..autotmp_11-16(SP)v11 00005 (6) MOVUPS X0, ""..autotmp_11-16(SP) v20 00006 (6) LEAQ type.string(SB), AXv20 00006 (6) LEAQ type.string(SB), AX v14 00007 (6) MOVQ AX, ""..autotmp_11-16(SP)v14 00007 (6) MOVQ AX, ""..autotmp_11-16(SP) v38 00008 (6) LEAQ ""..stmp_0(SB), AXv38 00008 (6) LEAQ ""..stmp_0(SB), AX v17 00009 (6) MOVQ AX, ""..autotmp_11-8(SP)v17 00009 (6) MOVQ AX, ""..autotmp_11-8(SP) v27 00010 (?) NOPv27 00010 (?) NOP # $GOROOT/src/fmt/print.go# $GOROOT/src/fmt/print.go ...... v36 00019 (274) PCDATA $1, $0v36 00019 (274) PCDATA $1, $0 v36 00020 (274) CALL fmt.Fprintln(SB)v36 00020 (274) CALL fmt.Fprintln(SB) # main.go# main.go b4 00021 (6) RETb4 00021 (6) RET 00022 (?) END00022 (?) END 2121
  22. 22. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 22/57 That's 4 logical phases of go compilerThat's 4 logical phases of go compiler 2222
  23. 23. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 23/57 But ...But ... The second phase contains moreThe second phase contains more sub-phasessub-phases Type Check const, type, and names and types of funcsType Check const, type, and names and types of funcs Type Check variable assignmentsType Check variable assignments Type check function bodiesType check function bodies Capture closure variablesCapture closure variables InliningInlining Escape analysisEscape analysis Transform closure bodiesTransform closure bodies 2323
  24. 24. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 24/57 But ...But ... The second phase contains moreThe second phase contains more sub-phasessub-phases Type Check const, type, and names and types of funcsType Check const, type, and names and types of funcs Type Check variable assignmentsType Check variable assignments Type check function bodiesType check function bodies Capture closure variablesCapture closure variables InliningInlining Escape analysisEscape analysis Transform closure bodiesTransform closure bodies Escape Analysis is important to help the compiler decide if it should allocate variables on theEscape Analysis is important to help the compiler decide if it should allocate variables on the heap or on the stack or how to store the associated data.heap or on the stack or how to store the associated data. 2424
  25. 25. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 25/57 What is Escape AnalysisWhat is Escape Analysis Escape Analysis is a method for determining the dynamic scope of pointers – where in theEscape Analysis is a method for determining the dynamic scope of pointers – where in the program a pointer can be accessed. It is related to pointer analysis and shape analysis.program a pointer can be accessed. It is related to pointer analysis and shape analysis. From https://en.wikipedia.org/wiki/Escape_analysisFrom https://en.wikipedia.org/wiki/Escape_analysis 2525
  26. 26. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 26/57 What is Escape AnalysisWhat is Escape Analysis Escape Analysis is a method for determining the dynamic scope of pointers –Escape Analysis is a method for determining the dynamic scope of pointers – wherewhere in thein the program a pointer can be accessed. It is related to pointer analysis and shape analysis.program a pointer can be accessed. It is related to pointer analysis and shape analysis. "where" means"where" means stackstack oror heapheap 2626
  27. 27. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 27/57 Stack vs HeapStack vs Heap This is traditional way heap/stack work.This is traditional way heap/stack work. Within Go runtime:Within Go runtime: Stacks are allocated within heap memoryStacks are allocated within heap memory There can be more than one stack, and stacks might grow/shrink/move over time.There can be more than one stack, and stacks might grow/shrink/move over time. 2727
  28. 28. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 28/57 Why is Escape Analysis necessaryWhy is Escape Analysis necessary The most benefit is helping convert heap allocation → stack allocationThe most benefit is helping convert heap allocation → stack allocation 2828
  29. 29. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 29/57 Why is Escape Analysis necessaryWhy is Escape Analysis necessary That means:That means: Faster execution time: stack allocation is much faster than heap allocationFaster execution time: stack allocation is much faster than heap allocation Reduce garbage collector pressureReduce garbage collector pressure 2929
  30. 30. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 30/57 ExampleExample StackStack package mainpackage main import "testing"import "testing" type s struct {type s struct { f *intf *int }} func stack() {func stack() { x := s{}x := s{} x.f = new(int)x.f = new(int) }} func BenchmarkAlloc(b *testing.B) {func BenchmarkAlloc(b *testing.B) { b.ReportAllocs()b.ReportAllocs() for i := 0; i <= b.N; i++ {for i := 0; i <= b.N; i++ { stack()stack() }} }} 3030
  31. 31. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 31/57 $ go test -bench=. benchstat_stack_test.go$ go test -bench=. benchstat_stack_test.go goos: linuxgoos: linux goarch: amd64goarch: amd64 BenchmarkAlloc-8 1000000000 0.241 ns/op 0 B/op 0 allocs/opBenchmarkAlloc-8 1000000000 0.241 ns/op 0 B/op 0 allocs/op BenchmarkAlloc-8 1000000000 0.241 ns/op 0 B/op 0 allocs/opBenchmarkAlloc-8 1000000000 0.241 ns/op 0 B/op 0 allocs/op BenchmarkAlloc-8 1000000000 0.239 ns/op 0 B/op 0 allocs/opBenchmarkAlloc-8 1000000000 0.239 ns/op 0 B/op 0 allocs/op BenchmarkAlloc-8 1000000000 0.239 ns/op 0 B/op 0 allocs/opBenchmarkAlloc-8 1000000000 0.239 ns/op 0 B/op 0 allocs/op BenchmarkAlloc-8 1000000000 0.239 ns/op 0 B/op 0 allocs/opBenchmarkAlloc-8 1000000000 0.239 ns/op 0 B/op 0 allocs/op BenchmarkAlloc-8 1000000000 0.239 ns/op 0 B/op 0 allocs/opBenchmarkAlloc-8 1000000000 0.239 ns/op 0 B/op 0 allocs/op BenchmarkAlloc-8 1000000000 0.240 ns/op 0 B/op 0 allocs/opBenchmarkAlloc-8 1000000000 0.240 ns/op 0 B/op 0 allocs/op BenchmarkAlloc-8 1000000000 0.240 ns/op 0 B/op 0 allocs/opBenchmarkAlloc-8 1000000000 0.240 ns/op 0 B/op 0 allocs/op BenchmarkAlloc-8 1000000000 0.241 ns/op 0 B/op 0 allocs/opBenchmarkAlloc-8 1000000000 0.241 ns/op 0 B/op 0 allocs/op BenchmarkAlloc-8 1000000000 0.239 ns/op 0 B/op 0 allocs/opBenchmarkAlloc-8 1000000000 0.239 ns/op 0 B/op 0 allocs/op PASSPASS ok command-line-arguments 2.661sok command-line-arguments 2.661s 3131
  32. 32. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 32/57 ExampleExample HeapHeap package mainpackage main import "testing"import "testing" type s struct {type s struct { f *intf *int }} func heap() {func heap() { x := &s{}x := &s{} x.f = new(int)x.f = new(int) }} func BenchmarkAlloc(b *testing.B) {func BenchmarkAlloc(b *testing.B) { b.ReportAllocs()b.ReportAllocs() for i := 0; i <= b.N; i++ {for i := 0; i <= b.N; i++ { heap()heap() }} }} 3232
  33. 33. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 33/57 $ go test -bench=. benchstat_heap_test.go$ go test -bench=. benchstat_heap_test.go goos: linuxgoos: linux goarch: amd64goarch: amd64 BenchmarkAlloc-8 100000000 10.9 ns/op 8 B/op 1 allocs/opBenchmarkAlloc-8 100000000 10.9 ns/op 8 B/op 1 allocs/op BenchmarkAlloc-8 100000000 11.6 ns/op 8 B/op 1 allocs/opBenchmarkAlloc-8 100000000 11.6 ns/op 8 B/op 1 allocs/op BenchmarkAlloc-8 94421409 11.2 ns/op 8 B/op 1 allocs/opBenchmarkAlloc-8 94421409 11.2 ns/op 8 B/op 1 allocs/op BenchmarkAlloc-8 100000000 10.9 ns/op 8 B/op 1 allocs/opBenchmarkAlloc-8 100000000 10.9 ns/op 8 B/op 1 allocs/op BenchmarkAlloc-8 100000000 11.0 ns/op 8 B/op 1 allocs/opBenchmarkAlloc-8 100000000 11.0 ns/op 8 B/op 1 allocs/op BenchmarkAlloc-8 100000000 11.5 ns/op 8 B/op 1 allocs/opBenchmarkAlloc-8 100000000 11.5 ns/op 8 B/op 1 allocs/op BenchmarkAlloc-8 100000000 12.0 ns/op 8 B/op 1 allocs/opBenchmarkAlloc-8 100000000 12.0 ns/op 8 B/op 1 allocs/op BenchmarkAlloc-8 98214091 11.1 ns/op 8 B/op 1 allocs/opBenchmarkAlloc-8 98214091 11.1 ns/op 8 B/op 1 allocs/op BenchmarkAlloc-8 105177076 11.2 ns/op 8 B/op 1 allocs/opBenchmarkAlloc-8 105177076 11.2 ns/op 8 B/op 1 allocs/op BenchmarkAlloc-8 100000000 11.1 ns/op 8 B/op 1 allocs/opBenchmarkAlloc-8 100000000 11.1 ns/op 8 B/op 1 allocs/op PASSPASS ok command-line-arguments 12.126sok command-line-arguments 12.126s 3333
  34. 34. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 34/57 ExampleExample $ benchstat stack.txt heap.txt$ benchstat stack.txt heap.txt name old time/op new time/op deltaname old time/op new time/op delta Alloc-8 0.24ns ± 1% 11.25ns ± 7% +4591.41% (p=0.000 n=10+10)Alloc-8 0.24ns ± 1% 11.25ns ± 7% +4591.41% (p=0.000 n=10+10) name old alloc/op new alloc/op deltaname old alloc/op new alloc/op delta Alloc-8 0.00B 8.00B ± 0% +Inf% (p=0.000 n=10+10)Alloc-8 0.00B 8.00B ± 0% +Inf% (p=0.000 n=10+10) name old allocs/op new allocs/op deltaname old allocs/op new allocs/op delta Alloc-8 0.00 1.00 ± 0% +Inf% (p=0.000 n=10+10)Alloc-8 0.00 1.00 ± 0% +Inf% (p=0.000 n=10+10) 3434
  35. 35. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 35/57 How is escape analysis implemented in the GoHow is escape analysis implemented in the Go compiler?compiler? 3535
  36. 36. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 36/57 Building a directed weighted graphBuilding a directed weighted graph With some rules:With some rules: Vertices (termed "location") represent variables.Vertices (termed "location") represent variables. Edges represent assignments between variables.Edges represent assignments between variables. Compound variables (struct, slice, array, map ...) is lowered to simplest representation.Compound variables (struct, slice, array, map ...) is lowered to simplest representation. 3636
  37. 37. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 37/57 That meansThat means var x struct { f, g *int }var x struct { f, g *int } var u []*intvar u []*int x.f = u[0]x.f = u[0] is modeled simply as:is modeled simply as: x = *ux = *u 3737
  38. 38. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 38/57 Building a directed weighted graphBuilding a directed weighted graph With some rules:With some rules: Vertices (termed "location") represents variables.Vertices (termed "location") represents variables. Edges represents assignments between variables.Edges represents assignments between variables. Compound variables is lowered to simplest representation.Compound variables is lowered to simplest representation. Number of dereference operations minus the number of addressing operations is theNumber of dereference operations minus the number of addressing operations is the edge's weightedge's weight p = &q // -1p = &q // -1 p = q // 0p = q // 0 p = *q // 1p = *q // 1 p = **q // 2p = **q // 2 p = **&**&q // 2p = **&**&q // 2 3838
  39. 39. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 39/57 Run Bellman-Ford shortest path algorithmRun Bellman-Ford shortest path algorithm To calculate the minimal number of dereferences from a "location" to others.To calculate the minimal number of dereferences from a "location" to others. Bellman-FordBellman-Ford(https://en.wikipedia.org/wiki/Bellman%E2%80%93Ford_algorithm)(https://en.wikipedia.org/wiki/Bellman%E2%80%93Ford_algorithm)is slower thanis slower than DijkstraDijkstra(https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm)(https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm),, but it can handle negative weight (from addressing operations).but it can handle negative weight (from addressing operations). Also do not have to worry about negative cycles, because the compiler does not allowAlso do not have to worry about negative cycles, because the compiler does not allow "distances" to go below 0."distances" to go below 0. var x intvar x int _ = &(&x) // invalid_ = &(&x) // invalid 3939
  40. 40. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 40/57 ExampleExample With following code:With following code: package ppackage p var px *intvar px *int func foo() {func foo() { var i intvar i int p := &ip := &i q := pq := p px = qpx = q }} ii has no edgeshas no edges pp has edge to i ( weight -1 )has edge to i ( weight -1 ) qq has edge to p ( weight 0 )has edge to p ( weight 0 ) heap has edge to q ( weight 0 )heap has edge to q ( weight 0 ) 4040
  41. 41. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 41/57 ExampleExample if __name__ == '__main__':if __name__ == '__main__': graph = {graph = { 'i': {},'i': {}, 'p': {'i': -1},'p': {'i': -1}, 'q': {'p': 0},'q': {'p': 0}, 'heap': {'q': 0}'heap': {'q': 0} }} distance, _ = bellman_ford(graph, source='heap')distance, _ = bellman_ford(graph, source='heap') print distanceprint distance $ python main.py$ python main.py {'i': -1, 'p': 0, 'q': 0, 'heap': 0}{'i': -1, 'p': 0, 'q': 0, 'heap': 0} sourcesource(https://gist.github.com/ngenator/6178728)(https://gist.github.com/ngenator/6178728) 4141
  42. 42. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 42/57 Static data-flow analysisStatic data-flow analysis To determine whether a locationTo determine whether a location outlivesoutlives others, that means it may survive beyond other'sothers, that means it may survive beyond other's lifetime if stack allocated.lifetime if stack allocated. A location outlives other if ...A location outlives other if ... 4242
  43. 43. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 43/57 Returned valuesReturned values We don't know what the caller will do with the returned values.We don't know what the caller will do with the returned values. Except for directly called closures:Except for directly called closures: var u intvar u int p := func() *int { return &u }()p := func() *int { return &u }() *p = 42*p = 42 4343
  44. 44. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 44/57 Higher loop scopeHigher loop scope in the same function:in the same function: var l *intvar l *int for {for { l = new(int)l = new(int) }} 4444
  45. 45. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 45/57 Other is declared within a child closureOther is declared within a child closure var l *intvar l *int func() {func() { l = new(int)l = new(int) }} 4545
  46. 46. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 46/57 Escape analysis also records information about whether a function's parameters will escape.Escape analysis also records information about whether a function's parameters will escape. var global *intvar global *int func f(p *int) { *p = 42 }func f(p *int) { *p = 42 } func g(p *int) { global = p }func g(p *int) { global = p } func h() {func h() { f(new(int)) // does not escapef(new(int)) // does not escape g(new(int)) // does escapeg(new(int)) // does escape }} That is because escape analysis firstly analyzes justThat is because escape analysis firstly analyzes just ff, then analyzes just *, then analyzes just *gg, then just, then just hh, but it, but it uses the results of previously analyzinguses the results of previously analyzing ff andand gg.. 4646
  47. 47. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 47/57 That's the theoryThat's the theory 4747
  48. 48. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 48/57 In practiceIn practice Go compiler provides tools for us to diagnose all of those things.Go compiler provides tools for us to diagnose all of those things. usage: compile [options] file.go...usage: compile [options] file.go... ...... version,destination for JSON compiler/optimizer loggingversion,destination for JSON compiler/optimizer logging -l disable inlining-l disable inlining -lang string-lang string release to compile forrelease to compile for -linkobj file-linkobj file write linker-specific object to filewrite linker-specific object to file -linkshared-linkshared generate code that will be linked against Go shared librariesgenerate code that will be linked against Go shared libraries -live-live debug liveness analysisdebug liveness analysis -m print optimization decisions-m print optimization decisions -memprofile file-memprofile file write memory profile to filewrite memory profile to file -memprofilerate rate-memprofilerate rate set runtime.MemProfileRate to rateset runtime.MemProfileRate to rate ...... 4848
  49. 49. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 49/57 ExampleExample 10 func stack() {10 func stack() { 11 x := s{}11 x := s{} 12 x.f = new(int)12 x.f = new(int) 13 }13 } 1414 15 func heap() {15 func heap() { 16 x := &s{}16 x := &s{} 17 x.f = new(int)17 x.f = new(int) 18 }18 } $ go tool compile -l -m stack_vs_heap_test.go$ go tool compile -l -m stack_vs_heap_test.go stack_vs_heap_test.go:12:11: new(int) does not escapestack_vs_heap_test.go:12:11: new(int) does not escape stack_vs_heap_test.go:16:7: &s{} does not escapestack_vs_heap_test.go:16:7: &s{} does not escape stack_vs_heap_test.go:17:11: new(int) escapes to heapstack_vs_heap_test.go:17:11: new(int) escapes to heap 4949
  50. 50. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 50/57 ExampleExample $ go tool compile -l -m=2 stack_vs_heap_test.go$ go tool compile -l -m=2 stack_vs_heap_test.go stack_vs_heap_test.go:12:11: new(int) does not escapestack_vs_heap_test.go:12:11: new(int) does not escape stack_vs_heap_test.go:17:11: new(int) escapes to heap:stack_vs_heap_test.go:17:11: new(int) escapes to heap: stack_vs_heap_test.go:17:11: flow: {heap} = &{storage for new(int)}:stack_vs_heap_test.go:17:11: flow: {heap} = &{storage for new(int)}: stack_vs_heap_test.go:17:11: from new(int) (spill) at stack_vs_heap_test.go:17:11stack_vs_heap_test.go:17:11: from new(int) (spill) at stack_vs_heap_test.go:17:11 stack_vs_heap_test.go:17:11: from x.f = new(int) (assign) at stack_vs_heap_test.go:17:6stack_vs_heap_test.go:17:11: from x.f = new(int) (assign) at stack_vs_heap_test.go:17:6 stack_vs_heap_test.go:16:7: &s{} does not escapestack_vs_heap_test.go:16:7: &s{} does not escape stack_vs_heap_test.go:17:11: new(int) escapes to heapstack_vs_heap_test.go:17:11: new(int) escapes to heap 5050
  51. 51. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 51/57 ExampleExample $ go tool compile -l -m=3 stack_vs_heap_test.go$ go tool compile -l -m=3 stack_vs_heap_test.go stack_vs_heap_test.go:11:4:[1] stack stmt: x := s{}stack_vs_heap_test.go:11:4:[1] stack stmt: x := s{} stack_vs_heap_test.go:11:2:[1] stack stmt: var x sstack_vs_heap_test.go:11:2:[1] stack stmt: var x s stack_vs_heap_test.go:12:6:[1] stack stmt: x.f = new(int)stack_vs_heap_test.go:12:6:[1] stack stmt: x.f = new(int) stack_vs_heap_test.go:12:11: new(int) does not escapestack_vs_heap_test.go:12:11: new(int) does not escape stack_vs_heap_test.go:16:4:[1] heap stmt: x := &s{}stack_vs_heap_test.go:16:4:[1] heap stmt: x := &s{} stack_vs_heap_test.go:16:2:[1] heap stmt: var x *sstack_vs_heap_test.go:16:2:[1] heap stmt: var x *s stack_vs_heap_test.go:17:6:[1] heap stmt: x.f = new(int)stack_vs_heap_test.go:17:6:[1] heap stmt: x.f = new(int) stack_vs_heap_test.go:17:11: new(int) escapes to heap:stack_vs_heap_test.go:17:11: new(int) escapes to heap: stack_vs_heap_test.go:17:11: flow: {heap} = &{storage for new(int)}:stack_vs_heap_test.go:17:11: flow: {heap} = &{storage for new(int)}: stack_vs_heap_test.go:17:11: from new(int) (spill) at stack_vs_heap_test.go:17:11stack_vs_heap_test.go:17:11: from new(int) (spill) at stack_vs_heap_test.go:17:11 stack_vs_heap_test.go:17:11: from x.f = new(int) (assign) at stack_vs_heap_test.go:17:6stack_vs_heap_test.go:17:11: from x.f = new(int) (assign) at stack_vs_heap_test.go:17:6 stack_vs_heap_test.go:16:7: &s{} does not escapestack_vs_heap_test.go:16:7: &s{} does not escape stack_vs_heap_test.go:17:11: new(int) escapes to heapstack_vs_heap_test.go:17:11: new(int) escapes to heap 5151
  52. 52. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 52/57 There's plenty of short-comingsThere's plenty of short-comings var global *intvar global *int func f(x bool, p *int) { if x { global = p } }func f(x bool, p *int) { if x { global = p } } func g() {func g() { f(false, new(int)) // BAD: new(int) is heap allocated, but would be safe to stack allocate heref(false, new(int)) // BAD: new(int) is heap allocated, but would be safe to stack allocate here }} Or assigment through a pointer is conservatively treated as a store to the heap.Or assigment through a pointer is conservatively treated as a store to the heap. func heap() {func heap() { x := &s{}x := &s{} x.f = new(int)x.f = new(int) }} 5252
  53. 53. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 53/57 Further readingFurther reading https://github.com/golang/go/blob/release-https://github.com/golang/go/blob/release- branch.go1.15/src/cmd/compile/internal/gc/escape.gobranch.go1.15/src/cmd/compile/internal/gc/escape.go https://www.cc.gatech.edu/~harrold/6340/cs6340_fall2009/Readings/choi99escape.pdfhttps://www.cc.gatech.edu/~harrold/6340/cs6340_fall2009/Readings/choi99escape.pdf5353
  54. 54. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 54/57 Q&AQ&A 5454
  55. 55. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 55/57 Special thanks (listing order by reviewing date)Special thanks (listing order by reviewing date) @huydx@huydx(https://github.com/huydx)(https://github.com/huydx) @favadi@favadi(https://github.com/favadi)(https://github.com/favadi) @mdempsky@mdempsky(https://github.com/mdempsky)(https://github.com/mdempsky) @odeke-em@odeke-em(https://github.com/odeke-em)(https://github.com/odeke-em) These folks have helped me review many things like parts of this talk and code.These folks have helped me review many things like parts of this talk and code. 5555
  56. 56. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 56/57 Thank youThank you Cuong Manh LeCuong Manh Le 2020-09-262020-09-26 Software EngineerSoftware Engineer cuong.manhle.vn@gmail.comcuong.manhle.vn@gmail.com(mailto:cuong.manhle.vn@gmail.com)(mailto:cuong.manhle.vn@gmail.com) https://cuonglm.xyzhttps://cuonglm.xyz(https://cuonglm.xyz)(https://cuonglm.xyz) @cuonglm_@cuonglm_(http://twitter.com/cuonglm_)(http://twitter.com/cuonglm_)
  57. 57. 10/15/2020 Escape analysis in the Go compiler https://talks.cuonglm.xyz/escape-analysis-in-go-compiler.slide#1 57/57

×