這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
編譯器會為某些場合進行專門最佳化,避免字串轉換時的額外分配和複製操作:
- 將[]byte轉換為string key,去map[string]查詢的時候。
- 將string轉換為[]byte,進行for range迭代時,直接取位元組賦值給局部變數。
Example Code
package mainfunc main() { m := map[string]int { "abc" : 123, } key := []byte("abc") x, ok := m[string(key)] println(x, ok)}
➜ go build -gcflags "-N -l" example.go
GDB go1.7.6 驗證成功
➜ gdb exampleGNU gdb (GDB) 8.0.1Copyright (C) 2017 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>This is free software: you are free to change and redistribute it.There is NO WARRANTY, to the extent permitted by law. Type "show copying"and "show warranty" for details.This GDB was configured as "x86_64-apple-darwin17.0.0".Type "show configuration" for configuration details.For bug reporting instructions, please see:<http://www.gnu.org/software/gdb/bugs/>.Find the GDB manual and other documentation resources online at:<http://www.gnu.org/software/gdb/documentation/>.For help, type "help".Type "apropos word" to search for commands related to "word"...Reading symbols from example...done.(gdb) b 9Breakpoint 1 at 0x2164: file /Users/anderson/Code/lang-experiments/golang/src/compiler-avoid-copy-when-transform-string/example.go, line 9.(gdb) cThe program is not being run.(gdb) rStarting program: /Users/anderson/Code/lang-experiments/golang/src/compiler-avoid-copy-when-transform-string/example [New Thread 0x2703 of process 90436]warning: unhandled dyld version (15)[New Thread 0x1907 of process 90436][New Thread 0x1a03 of process 90436][New Thread 0x2503 of process 90436][New Thread 0x2603 of process 90436]Thread 2 hit Breakpoint 1, main.main () at /Users/anderson/Code/lang-experiments/golang/src/compiler-avoid-copy-when-transform-string/example.go:99 x, ok := m[string(key)](gdb) p key$1 = {array = 0xc420047ee0 "abc", len = 3, cap = 32}(gdb) b runtime.mapaccess2_faststrBreakpoint 2 at 0x94b0: file /Users/anderson/.gvm/gos/go1.7.6/src/runtime/hashmap_fast.go, line 297.(gdb) cContinuing.Thread 2 hit Breakpoint 2, runtime.mapaccess2_faststr (t=0x5a2c0 <type.*+50144>, h=0xc42007c000, ky=..., ~r3=0x3, ~r4=32) at /Users/anderson/.gvm/gos/go1.7.6/src/runtime/hashmap_fast.go:297297 func mapaccess2_faststr(t *maptype, h *hmap, ky string) (unsafe.Pointer, bool) {(gdb) p ky$2 = 0xc420047ee0 "abc"
delve go1.7.6 驗證失敗
地址分別是:
- 0xc420047f20
- 0xc420047ea8
➜ dlv exec exampleType 'help' for list of commands.(dlv) b example.go:9Breakpoint 1 set at 0x2164 for main.main() ./example.go:9(dlv) c> main.main() ./example.go:9 (hits goroutine(1):1 total:1) (PC: 0x2164) 4: m := map[string]int { 5: "abc" : 123, 6: } 7: 8: key := []byte("abc")=> 9: x, ok := m[string(key)] 10: 11: println(x, ok) 12: }(dlv) p key[]uint8 len: 3, cap: 32, [97,98,99](dlv) p &key(*[]uint8)(0xc420047f20) # 地址是0xc420047f20(dlv) b runtime.mapaccess2_faststrBreakpoint 2 set at 0x94c3 for runtime.mapaccess2_faststr() /Users/anderson/.gvm/gos/go1.7.6/src/runtime/hashmap_fast.go:297(dlv) c> runtime.mapaccess2_faststr() /Users/anderson/.gvm/gos/go1.7.6/src/runtime/hashmap_fast.go:297 (hits goroutine(1):1 total:1) (PC: 0x94c3) 292: return unsafe.Pointer(&zeroVal[0]) 293: } 294: } 295: } 296: => 297: func mapaccess2_faststr(t *maptype, h *hmap, ky string) (unsafe.Pointer, bool) { 298: if raceenabled && h != nil { 299: callerpc := getcallerpc(unsafe.Pointer(&t)) 300: racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapaccess2_faststr)) 301: } 302: if h == nil || h.count == 0 {(dlv) p &ky(*string)(0xc420047ea8) # 地址是0xc420047ea8
LLDB go1.7.6 驗證成功
➜ lldb example(lldb) target create "example"Current executable set to 'example' (x86_64).(lldb) b example.go:9Breakpoint 1: where = example`main.main + 292 at example.go:9, address = 0x0000000000002164(lldb) rProcess 91197 launched: '/Users/anderson/Code/lang-experiments/golang/src/compiler-avoid-copy-when-transform-string/example' (x86_64)Process 91197 stopped* thread #1, stop reason = breakpoint 1.1 frame #0: 0x0000000000002164 example`main.main at example.go:9 6 } 7 8 key := []byte("abc")-> 9 x, ok := m[string(key)] 10 11 println(x, ok) 12 }Target 0: (example) stopped.(lldb) p key([]uint8) key = (len 3, cap 32) { [0] = 97 [1] = 98 [2] = 99}(lldb) p &key(*[]uint8) = 0x000000c420041ee0 (len 0, cap 0)(lldb) b runtime.mapaccess2_faststrBreakpoint 2: where = example`runtime.mapaccess2_faststr + 33 at hashmap_fast.go:302, address = 0x00000000000094d1(lldb) p key([]uint8) key = (len 3, cap 32) { [0] = 97 [1] = 98 [2] = 99}(lldb) cProcess 91197 resumingProcess 91197 stopped* thread #1, stop reason = breakpoint 2.1 frame #0: 0x00000000000094d1 example`runtime.mapaccess2_faststr(t=0x000000000005a2c0, h=0x000000c420076000, ky="abc", ~r3=0x0000000000000003, ~r4=true) at hashmap_fast.go:302 299 callerpc := getcallerpc(unsafe.Pointer(&t)) 300 racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapaccess2_faststr)) 301 }-> 302 if h == nil || h.count == 0 { 303 return unsafe.Pointer(&zeroVal[0]), false 304 } 305 if h.flags&hashWriting != 0 {Target 0: (example) stopped.(lldb) p ky(string) ky = "abc"(lldb) p &ky(*string) = 0x000000c420041ee0 ""
GDB go1.10 無法查看數組地址,無法驗證
➜ gdb example_1_10 GNU gdb (GDB) 8.0.1Copyright (C) 2017 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>This is free software: you are free to change and redistribute it.There is NO WARRANTY, to the extent permitted by law. Type "show copying"and "show warranty" for details.This GDB was configured as "x86_64-apple-darwin17.0.0".Type "show configuration" for configuration details.For bug reporting instructions, please see:<http://www.gnu.org/software/gdb/bugs/>.Find the GDB manual and other documentation resources online at:<http://www.gnu.org/software/gdb/documentation/>.For help, type "help".Type "apropos word" to search for commands related to "word"...Reading symbols from example_1_10...done.Loading Go Runtime support.(gdb) b 9Breakpoint 1 at 0x104d4b4: file /Users/anderson/Code/lang-experiments/golang/src/compiler-avoid-copy-when-transform-string/example.go, line 9.(gdb) rStarting program: /Users/anderson/Code/lang-experiments/golang/src/compiler-avoid-copy-when-transform-string/example_1_10 [New Thread 0x1903 of process 92053]warning: unhandled dyld version (15)[New Thread 0x1807 of process 92053][New Thread 0x1a03 of process 92053][New Thread 0x2503 of process 92053][New Thread 0x2603 of process 92053]Thread 2 hit Breakpoint 1, main.main () at /Users/anderson/Code/lang-experiments/golang/src/compiler-avoid-copy-when-transform-string/example.go:99 x, ok := m[string(key)](gdb) p key$1 = []uint8 = {97 'a', 98 'b', 99 'c'}(gdb) p &key$2 = []uint8 * = {97 'a', 98 'b', 99 'c'}(gdb) b runtime.mapaccess2_faststrBreakpoint 2 at 0x1007ee0: file /usr/local/Cellar/go/1.10/libexec/src/runtime/hashmap_fast.go, line 261.(gdb) cContinuing.Thread 2 hit Breakpoint 2, runtime.mapaccess2_faststr (h=0xc420057e78, ky="abc", t=0x105ad60 <type.*+55168>, ~r3=0x3, ~r4=32) at /usr/local/Cellar/go/1.10/libexec/src/runtime/hashmap_fast.go:261261 func mapaccess2_faststr(t *maptype, h *hmap, ky string) (unsafe.Pointer, bool) {(gdb) p ky$3 = "abc"(gdb) p &ky$4 = "abc"
delve go1.10 驗證失敗
地址分別是:
- 0xc420057e60
- 0xc420057dd0
➜ dlv exec example_1_10Type 'help' for list of commands.(dlv) b example.go:9Breakpoint 1 set at 0x104d4b4 for main.main() ./example.go:9(dlv) c> main.main() ./example.go:9 (hits goroutine(1):1 total:1) (PC: 0x104d4b4) 4: m := map[string]int { 5: "abc" : 123, 6: } 7: 8: key := []byte("abc")=> 9: x, ok := m[string(key)] 10: 11: println(x, ok) 12: }(dlv) p key[]uint8 len: 3, cap: 32, [97,98,99](dlv) p &key(*[]uint8)(0xc420057e60)(dlv) b runtime.mapaccess2_faststrBreakpoint 2 set at 0x1007ef3 for runtime.mapaccess2_faststr() /usr/local/Cellar/go/1.10/libexec/src/runtime/hashmap_fast.go:261(dlv) c> runtime.mapaccess2_faststr() /usr/local/Cellar/go/1.10/libexec/src/runtime/hashmap_fast.go:261 (hits goroutine(1):1 total:1) (PC: 0x1007ef3)Warning: debugging optimized function 256: } 257: } 258: return unsafe.Pointer(&zeroVal[0]) 259: } 260: => 261: func mapaccess2_faststr(t *maptype, h *hmap, ky string) (unsafe.Pointer, bool) { 262: if raceenabled && h != nil { 263: callerpc := getcallerpc() 264: racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapaccess2_faststr)) 265: } 266: if h == nil || h.count == 0 {(dlv) p ky"abc"(dlv) p &ky(*string)(0xc420057dd0)
LLDB go1.10 驗證成功
➜ lldb example_1_10 (lldb) target create "example_1_10"Current executable set to 'example_1_10' (x86_64).(lldb) b example.go:9Breakpoint 1: where = example_1_10`main.main + 372 at example.go:9, address = 0x000000000104d4b4(lldb) rProcess 91907 launched: '/Users/anderson/Code/lang-experiments/golang/src/compiler-avoid-copy-when-transform-string/example_1_10' (x86_64)Process 91907 stopped* thread #1, stop reason = breakpoint 1.1 frame #0: 0x000000000104d4b4 example_1_10`main.main at example.go:9 6 } 7 8 key := []byte("abc")-> 9 x, ok := m[string(key)] 10 11 println(x, ok) 12 }Target 0: (example_1_10) stopped.(lldb) p key([]uint8) key = (len 3, cap 32) { [0] = 97 [1] = 98 [2] = 99}(lldb) p &key(*[]uint8) = 0x000000c420057e10 (len 0, cap 0)(lldb) b runtime.mapaccess2_faststrBreakpoint 2: where = example_1_10`runtime.mapaccess2_faststr + 38 at hashmap_fast.go:266, address = 0x0000000001007f06(lldb) cProcess 91907 resumingProcess 91907 stopped* thread #1, stop reason = breakpoint 2.1 frame #0: 0x0000000001007f06 example_1_10`runtime.mapaccess2_faststr(h=0x000000c420057e78, ky="abc", t=0x000000000105ad60, ~r3=0x0000000000000003, ~r4=true) at hashmap_fast.go:266 263 callerpc := getcallerpc() 264 racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapaccess2_faststr)) 265 }-> 266 if h == nil || h.count == 0 { 267 return unsafe.Pointer(&zeroVal[0]), false 268 } 269 if h.flags&hashWriting != 0 {Target 0: (example_1_10) stopped.(lldb) p ky(string) ky = "abc"(lldb) p &ky(*string) = 0x000000c420057e10 ""(lldb)
結論
- 不知道為什麼,切換版本前後delve都驗證失敗了
- 切換版本前後,LLDB都驗證成功了
- GDB無法查看GO1.10的數組地址