ã¯ããã«
ãã®èšäºã§ã¯ãLinuxã§å©çšå¯èœãªçµã¿èŸŒã¿ã®äžè¬çãªããŒã«ã䜿çšããŠGoã¢ããªã±ãŒã·ã§ã³ããããã¡ã€ã«ããã³æé©åããæ¹æ³ã瀺ããŸãã
ãããã¡ã€ãªã³ã°ãšæé©åãšã¯äœã§ããïŒ ããã°ã©ã ãååã«éãåäœãããã¡ã¢ãªã倧éã«äœ¿çšããããã»ããµãæé©ã«äœ¿çšããªãå Žåãåé¡ãç解ããŠä¿®æ£ããŸããããããããã¡ã€ãªã³ã°ãšæé©åã§ãã
誀ã£ãã¢ããªã±ãŒã·ã§ã³æäœã®åé¡ãããã«é®æããããã«ããã®ãããªå®çŸ©ãäžããŸããã ãã®èšäºã§ã¯ããã«ãã¹ã¬ããããã°ã©ãã³ã°ã®åé¡ãããŒã¿ãã©ã€ãïŒè±èªã®
ããŒã¿ã¬ãŒã¹ ïŒããšã©ãŒã®æ€çŽ¢ïŒè±èªã®
ãããã° ïŒã«ã€ããŠã¯èª¬æããŸããã Goã«ã¯ãããããã¹ãŠã®ããã®ç¬èªã®ãŠãŒãã£ãªãã£ãšã¢ãããŒãããããŸãããå°æ¥ã®ããã«ãã®ãããã¯ãæ®ããŸãããã
CPU
ããã»ããµã䜿çšããŠä»æ¥ã®ã¬ãã¥ãŒãå§ããŸãããã
Goã«ã¯çµã¿èŸŒã¿ã®ãããã¡ã€ã©ãŒããããC / C ++çšã®ããŒã«ã®gperftoolsã»ãããããããã¡ã€ã©ãŒã®ã€ã¡ãŒãžãšé¡äŒŒæ§ã§äœæãããŠããŸãã ããã«ãGoã§èšè¿°ããããããã¡ã€ãªã³ã°çµæãèŠèŠåããããã«èšèšãããpprofãŠãŒãã£ãªãã£ã®é¡äŒŒç©ãã¡ã€ã³ããŒãžã§ã³ã«ãªããGoãšC / C ++ã®äž¡æ¹ã®èŠèŠåã«æšå¥šãããŸãã
åé¡ã«é¢ããŠã¯ãGoãããã¡ã€ã©ãŒã¯ã
ãµã³ããªã³ã°ãããã¡ã€ã©ãŒ ãã§ãã ããã¯ãäžå®ã®é »åºŠã§ããã°ã©ã ãäžæããã¹ã¿ãã¯ãã¬ãŒã¹ãååŸããã©ããã«æžã蟌ãããšãæå³ããæçµçã«ã¯ãã¹ã¿ãã¯ãã¬ãŒã¹ã§ç°ãªãé¢æ°ãæ€åºãããé »åºŠã«åºã¥ããŠãã©ã®é¢æ°ã䜿çšãããããç解ããŸãããå€ãã®ããã»ããµãªãœãŒã¹ãããã³ããå°ãªããªãœãŒã¹ã
ã»ãšãã©ãã¹ãŠã®GoãŠãŒãã£ãªãã£ãšãããã¡ã€ã©ãŒã¯ããã€ãã®æ¹æ³ã§èµ·åã§ããŸãããã®ãã¡ã®ããã€ãã¯ãã®èšäºã§èª¬æããŸãã
äŸããå§ããŠãããã«è©³ãã話ããŸãããã
äŸ
package perftest import ( "regexp" "strings" "testing" ) var haystack = `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras accumsan nisl et iaculis fringilla. Integer sapien orci, facilisis ut venenatis nec, suscipit at massa. Cras suscipit lectus non neque molestie, et imperdiet sem ultricies. Donec sit amet mattis nisi, efficitur posuere enim. Aliquam erat volutpat. Curabitur mattis nunc nisi, eu maximus dui facilisis in. Quisque vel tortor mauris. Praesent tellus sapien, vestibulum nec purus ut, luctus egestas odio. Ut ac ipsum non ipsum elementum pretium in id enim. Aenean eu augue fringilla, molestie orci et, tincidunt ipsum. Nullam maximus odio vitae augue fermentum laoreet eget scelerisque ligula. Praesent pretium eu lacus in ornare. Maecenas fermentum id sapien non faucibus. Donec est tellus, auctor eu iaculis quis, accumsan vitae ligula. Fusce dolor nisl, pharetra eu facilisis non, hendrerit ac turpis. Pellentesque imperdiet aliquam quam in luctus. Curabitur ut orci sodales, faucibus nunc ac, maximus odio. Vivamus vitae nulla posuere, pellentesque quam posuere` func BenchmarkSubstring(b *testing.B) { for i := 0; i < bN; i++ { strings.Contains(haystack, "auctor") } } func BenchmarkRegex(b *testing.B) { for i := 0; i < bN; i++ { regexp.MatchString("auctor", haystack) } }
Nãæååå
ã®éšåæååãNåæ€çŽ¢ãã2ã€ã®ãã³ãããŒã¯ã次ã«ç€ºããŸãã 1ã€ã¯æ£èŠè¡šçŸããã±ãŒãžã䜿çšããŠãããè¡ãããã1ã€ã¯æååããã±ãŒãžã䜿çšããŸãã auctorãšããåèªãæ¢ããŠããŸãã
ãã³ãããŒã¯ãå®è¡ããçµæã確èªããŸãã
$ go test -bench=. testing: warning: no tests to run BenchmarkSubstring-8 10000000 194 ns/op BenchmarkRegex-8 200000 7516 ns/op PASS ok github.com/mkevac/perftest00 3.789s
çµæãæåŸ
ãããŸãããªããªã æ£èŠè¡šçŸã¯ã¯ããã«åŒ·åã§ããããäœéãªããŒã«ã§ããããã®ã³ãŒãããããã¡ã€ã«ããŠã¿ãŸãããã
ãã®å Žåã«ãããã¡ã€ã©ãŒã䜿çšããæãç°¡åãªæ¹æ³ã¯ãè¿œå ãªãã·ã§ã³
-cpuprofile cpu.outã䜿çšããŠåããã³ãããŒã¯ãå®è¡ããããš
ã§ã ã ãã®çµæããããã¡ã€ãªã³ã°çµæãšãæåã®ååŸãå解ãªã©ã«å¿
èŠãªãã€ããªã
å«ãcpu.outãã¡ã€ã«ããã£ã¬ã¯ããªã«è¡šç€ºãããŸãã
å®éããã€ããªã¯åžžã«äœæãããŸãããéåžžã®å Žåãäžæãã£ã¬ã¯ããªã«äœæããããã³ãããŒã¯ã®çŽåŸã«åé€ãããŸãã ãããã¡ã€ãªã³ã°ã§èµ·åããå Žåãçµæã®ãã€ããªã¯åé€ãããŸããã
ãããã£ãŠããããã¡ã€ãªã³ã°ã䜿çšããŠBenchmarkRegex
ãã³ãããŒã¯ãå®è¡ã
ãŸã ã
$ GOGC=off go test -bench=BenchmarkRegex -cpuprofile cpu.out testing: warning: no tests to run BenchmarkRegex-8 200000 6773 ns/op PASS ok github.com/mkevac/perftest00 1.491s
ã芧ã®ãšãããæåã«ãã¬ãã£ãã¯ã¹
GOGC = offã
ä»ããŠãã³ãããŒã¯ãéå§
ããŸããã GOGCç°å¢å€æ°ãoffã«èšå®ãããšãã¬ããŒãžã³ã¬ã¯ã¿ãŒãç¡å¹ã«ãªããŸãã ã¬ããŒãžã³ã¬ã¯ã¿ãŒãšãã®ã¹ã¿ãã¯ãã¬ãŒã¹ãã¹ããŒãªãŒã®æµãããç§ãã¡ã®æ³šæãããããªãããã«ãç§ã¯æèçã«ãããè¡ããŸããã
ã¡ãªã¿ã«ãçåœã®ã¹ã¯ãªãããšããã°ã©ã ã®GCãç¡å¹ã«ããããšã¯ãããã°ã©ã ã®å®è¡æéãæ°åççž®ã§ããåªãããœãªã¥ãŒã·ã§ã³ã§ãã Goã ãã§ã¯ãããŸããã PHPã®å Žåãç§ãç¥ãéãããã®ããã§ã€ã³ããã䜿çšããããšããããŸãã å®éããµãŒããŒã®ã¡ã¢ãªäœ¿çšéã«ãããåäœæéãççž®ããŠããŸãã
ããã§ãpprofãŠãŒãã£ãªãã£ã䜿çšããŠåŒã³åºãã°ã©ããèŠèŠåããŸãã
$ go tool pprof perftest00.test cpu.out
ã°ã©ããååŸããæãç°¡åãªæ¹æ³ã¯ãSVGã€ã¡ãŒãžãäžæãã£ã¬ã¯ããªã«èªåçã«ä¿åãããã©ãŠã¶ãŒãèµ·åããŠè¡šç€ºããwebã³ãã³ãã§ãã
ãªã¢ãŒããµãŒããŒã§äœæ¥ããŠããå Žåããã®ãªãã·ã§ã³ã¯æ©èœããŸããã SSHã®-Yã¹ã€ããã䜿çšããŠXãµãŒããŒãçµäºãããã
go tool pprof -svg ./perftest00.test ./cpu.out> cpu.svgã³ãã³ãã䜿çšããŠSVGãã¡ã€ã«ããã£ã¹ã¯ã«ä¿åããã³ã³ãã¥ãŒã¿ãŒã«ã³ããŒããŠãéããŠ
ç§ã®ãããªOSXã®å ŽåãSSH転éãæ©èœããããã«XQuartz XãµãŒããŒãã€ã³ã¹ããŒã«ããå¿
èŠããããŸãã
çµæã®åŒã³åºãã°ã©ããèŠãŠã¿ãŸãããã
ãã®ãããªã°ã©ããæ€èšããéã«ã¯ããŸãããšããžã®å€ªãïŒç¢å°ïŒãšã°ã©ãã®ããŒãã®ãµã€ãºïŒåè§ïŒã«æ³šæããå¿
èŠããããŸãã æéã¯ç«¯ã§çœ²åãããŸãããããã¡ã€ãªã³ã°äžã«ã¹ã¿ãã¯ãã¬ãŒã¹ã«ããç¹å®ã®ããŒããŸãã¯äžäœããŒãã®é·ãã
æåã®ïŒäžçªäžã®ïŒããŒããã倪åã®ç¢å°ã«æ²¿ã£ãŠé²ã¿ãæåã®åå²ç¹ã«è¡ããŸãããã
BenchmarkRegexé¢æ°ãåŒã³åºã
regexp.MatchStringé¢æ°ãããã³åå²ããŠããããšãããããŸãã
æ£èŠè¡šçŸã䜿çšããããšãããå Žåãã»ãšãã©ã®å®è£
ã¯ããã»ã¹ãæ£èŠè¡šçŸã®åææååè¡šçŸãäœããã®äžéããªã¢ã³ãã«ã³ã³ãã€ã«ããå®éã«ãã®äžéããªã¢ã³ãã䜿çšãã段éã«åå²ããããšããåãã§ãããã
äºçŽ°ãªæé©åãé Œãã«ãªããŸãïŒã³ã³ãã€ã«ã1åå®è¡ããŸãããç¹°ãè¿ãã¯ããŸããã
ããããã£ãŠã¿ãŸãããïŒ
package perftest import ( "regexp" "strings" "testing" ) var haystack = `Lorem ipsum dolor sit amet, consectetur adipiscing [...] Vivamus vitae nulla posuere, pellentesque quam posuere` var pattern = regexp.MustCompile("auctor") func BenchmarkSubstring(b *testing.B) { for i := 0; i < bN; i++ { strings.Contains(haystack, "auctor") } } func BenchmarkRegex(b *testing.B) { for i := 0; i < bN; i++ { pattern.MatchString(haystack) } }
ãããŠãäœãå€ãã£ãã®ãèŠãŠã¿ãŸãããïŒ
$ go test -bench=. testing: warning: no tests to run BenchmarkSubstring-8 10000000 170 ns/op BenchmarkRegex-8 5000000 297 ns/op PASS ok github.com/mkevac/perftest01 3.685s
ã芧ã®ãšãããæ£èŠè¡šçŸã䜿çšããããªã¢ã³ãã¯æ¡éãã«é«éåãããåçŽãªéšåæååæ€çŽ¢ã䜿çšããŠããªã¢ã³ãã«è¿ã¥ããŸããã
ããããã³ãŒã«ã°ã©ãã¯ã©ã®ããã«å€åããŸãããïŒ ããã£ãšç°¡åãã«ãªã£ãã®ã¯ã çŸåšãã³ã³ãã€ã«ã¯äžåºŠã ãè¡ãããŸãã ããã«ãã³ã³ãã€ã«åŒã³åºãã¯ã°ã©ãã«ãŸã£ãããããããŸããã§ããã ãããã¡ã€ãªã³ã°ã¯ãµã³ããªã³ã°ã§ãã
çŽ æŽãããã CPU CPU Go Profilerãå®è¡ããããã«ä»ã«ã©ã®ãããªã¡ãœãããããã®ãââèŠãŠã¿ãŸãããã
ãããã¡ã€ã©ãŒãèµ·åããæ¹æ³
ãã§ã«ç¢ºèªãã1ã€ã®æ¹æ³ã¯ã
go testã³ãã³ãã®
-cpuprofileãªãã·ã§ã³ã§ã
ãé¢æ°
pprof.StartCPUProfileïŒïŒããã³
pprof.StopCPUProfileïŒïŒã䜿çšããŠããããã¡ã€ã©ãŒãæåã§éå§ããããšãã§ããŸãã Dave CheneyïŒ
https://github.com/pkg/profile ïŒã®ãããã®é¢æ°ã«å¯ŸããŠéåžžã«äŸ¿å©ãªã©ãããŒã䜿çšããæ¹ãå°ãç°¡åã§ããããã«ããããã¡ã€ã«ãäœæãããæžã蟌ã¿ãªã©ãè¡ãããŸãã
ãã1ã€ã®åªããæ¹æ³ã¯ã
net / http / pprofããã±ãŒãžã䜿çšããããš
ã§ãã ã€ã³ããŒããããšãURL
/ debug / pprofã® HTTPãã³ãã©ãŒãèªåçã«è¿œå ãããåã
goããŒã«pprofã䜿çšããŠå®è¡äžã®ããã°ã©ã ããªã¢ãŒãã§ãããã¡ã€ã«ã§ããŸã
ã ãããã©ã®ããã«èŠãããèŠãŠã¿ãŸãããã
ç°¡åãªäŸãæžããŠã¿ãŸãããã
package main import ( "net/http" _ "net/http/pprof" ) func cpuhogger() { var acc uint64 for { acc += 1 if acc&1 == 0 { acc <<= 1 } } } func main() { go http.ListenAndServe("0.0.0.0:8080", nil) cpuhogger() }
ã芧ã®ãšããã
net / http / pprofããã±ãŒãžãã€ã³ããŒããã
http.ListenAndServeïŒïŒã³ãã³ãã§HTTPãµãŒããŒãèµ·åããŸããã ããã¯ãããã°ã©ã ã®å®è¡äžã«ãããã¡ã€ã©ãŒã䜿çšããã®ã«ååã§ãã
ããã°ã©ã ãå®è¡ããŠãããã¡ã€ã©ãŒã䜿çšããŠã¿ãŸãããã
$ go tool pprof http://localhost:8080/debug/pprof/profile?seconds=5
ã芧ã®ãšãããpprofãŠãŒãã£ãªãã£ã«ããããã¡ã€ã©ãŒãããªãã¹ã³ããããã³ãã©ãŒãžã®ãã¹ãæž¡ãã ãã§ãã ããã«ããããã¡ã€ã©ã®åäœæéïŒããã©ã«ãã§ã¯30ç§ïŒã転éã§ããŸãã
webã³ãã³ãã¯æ£åžžã«åäœããtopã³ãã³ãã¯æ£åžžã«åäœããŸãããlistãšdisasmã¯ãããã°ã©ã ã®ãœãŒã¹ã«é¢ããæ
å ±ããªããšèšããŸãã
(pprof) web (pprof) top 4.99s of 4.99s total ( 100%) flat flat% sum% cum cum% 4.99s 100% 100% 4.99s 100% main.cpuhogger 0 0% 100% 4.99s 100% runtime.goexit 0 0% 100% 4.99s 100% runtime.main (pprof) list cpuhogger Total: 4.99s No source information for main.cpuhogger
ãœãŒã¹ã«é¢ããæ
å ±ãååŸããã«ã¯ãpprofãå°ãç°ãªãæ¹æ³ã§å®è¡ããå¿
èŠããããŸãã 圌ã«ãã€ããªãžã®ãã¹ãæž¡ãå¿
èŠããããŸãã
$ go tool pprof pproftest http://localhost:8080/debug/pprof/profile?seconds=5
ããã§ãlistãšdisasmã®äž¡æ¹ââã䜿çšããŠãçŸå®ãæåŸ
ãšäžèŽããããã«ã§ããŸãã
(pprof) list cpuhogger Total: 4.97s ROUTINE ======================== main.cpuhogger in /home/marko/goprojects/src/github.com/mkevac/pproftest/main.go 4.97s 4.97s (flat, cum) 100% of Total . . 6:) . . 7: . . 8:func cpuhogger() { . . 9: var acc uint64 . . 10: for { 2.29s 2.29s 11: acc += 1 1.14s 1.14s 12: if acc&1 == 0 { 1.54s 1.54s 13: acc <<= 1 . . 14: } . . 15: } . . 16:} . . 17: . . 18:func main() { (pprof) disasm cpuhogger Total: 4.97s ROUTINE ======================== main.cpuhogger 4.97s 4.97s (flat, cum) 100% of Total . . 401000: XORL AX, AX 1.75s 1.75s 401002: INCQ AX 1.14s 1.14s 401005: TESTQ $0x1, AX . . 40100b: JNE 0x401002 1.54s 1.54s 40100d: SHLQ $0x1, AX 540ms 540ms 401010: JMP 0x401002 . . 401012: INT $0x3
ãã£ãšæ¥œãã¿ãŸãããã èªåã§URLãè¿ããã®ãåçž®ãããŸãïŒ
$ curl http://localhost:8080/debug/pprof/profile?seconds=5 -o /tmp/cpu.log
go tool test -cpuprofileãŸãã¯
StartCPUProfileïŒïŒã䜿çšãããšã/ tmp / cpu.logå
ã§åããã€ããªããŒã¿ãè¿ãããããš
ã ããããŸãã ãã®ãã€ããªãã¡ã€ã«ã«
æååã³ãã³ãããèšå®ãããé¢æ°åãããããæåãå
éšã«ãªãããšãç解ããŸãã
$ strings /tmp/cpu.log | grep cpuhogger
ã§ã¯ãæåã®å Žåããã€ããªãªãã§pprofãèµ·åãããšããé¢æ°åã¯ã©ãã§ååŸãããŸãããïŒ
net / http / pprofãã€ã³ããŒããããšãå¥ã®URL
/ debug / pprof / symbolãè¿œå ãã ãé¢æ°ã®ã¢ãã¬ã¹ã«ååãè¿ãããŸãã ãã®URLãç
§äŒããããšã«ãããpprofã³ãã³ãã¯é¢æ°åãååŸããŸãã
ãã ãããã®URLã¯é¢æ°ã®ãœãŒã¹ã³ãŒããè¿ãããéã¢ã»ã³ãã«ãããŸããã å解ããã«ã¯ããã€ããªãå¿
èŠã§ãããœãŒã¹ã³ãŒãã«ã¯ããã£ã¹ã¯äžã®ãã€ããªãšãœãŒã¹ã³ãŒãã®äž¡æ¹ãå¿
èŠã§ãã
泚æïŒéä¿¡ããããã€ããªãšãœãŒã¹ã³ãŒãã¯å®è¡äžã®ãã®ã§ãªããã°ãªããŸããã ããããªããšãæåŸ
ããããŒã¿ããŸã£ããåŸãããªãå¯èœæ§ããããååšããªãåé¡ãæ¢ããŸãã
pprofã¯ã©ã®ããã«æ©èœããŸããïŒ
奜å¥å¿ãããpprofãã©ã®ããã«æ©èœãã䜿çšããã¢ãããŒãã«ã©ã®ãããªæ¬ ç¹ãããããæ£ç¢ºã«èŠãŠã¿ãŸãããã
å€ãã®ããã°ã©ã ã®åäœãåæã«ä¿èšŒããããã«ãææ°ã®ãã¹ã¯ãããããã³ãµãŒããŒãªãã¬ãŒãã£ã³ã°ã·ã¹ãã ã¯ãããããããªãšã³ããã£ããã«ãã¿ã¹ã¯ãå®è£
ããŠããŸãã ããã°ã©ã ã«ã¯ãç¹å®ã®æéãšããããæ©èœããç¹å®ã®ããã»ããµãå²ãåœãŠãããŸãã ãã®æéãçµéãããšãOSã¯ããã°ã©ã ã®ä»£ããã«ãªããäœæ¥ã®æºåãã§ããŠããã°ã代ããã«å¥ã®ããã°ã©ã ãèµ·åããŸãã
ããããå²ã蟌ã¿æ©èœã¯ã©ã®ãããæ£ç¢ºã«å®è£
ãããŠããŸããïŒ çµå±ã®ãšãããOSã¯ã»ãŒåãããã°ã©ã ã§ãã åé¡ã¯ãOSãç¹å®ã®åšæ³¢æ°ã®ä¿¡å·ãã¢ã€ãã³ã«éä¿¡ããããã«èŠæ±ãããã®ä¿¡å·ã«ããã»ããµãå²ãåœãŠãããšã§ãã ã·ã°ãã«ãå°çãããšãããã»ããµã¯çŸåšå®è¡äžã®ãã¹ãŠãåæ¢ããæå®ããããã³ãã©ãŒãéå§ããŸãã ãã®ãã³ãã©ã§ã¯ãOSãçŸåšã®ããã»ã¹ã暪åãããããå¥ã®ããã»ã¹ã«çœ®ãæãããããå ŽåããããŸãã
Goãããã¡ã€ã©ãŒãåãããã«æ©èœããŸãã Goã©ã³ã¿ã€ã ã¯ãç¹å®ã®é »åºŠã§ã·ã°ãã«ïŒman setitimerïŒãéä¿¡ããããOSã«èŠæ±ãããã®ã·ã°ãã«ã«ãã³ãã©ãŒãå²ãåœãŠãŸãã ãã³ãã©ãŒã¯ããã¹ãŠã®ãŽã«ãŒãã³ïŒè±èªã®
ãŽã«ãŒãã³ ïŒã®ã¹ã¿ãã¯ãã¬ãŒã¹ãããã€ãã®è¿œå æ
å ±ãååŸããããããããã¡ãŒã«æžã蟌ãã§çµäºããŸãã
以åã®ããŒãžã§ã³ã®OS Xã®åé¡ãé¢é£ããŠããã®ã¯ãç¹å®ã®ã¹ã¬ããã«ã·ã°ãã«ãé
ä¿¡ããããã»ã¹ã®ãã°ã§ãã
ãã®ã¢ãããŒãã®æ¬ ç¹ã¯äœã§ããïŒ
- åã·ã°ãã«ã¯ã³ã³ããã¹ãã®å€åã§ãã ç©äºã¯ç§ãã¡ã®æ代ã«ã¯éåžžã«é«äŸ¡ã§ãã ãããã£ãŠãçŸåšã1ç§ããã500ãè¶
ããä¿¡å·ãåä¿¡ããã®ã¯çŸå®çã§ãã Goã®ããã©ã«ãå€ã¯1ç§ããã100ã§ãã æã«ã¯ããã§ååã§ã¯ãããŸããã
- ããšãã°ã -buildmode = c-archiveãŸãã¯-buildmode = c-sharedã䜿çšããã«ã¹ã¿ã ãã«ãã®å Žåããããã¡ã€ã©ãŒã¯ããã©ã«ãã§ã¯æ©èœããŸããã ããã¯ãOSãéä¿¡ããSIGPROFã·ã°ãã«ãGoã«ãã£ãŠå¶åŸ¡ãããŠããªãã¡ã€ã³ããã°ã©ã ã¹ããªãŒã ã«å°éãããšããäºå®ã«ãããã®ã§ãã
- Goããã°ã©ã ã§ãããŠãŒã¶ãŒç©ºéããã»ã¹ã¯ãæ žã¹ã¿ãã¯ãã¬ãŒã¹ãåä¿¡ã§ããŸããã éæé©æ§ãšåé¡ãã³ã¢ã«ããå ŽåããããŸãã
ãã¡ãããäž»ãªå©ç¹ã¯ãGoã©ã³ã¿ã€ã ããã®å
éšæ§é ã«é¢ããå®å
šãªæ
å ±ãæã£ãŠããããšã§ãã ããšãã°ãå€éšããŒã«ã¯ãããã©ã«ãã§ã¯ãŽã«ãŒãã³ã«ã€ããŠäœãç¥ããŸããã 圌ãã«ãšã£ãŠã¯ãããã»ã¹ãšã¹ã¬ããã®ã¿ã§ãã
ã·ã¹ãã ãããã¡ã€ã©ãŒ
çµã¿èŸŒã¿ã®Goãããã¡ã€ã©ãã©ã®ããã«æ©èœãããã調ã¹ãŸããã æšæºã®perfããã³SystemTap Linuxãããã¡ã€ã©ãŒãã©ã®ããã«é©çšã§ããããèŠãŠã¿ãŸãããã
ãã®èšäºã®æåã®ããã°ã©ã ãåãäžããŸãããã³ãããŒã¯ãããç¡éã«æ©èœããéåžžã®ããã°ã©ã ã«å€ããŸãã
package main import ( "regexp" "strings" ) var haystack = `Lorem ipsum dolor sit amet, consectetur adipiscing [...] Vivamus vitae nulla posuere, pellentesque quam posuere` func UsingSubstring() bool { found := strings.Contains(haystack, "auctor") return found } func UsingRegex() bool { found, _ := regexp.MatchString("auctor", haystack) return found } func main() { go func() { for { UsingSubstring() } }() for { UsingRegex() } }
ã·ã¹ãã ã¿ãã
SystemTapã¯éåžžã«åŒ·åãªãããã¡ã€ã©ãŒã§ãå°ããªèšèªã®ããã°ã©ã ãæ¬äŒŒèšèªã§äœæã§ããŸãã ãã®åŸããã®ããã°ã©ã ã¯èªåçã«Cã«å€æãããLinuxã«ãŒãã«ã¢ãžã¥ãŒã«ãšããŠã¢ã»ã³ãã«ãããããŒããå®è¡ãã¢ã³ããŒããããŸãã
SystemTapãé¢æ°ãèªèããŠãããã©ãããèŠãŠã¿ãŸãããã
$ stap -l 'process("systemtap").function("main.*")' process("systemtap").function("main.UsingRegex@main.go:16") process("systemtap").function("main.UsingSubstring@main.go:11") process("systemtap").function("main.init@main.go:32") process("systemtap").function("main.main.func1@main.go:22") process("systemtap").function("main.main@main.go:21")
圌ã¯èŠãŸãã äºæ³ã©ããããã¹ãŠã®é¢æ°ã«ã¯mainãšãããã¬ãã£ãã¯ã¹ãä»ããŠããŸãã
2ã€ã®é¢æ°ã®åäœæéã枬å®ããçµæããã¹ãã°ã©ã ã§è¡šç€ºããŠã¿ãŸãããã
SystemTapèšèªã§æ¬¡ã®ç°¡åãªã¹ã¯ãªãããäœæããŸãã é¢æ°ã®å
¥ãå£ã®æéãèšæ¶ããåºå£ã®æéã枬å®ããå·®ãèšç®ããŠä¿åããŸãã ãžã§ãã®å®äºåŸã圌ã¯ãã®æ
å ±ãå°å·ããŸãã
global etime global intervals probe $1.call { etime = gettimeofday_ns() } probe $1.return { intervals <<< (gettimeofday_ns() - etime)/1000 } probe end { printf("Duration min:%dus avg:%dus max:%dus count:%d\n", @min(intervals), @avg(intervals), @max(intervals), @count(intervals)) printf("Duration (us):\n") print(@hist_log(intervals)); printf("\n") }
ããã¿ãŒããã«ã§ããã°ã©ã ãå®è¡ããå¥ã®ã¿ãŒããã«ã§ãããã¹ã§çããŸãã
$ sudo stap main.stap 'process("systemtap").function("main.UsingSubstring")' ^CDuration min:0us avg:1us max:586us count:1628362 Duration (us): value |-------------------------------------------------- count 0 | 10 1 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 1443040 2 |@@@@@ 173089 4 | 6982 8 | 4321 16 | 631 32 | 197 64 | 74 128 | 13 256 | 4 512 | 1 1024 | 0 2048 | 0
çµæã¯åŸãããŸããããããã°ã©ã ã¯éåžžã«ããŸãæ©èœããããããšã©ãŒãçºçããŸããã
$ ./systemtap runtime: unexpected return pc for main.UsingSubstring called from 0x7fffffffe000 fatal error: unknown caller pc runtime stack: runtime.throw(0x494e40, 0x11) /home/marko/go/src/runtime/panic.go:566 +0x8b runtime.gentraceback(0xffffffffffffffff, 0xc8200337a8, 0x0, 0xc820001d40, 0x0, 0x0, 0x7fffffff, 0x7fff2fa88030, 0x0, 0x0, ...) /home/marko/go/src/runtime/traceback.go:311 +0x138c runtime.scanstack(0xc820001d40) /home/marko/go/src/runtime/mgcmark.go:755 +0x249 runtime.scang(0xc820001d40) /home/marko/go/src/runtime/proc.go:836 +0x132 runtime.markroot.func1() /home/marko/go/src/runtime/mgcmark.go:234 +0x55 runtime.systemstack(0x4e4f00) /home/marko/go/src/runtime/asm_amd64.s:298 +0x79 runtime.mstart() /home/marko/go/src/runtime/proc.go:1087
go-nutsã§ããã«é¢ããã¹ã¬ãããèŠã€ããŸãããããŸã 解決çã¯ãããŸããã ã©ããããSystemTapãããã°ã©ã ã³ãŒããå€æŽããŠé¢æ°ãã€ã³ã¿ãŒã»ããããæ¹æ³ã¯ãGCã§ã¹ã¿ãã¯ãã¬ãŒã¹ãåä¿¡ãããšãã®Goã©ã³ã¿ã€ã ã奜ãŸãªãããã§ãã
äŸå€ãåŠçãããšãã«ãC ++ã§ãåãåé¡ã
çºçããŸãã Uretprobesã¯å®ç§ã§ã¯ãããŸããã
ããŠããããã.returnãµã³ãã«ã䜿çšããªãå Žåã倧äžå€«ã§ããïŒ ãã£ãŠã¿ãŸãããã
ä¹±æ°ãååŸããŠæååã«å€æãããããã¡ã«æ ŒçŽããããã°ã©ã ã次ã«ç€ºããŸãã
package main import ( "bytes" "fmt" "math/rand" "time" ) func ToString(number int) string { return fmt.Sprintf("%d", number) } func main() { r := rand.New(rand.NewSource(time.Now().UnixNano())) var buf bytes.Buffer for i := 0; i < 1000; i++ { value := r.Int() % 1000 value = value - 500 buf.WriteString(ToString(value)) } }
æååã«å€æããæ°å€ã®ååžãäœæããã¹ã¯ãªãããäœæããŸãããã
global intervals probe process("systemtap02").function("main.ToString").call { intervals <<< $number } probe end { printf("Variables min:%dus avg:%dus max:%dus count:%d\n", @min(intervals), @avg(intervals), @max(intervals), @count(intervals)) printf("Variables:\n") print(@hist_log(intervals)); printf("\n") }
åã®ããã°ã©ã ãšã¯ç°ãªããããã°ã©ã ã¯.returnãããŒãã䜿çšããŸããããåŒæ°çªå·ãåãã䜿çšããŸãã
å®è¡ããŠäœãèµ·ãã£ãã®ããèŠãŠãã ããïŒ
$ sudo stap main.stap -c ./systemtap02 Variables min:-499us avg:8us max:497us count:1000 Variables: value |-------------------------------------------------- count -1024 | 0 -512 | 0 -256 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 249 -128 |@@@@@@@@@@@@@@@@@@@@ 121 -64 |@@@@@@@@@@ 60 -32 |@@@@@@ 36 -16 |@@ 12 -8 |@ 8 -4 | 5 -2 | 3 -1 | 2 0 | 2 1 | 2 2 | 3 4 |@ 7 8 | 4 16 |@@@ 20 32 |@@@@@ 33 64 |@@@@@@@ 44 128 |@@@@@@@@@@@@@@@@@@ 110 256 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 279 512 | 0 1024 | 0
é
åžã®ã¹ã±ãžã¥ãŒã«ããããã«ãªããŸããã
æ§èœ
perfãŠãŒãã£ãªãã£ãšperf_eventsãµãã·ã¹ãã ã¯çŸåšãLinuxã®ããã©ã«ãã®ãããã¡ã€ã©ãŒã§ãã ãœãŒã¹ãšéçºã¯äž»ã«ã«ãŒãã«ãªããžããªã«è¡ããã«ãŒãã«ãšåçã«ãªããŸãã
perf topã¯ãtopãšåæ§ã«ãæãããããªã³ãŒãããªã¢ã«ã¿ã€ã ã§è¡šç€ºããã³ãã³ãã§ãã ãã¹ãããã°ã©ã ãå®è¡ããperf topã衚瀺ãããã®ã確èªããŸãã
$ sudo perf top -p $(pidof systemtap)
ãã¹ãŠãããŸãããããã§ããœãŒã¹ããã³ãã·ã³ã³ãŒãã䜿çšãã泚éãæ©èœããŸãã
ããã§ã¯ãBrendan Greggã«ãã£ãŠæ®åããããããã
FlameGraphãæ§ç¯ããŠã¿ãŸãããã Brendanã¯çŸåšNetflixã§åããŠãããLinuxã®é©æ°çãªãããã¡ã€ãªã³ã°ã®äž»èŠãªããã¢ãŒã¿ãŒããã³ããšã³ãžã³ãã®1ã€ã§ãã
å床ãããã°ã©ã ãå®è¡ãã10ç§ä»¥å
ã«ã¹ã¿ãã¯ãã¬ãŒã¹ããã¡ã€ã«ã«åéããŸãã
$ sudo perf record -F 99 -g -p $(pidof systemtap) -- sleep 10 [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.149 MB perf.data (1719 samples) ]
Brendanã®ãŠãŒãã£ãªãã£ã䜿çšããŠãããã©ãŒãã³ã¹ããŒã¿ãFlameGraphã«å€æããŸãã
$ sudo perf script | ~/tmp/FlameGraph/stackcollapse-perf.pl > out.perf-folded $ ~/tmp/FlameGraph/flamegraph.pl out.perf-folded > perf-kernel.svg
ãããŠãããã«ç§ãã¡ãåŸããã®ããããŸãïŒ
ã芧ã®ãšãããGoã®çµã¿èŸŒã¿ãããã¡ã€ã©ãŒãšã¯ç°ãªããããã«ã¯ã«ãŒãã«ã¹ã¿ãã¯ãã¬ãŒã¹ããããŸãã
èšæ¶
CãŸãã¯C ++ã§ããã°ã©ãã³ã°ããå Žåãã¡ã¢ãªäœ¿çšéãã©ã®ããã«ãããã¡ã€ã«ããŸããïŒ
C / C ++ã®äžçã«ã¯ãã¡ã¢ãªã䜿çšãããšãã«ãšã©ãŒãæ€çŽ¢ããããã«èšèšããããŠãŒãã£ãªãã£ã§ããValgrindããããŸãïŒãªãŒã¯ãé
åã®å¢çå€ãžã®ç§»åããã§ã«è§£æŸãããã¡ã¢ãªã®äœ¿çšãªã©ã ããã¯ãã¹ãŠå¿
èŠãããŸããããªããªã Goã§ã¯ããã®ãããªåé¡ããªãããšãä¿èšŒãããŠããŸãïŒãã¡ããcgoã䜿çšããå Žåãé€ãïŒã
ããããValgrindã¯ãçµã¿èŸŒã¿ã®Massifãµãã·ã¹ãã ã䜿çšããŠãã¡ã¢ãªæ¶è²»ã䟿å©ãªã¿ã€ã ã¹ã±ãžã¥ãŒã«ã®åœ¢åŒã§è¡šç€ºããæ¹æ³ãç¥ã£ãŠããŸãã
åçŽã«å²ãåœãŠãŠãã20 MiBã®ã¡ã¢ãªã解æŸããåçŽãªCããã°ã©ã ã䜿çšããå Žå
#include <stdlib.h> #include <unistd.h> #include <string.h> int main() { const size_t MB = 1024*1024; const unsigned count = 20; char **buf = calloc(count, sizeof(*buf)); for (unsigned i = 0; i < count; i++) { buf[i] = calloc(1, MB); memset(buf[i], 0xFF, MB); sleep(1); } for (unsigned i = 0; i < count; i++) { free(buf[i]); sleep(1); } free(buf); }
Massifã®äžã§å®è¡ãããšãã¡ã¢ãªã®å²ãåœãŠãéå§ãããå Žæã®ã¹ã¿ãã¯ãã¬ãŒã¹ã§ãã®ã°ã©ãã®ãããªãã®ãåŸãããŸãã
-------------------------------------------------------------------------------- Command: ./main Massif arguments: --pages-as-heap=yes --time-unit=ms ms_print arguments: massif.out.15091 -------------------------------------------------------------------------------- MB 26.20^ :: | ::: # | @@: : #:: | ::@ : : #: :: | ::::: @ : : #: : :::: | :: : : @ : : #: : : : :: | :::: : : @ : : #: : : : : : | ::::: :: : : @ : : #: : : : : ::::: | ::: : : :: : : @ : : #: : : : : :: : @@ | ::: : : : :: : : @ : : #: : : : : :: : @ :: | ::@: : : : : :: : : @ : : #: : : : : :: : @ : ::: | ::: @: : : : : :: : : @ : : #: : : : : :: : @ : : ::: | ::: : @: : : : : :: : : @ : : #: : : : : :: : @ : : :: :: | ::: : : @: : : : : :: : : @ : : #: : : : : :: : @ : : :: : :: | ::::: : : : @: : : : : :: : : @ : : #: : : : : :: : @ : : :: : : :::: |:: : : : : : @: : : : : :: : : @ : : #: : : : : :: : @ : : :: : : : : : |@: : : : : : @: : : : : :: : : @ : : #: : : : : :: : @ : : :: : : : : :@ |@: : : : : : @: : : : : :: : : @ : : #: : : : : :: : @ : : :: : : : : :@ |@: : : : : : @: : : : : :: : : @ : : #: : : : : :: : @ : : :: : : : : :@ |@: : : : : : @: : : : : :: : : @ : : #: : : : : :: : @ : : :: : : : : :@ 0 +----------------------------------------------------------------------->s 0 39.13 Number of snapshots: 66 Detailed snapshots: [9, 10, 20, 22, 32, 42, 45 (peak), 55, 65]
Massifã¯ãã¡ã¢ãªãæäœããããã®äž»èŠãªé¢æ°ïŒmallocãcallocãreallocãmemalignãnewãnew []ïŒãç¬èªã«åå®çŸ©ããããã«æ©èœããŸãã
Goã¯ãããã®æ©èœã䜿çšããŸããã GoãœãŒã¹ã«ã¯ç¬èªã®ã¢ãã±ãŒã¿ãŒããããããã¯mmapãŸãã¯sbrkã·ã¹ãã ã³ãŒã«ã䜿çšããŠOSããã¡ã¢ãªãçŽæ¥èŠæ±ãããã§ã«ãããå°ããªæçã«åå²ããŠããŸãã
Valgrindã¯ç¹å¥ãªã³ãã³ãã©ã€ã³ãã©ã¡ãŒã¿ãŒã§èŠæ±ãããšmmap / sbrkããã£ããã§ããŸãããããã§ã圹ã«ç«ããªãã®ã¯ã第1ã«ããããã®æé«ã®å²ãåœãŠãšãªãªãŒã¹ã衚瀺ãããã第2ã«ãç解ã§ããªãããã§ããåç
§ãããªããªã£ãã¡ã¢ãªãšããŸã ãã©ã€ããã§ããã¡ã¢ãªã
C / C ++ã®äžçã®ä»ã®äžè¬çãªãŠãŒãã£ãªãã£ãã»ãšãã©åœ¹ã«ç«ããªã ãããã®ã»ãšãã©ã¯åæ§ã®æ¹æ³ã§åäœããŸãã ã¡ã¢ãªã®å²ãåœãŠãšè§£æŸã®æ©èœãååããããšã«ããã
åºæ¬çã«2ã€ã®ãªãã·ã§ã³ããããŸãã
- çè«çã«ã¯ãGoããã°ã©ã ã®å€éšïŒperfãSystemTapãªã©ïŒã§Goã©ã³ã¿ã€ã ããã¡ã¢ãªãå²ãåœãŠãã解æŸãããããæ©èœãã€ã³ã¿ãŒã»ãããããããã®åŒã³åºãã«åºã¥ããŠäœããç解ããããšããŸãã
- çµã¿èŸŒã¿ã®Goã©ã³ã¿ã€ã ã¡ã¢ãªäœ¿çšéã¢ã«ãŠã³ãã£ã³ã°ã䜿çšããŸãã
Goã¯ãå®æçã«ã¡ã¢ãªå²ãåœãŠã«é¢ããæ
å ±ãåéã§ããŸãã ãã®é »åºŠã¯æåã§èšå®ã§ããŸãããããã©ã«ãã§ã¯ãå²ãåœãŠãããã¡ã¢ãª512ãããã€ãã«ã€ã1åã§ãã
ãã€ãã®ããã«ãäŸãèŠãŠã¿ãŸãããã
äŸ
ããã»ããµã®ãããã¡ã€ãªã³ã°ãšåæ§ã«ãã¡ã¢ãªã®ãããã¡ã€ãªã³ã°ã¯ã
go test ã
runtime.MemProfileïŒïŒãžã®çŽæ¥åŒã³åºã
ããŸãã¯
net / http / pprofããã±ãŒãžã䜿çšããŠéå§ã§ã
ãŸã ã ä»åã¯ãæåŸã®ãªãã·ã§ã³ã䜿çšããŸãããã
ãã®ãããããã§ã¯ããŽã«ãŒãã³ã®1ã€ã§çµ¶ããé
åãå²ãåœãŠãŠå¥ã®é
åã«æ ŒçŽããããã°ã©ã ã瀺ããŸããä»ã®ãŽã«ãŒãã³ã§ã¯åãããšãè¡ããŸãããé
åã®é
åãå®æçã«ãå¿ãããŸãã
package main import ( "net/http" _ "net/http/pprof" "time" ) func allocAndKeep() { var b [][]byte for { b = append(b, make([]byte, 1024)) time.Sleep(time.Millisecond) } } func allocAndLeave() { var b [][]byte for { b = append(b, make([]byte, 1024)) if len(b) == 20 { b = nil } time.Sleep(time.Millisecond) } } func main() { go allocAndKeep() go allocAndLeave() http.ListenAndServe("0.0.0.0:8080", nil) }
ã€ãŸã , , , .
, .
go tool pprof , :
- alloc_space â ;
- alloc_objects â ;
- inuse_space â ;
- inuse_objects â .
, â .
,
inuse allocAndKeep(), alloc :
$ go tool pprof -inuse_space memtest http://localhost:8080/debug/pprof/heap Fetching profile from http://localhost:8080/debug/pprof/heap Saved profile in /home/marko/pprof/pprof.memtest.localhost:8080.inuse_objects.inuse_space.005.pb.gz Entering interactive mode (type "help" for commands) (pprof) top 15.36MB of 15.36MB total ( 100%) Dropped 2 nodes (cum <= 0.08MB) flat flat% sum% cum cum% 15.36MB 100% 100% 15.36MB 100% main.allocAndKeep 0 0% 100% 15.36MB 100% runtime.goexit $ go tool pprof -alloc_space memtest http://localhost:8080/debug/pprof/heap Fetching profile from http://localhost:8080/debug/pprof/heap Saved profile in /home/marko/pprof/pprof.memtest.localhost:8080.alloc_objects.alloc_space.008.pb.gz Entering interactive mode (type "help" for commands) (pprof) top 54.49MB of 54.49MB total ( 100%) Dropped 8 nodes (cum <= 0.27MB) flat flat% sum% cum cum% 27.97MB 51.33% 51.33% 29.47MB 54.08% main.allocAndKeep 23.52MB 43.17% 94.49% 25.02MB 45.92% main.allocAndLeave 3MB 5.51% 100% 3MB 5.51% time.Sleep 0 0% 100% 54.49MB 100% runtime.goexit
. , Sleep() - . .
(pprof) list time.Sleep Total: 54.49MB ROUTINE ======================== time.Sleep in /home/marko/go/src/runtime/time.go 3MB 3MB (flat, cum) 5.51% of Total . . 48:func timeSleep(ns int64) { . . 49: if ns <= 0 { . . 50: return . . 51: } . . 52: 3MB 3MB 53: t := new(timer) . . 54: t.when = nanotime() + ns . . 55: tf = goroutineReady . . 56: t.arg = getg() . . 57: lock(&timers.lock) . . 58: addtimerLocked(t)
, ,
time.Sleep() new().
(1)
, , â . , .
, .
package printtest import ( "bytes" "fmt" "testing" ) func BenchmarkPrint(b *testing.B) { var buf bytes.Buffer var s string = "test string" for i := 0; i < bN; i++ { buf.Reset() fmt.Fprintf(&buf, "string is: %s", s) } }
fmt.Fprintf() .
ᅩᅩ-benchmem test .
$ go test -bench=. -benchmem testing: warning: no tests to run BenchmarkPrint-8 10000000 128 ns/op 16 B/op 1 allocs/op PASS ok github.com/mkevac/converttest 1.420s
, 1 16 . ?
:
$ go test -bench=. -memprofile=mem.out -memprofilerate=1
memprofilerate , . , . , . .
:
$ go tool pprof -alloc_space converttest.test mem.out (pprof) top 15.41MB of 15.48MB total (99.59%) Dropped 73 nodes (cum <= 0.08MB) flat flat% sum% cum cum% 15.41MB 99.59% 99.59% 15.43MB 99.67% github.com/mkevac/converttest.BenchmarkPrint 0 0% 99.59% 15.47MB 99.93% runtime.goexit 0 0% 99.59% 15.42MB 99.66% testing.(*B).launch 0 0% 99.59% 15.43MB 99.67% testing.(*B).runN
, 15 MiB . ã©ãïŒ
(pprof) list BenchmarkPrint Total: 15.48MB ROUTINE ======================== github.com/mkevac/converttest.BenchmarkPrint in /home/marko/goprojects/src/github.com/mkevac/converttest/convert_test.go 15.41MB 15.43MB (flat, cum) 99.67% of Total . . 9:func BenchmarkPrint(b *testing.B) { . . 10: var buf bytes.Buffer . . 11: var s string = "test string" . . 12: for i := 0; i < bN; i++ { . . 13: buf.Reset() 15.41MB 15.43MB 14: fmt.Fprintf(&buf, "string is: %s", s) . . 15: } . . 16:}
fmt.Fprintf(). ããã ãããŠã©ãïŒ
(pprof) list fmt.Fprintf Total: 15.48MB ROUTINE ======================== fmt.Fprintf in /home/marko/go/src/fmt/print.go 0 12.02kB (flat, cum) 0.076% of Total . . 175:// These routines end in 'f' and take a format string. . . 176: . . 177:// Fprintf formats according to a format specifier and writes to w. . . 178:// It returns the number of bytes written and any write error encountered. . . 179:func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) { . 11.55kB 180: p := newPrinter() . 480B 181: p.doPrintf(format, a) . . 182: n, err = w.Write(p.buf) . . 183: p.free() . . 184: return . . 185:} . . 186:
. , , ⊠- . 15 , 12 . - .
:
. . 466edb: CALL bytes.(*Buffer).Reset(SB) . . 466ee0: LEAQ 0x98b6b(IP), AX . . 466ee7: MOVQ AX, 0x70(SP) . . 466eec: MOVQ $0xb, 0x78(SP) . . 466ef5: MOVQ $0x0, 0x60(SP) . . 466efe: MOVQ $0x0, 0x68(SP) . . 466f07: LEAQ 0x70d92(IP), AX . . 466f0e: MOVQ AX, 0(SP) . . 466f12: LEAQ 0x70(SP), AX . . 466f17: MOVQ AX, 0x8(SP) . . 466f1c: MOVQ $0x0, 0x10(SP) 15.41MB 15.41MB 466f25: CALL runtime.convT2E(SB) . . 466f2a: MOVQ 0x18(SP), AX . . 466f2f: MOVQ 0x20(SP), CX . . 466f34: MOVQ AX, 0x60(SP) . . 466f39: MOVQ CX, 0x68(SP) . . 466f3e: LEAQ 0x10b35b(IP), AX . . 466f45: MOVQ AX, 0(SP) . . 466f49: MOVQ 0x58(SP), AX . . 466f4e: MOVQ AX, 0x8(SP) . . 466f53: LEAQ 0x99046(IP), CX . . 466f5a: MOVQ CX, 0x10(SP) . . 466f5f: MOVQ $0xd, 0x18(SP) . . 466f68: LEAQ 0x60(SP), CX . . 466f6d: MOVQ CX, 0x20(SP) . . 466f72: MOVQ $0x1, 0x28(SP) . . 466f7b: MOVQ $0x1, 0x30(SP) . 12.02kB 466f84: CALL fmt.Fprintf(SB)
- runtime.convT2E . ããã¯äœã§ãã
,
fmt.Fprintf(): func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error)
, . , ,
void* .
«» , .. . , ? ãªãã§ïŒ Go.
Go ,
string, chan, func, slice, interface .. .
, string, .. :
â 16 . 8 â , , , , 8 â .
interface. Interface 8- .
8 â , , 8 â .
var s string = "marko" var a interface{} = &s
, 8 .
. , :
var s string = "marko" var a interface{} = s
Go
runtime.convT2E.:
16
go test.«» .
fmt.Fprintf :
package main import ( "bytes" "testing" ) func BenchmarkPrint(b *testing.B) { var buf bytes.Buffer var s string = "test string" for i := 0; i < bN; i++ { buf.Reset() buf.WriteString("string is: ") buf.WriteString(s) } }
0 :
$ go test -bench=BenchmarkPrint -benchmem testing: warning: no tests to run BenchmarkPrint-8 50000000 27.5 ns/op 0 B/op 0 allocs/op PASS ok github.com/mkevac/converttest01 1.413s
4 .
(2)
«» , cgo. (
char * ) â , , . , .
Go, , . â , .
package main import ( "fmt" ) func main() { var array = []byte{'m', 'a', 'r', 'k', 'o'} if string(array) == "marko" { fmt.Println("equal") } }
, . .
git-blame , . , , (do not escapes to heap), , .
. , , , , . runtime. , , , , 2010 , , .
.
- , Go- , escapes to heap.
:
package main import ( "bytes" "testing" "unsafe" ) var s string func BenchmarkConvert(b *testing.B) { var buf bytes.Buffer var array = []byte{'m', 'a', 'r', 'k', 'o', 0} for i := 0; i < bN; i++ { buf.Reset() s = string(array) buf.WriteString(s) } }
$ go test -bench=. -benchmem testing: warning: no tests to run BenchmarkConvert-8 30000000 42.1 ns/op 8 B/op 1 allocs/op
reflect unsafe.
func BytesToString(b []byte) string { bh := (*reflect.SliceHeader)(unsafe.Pointer(&b)) sh := reflect.StringHeader{bh.Data, bh.Len} return *(*string)(unsafe.Pointer(&sh)) } func BenchmarkNoConvert(b *testing.B) { var buf bytes.Buffer var array = []byte{'m', 'a', 'r', 'k', 'o', 0} for i := 0; i < bN; i++ { buf.Reset() s = BytesToString(array) buf.WriteString(s) } }
.
$ go test -bench=. -benchmem testing: warning: no tests to run BenchmarkConvert-8 30000000 44.5 ns/op 8 B/op 1 allocs/op BenchmarkNoConvert-8 100000000 19.2 ns/op 0 B/op 0 allocs/op PASS ok github.com/mkevac/bytetostring 3.332s
, Go â . , Go runtime , , , : , , , .. Go
runtime/trace.go .
â . , Chrome -.
, , .
äŸ
- GC- debugcharts.
runtime.ReadMemStats() , GC- .
, « », debugcharts.
package main import ( "net/http" _ "net/http/pprof" "time" _ "github.com/mkevac/debugcharts" ) func CPUHogger() { var acc uint64 t := time.Tick(2 * time.Second) for { select { case <-t: time.Sleep(50 * time.Millisecond) default: acc++ } } } func main() { go CPUHogger() go CPUHogger() http.ListenAndServe("0.0.0.0:8181", nil) }
trace, , .
10 . : runtime - , 1-3 . , Chrome JavaScript .
curl http://localhost:8181/debug/pprof/trace?seconds=10 -o trace.out
go tool trace , :
go tool trace -http "0.0.0.0:8080" ./tracetest trace.out
, :
:
, , , . , , .
, , , , :
, 4 , , 2 50 , . , - , , , debugcharts. , .
, debugcharts:
- . Debugcharts , , , . .
, , , proc stop proc start .
, , debugcharts . latency .
runtime.ReadMemStats() , .
180 // ReadMemStats populates m with memory allocator statistics. 181 func ReadMemStats(m *MemStats) { 182 stopTheWorld("read mem stats") 183 184 systemstack(func() { 185 readmemstats_m(m) 186 }) 187 188 startTheWorld() 189 }
. .
, debugcharts .
ãããã«
, , .
, Go:
- CPU;
- ;
- ;
- escape-;
- ;
- ;
- ;
- ;
- , CPU GC .
, Go , , , .
Go, , . perf SystemTap. .
, â , , , â .
, . Stay curious!
, C/C++