go 实践小技巧

118 阅读5分钟

入参为interface 会触发变量逃逸

以fmt.Sprintf 举例说明

func main() {
	hello := "hello"
	fmt.Println(fmt.Sprintf("%s", hello))
}

go build -gcflags=-m main.go

# command-line-arguments
./main.go:9:13: inlining call to fmt.Println
./main.go:9:13: ... argument does not escape
./main.go:9:25: fmt.Sprintf("%s", ... argument...) escapes to heap
./main.go:9:25: ... argument does not escape
./main.go:9:32: hello escapes to heap

发现变量hello逃逸到堆上了

更进一步的,查看反编译代码,可以发现调用了 convTstring 将 字符串 转换成interface(eface)结构

  1. go build -gcflags "-N -l" -o main main.go
  2. go tool objdump -s "main.main" main
TEXT main.main(SB) /home/liujun.helloworld/app/go/src/go_test/test_cal/main.go
  main.go:7             0x80da6a0               658b0d00000000          MOVL GS:0, CX
  main.go:7             0x80da6a7               8b89fcffffff            MOVL 0xfffffffc(CX), CX
  main.go:7             0x80da6ad               3b6108                  CMPL SP, 0x8(CX)
  main.go:7             0x80da6b0               0f8667010000            JBE 0x80da81d
  main.go:7             0x80da6b6               83ec64                  SUBL $0x64, SP
  main.go:8             0x80da6b9               8d05a63e0f08            LEAL 0x80f3ea6, AX
  main.go:8             0x80da6bf               8944241c                MOVL AX, 0x1c(SP)
  main.go:8             0x80da6c3               c744242005000000        MOVL $0x5, 0x20(SP)
  main.go:9             0x80da6cb               c744245c00000000        MOVL $0x0, 0x5c(SP)
  main.go:9             0x80da6d3               c744246000000000        MOVL $0x0, 0x60(SP)
  main.go:9             0x80da6db               8d44245c                LEAL 0x5c(SP), AX
  main.go:9             0x80da6df               8944243c                MOVL AX, 0x3c(SP)
  main.go:9             0x80da6e3               8b44241c                MOVL 0x1c(SP), AX
  main.go:9             0x80da6e7               8b4c2420                MOVL 0x20(SP), CX
  main.go:9             0x80da6eb               890424                  MOVL AX, 0(SP)
  main.go:9             0x80da6ee               894c2404                MOVL CX, 0x4(SP)
  main.go:9             0x80da6f2               e89916fdff              CALL runtime.convTstring(SB)
  main.go:9             0x80da6f7               8b442408                MOVL 0x8(SP), AX
  main.go:9             0x80da6fb               89442438                MOVL AX, 0x38(SP)
  main.go:9             0x80da6ff               8b4c243c                MOVL 0x3c(SP), CX
  main.go:9             0x80da703               8401                    TESTB AL, 0(CX)
  main.go:9             0x80da705               8d15204c0e08            LEAL 0x80e4c20, DX
  main.go:9             0x80da70b               8911                    MOVL DX, 0(CX)
  main.go:9             0x80da70d               8b1598901908            MOVL runtime.writeBarrier(SB), DX
  main.go:9             0x80da713               85d2                    TESTL DX, DX
  main.go:9             0x80da715               7402                    JE 0x80da719
  main.go:9             0x80da717               eb02                    JMP 0x80da71b
  main.go:9             0x80da719               eb0f                    JMP 0x80da72a
  main.go:9             0x80da71b               e8c0abfdff              CALL runtime.gcWriteBarrier2(SB)
  main.go:9             0x80da720               8907                    MOVL AX, 0(DI)
  main.go:9             0x80da722               8b5104                  MOVL 0x4(CX), DX
  main.go:9             0x80da725               895704                  MOVL DX, 0x4(DI)
  main.go:9             0x80da728               eb00                    JMP 0x80da72a
  main.go:9             0x80da72a               894104                  MOVL AX, 0x4(CX)
  main.go:9             0x80da72d               8b44243c                MOVL 0x3c(SP), AX
  main.go:9             0x80da731               8400                    TESTB AL, 0(AX)
  main.go:9             0x80da733               eb00                    JMP 0x80da735
  main.go:9             0x80da735               89442440                MOVL AX, 0x40(SP)
  main.go:9             0x80da739               c744244401000000        MOVL $0x1, 0x44(SP)
  main.go:9             0x80da741               c744244801000000        MOVL $0x1, 0x48(SP)
  main.go:9             0x80da749               8d0d263d0f08            LEAL 0x80f3d26, CX
  main.go:9             0x80da74f               890c24                  MOVL CX, 0(SP)
  main.go:9             0x80da752               c744240402000000        MOVL $0x2, 0x4(SP)
  main.go:9             0x80da75a               89442408                MOVL AX, 0x8(SP)
  main.go:9             0x80da75e               c744240c01000000        MOVL $0x1, 0xc(SP)
  main.go:9             0x80da766               c744241001000000        MOVL $0x1, 0x10(SP)
  main.go:9             0x80da76e               e80d95ffff              CALL fmt.Sprintf(SB)
  main.go:9             0x80da773               8b442414                MOVL 0x14(SP), AX
  main.go:9             0x80da777               8b4c2418                MOVL 0x18(SP), CX
  main.go:9             0x80da77b               89442454                MOVL AX, 0x54(SP)
  main.go:9             0x80da77f               894c2458                MOVL CX, 0x58(SP)
  main.go:9             0x80da783               c744244c00000000        MOVL $0x0, 0x4c(SP)
  main.go:9             0x80da78b               c744245000000000        MOVL $0x0, 0x50(SP)
  main.go:9             0x80da793               8d44244c                LEAL 0x4c(SP), AX
  main.go:9             0x80da797               89442428                MOVL AX, 0x28(SP)
  main.go:9             0x80da79b               8b442454                MOVL 0x54(SP), AX
  main.go:9             0x80da79f               8b4c2458                MOVL 0x58(SP), CX
  main.go:9             0x80da7a3               890424                  MOVL AX, 0(SP)
  main.go:9             0x80da7a6               894c2404                MOVL CX, 0x4(SP)
  main.go:9             0x80da7aa               e8e115fdff              CALL runtime.convTstring(SB)
  main.go:9             0x80da7af               8b442408                MOVL 0x8(SP), AX
  main.go:9             0x80da7b3               89442424                MOVL AX, 0x24(SP)
  main.go:9             0x80da7b7               8b4c2428                MOVL 0x28(SP), CX
  main.go:9             0x80da7bb               8401                    TESTB AL, 0(CX)
  main.go:9             0x80da7bd               8d15204c0e08            LEAL 0x80e4c20, DX
  main.go:9             0x80da7c3               8911                    MOVL DX, 0(CX)
  main.go:9             0x80da7c5               8b1598901908            MOVL runtime.writeBarrier(SB), DX
  main.go:9             0x80da7cb               85d2                    TESTL DX, DX
  main.go:9             0x80da7cd               7402                    JE 0x80da7d1
  main.go:9             0x80da7cf               eb02                    JMP 0x80da7d3
  main.go:9             0x80da7d1               eb0f                    JMP 0x80da7e2
  main.go:9             0x80da7d3               e808abfdff              CALL runtime.gcWriteBarrier2(SB)
  main.go:9             0x80da7d8               8907                    MOVL AX, 0(DI)
  main.go:9             0x80da7da               8b5104                  MOVL 0x4(CX), DX
  main.go:9             0x80da7dd               895704                  MOVL DX, 0x4(DI)
  main.go:9             0x80da7e0               eb00                    JMP 0x80da7e2
  main.go:9             0x80da7e2               894104                  MOVL AX, 0x4(CX)
  main.go:9             0x80da7e5               8b442428                MOVL 0x28(SP), AX
  main.go:9             0x80da7e9               8400                    TESTB AL, 0(AX)
  main.go:9             0x80da7eb               eb00                    JMP 0x80da7ed
  main.go:9             0x80da7ed               8944242c                MOVL AX, 0x2c(SP)
  main.go:9             0x80da7f1               c744243001000000        MOVL $0x1, 0x30(SP)
  main.go:9             0x80da7f9               c744243401000000        MOVL $0x1, 0x34(SP)
  main.go:9             0x80da801               890424                  MOVL AX, 0(SP)
  main.go:9             0x80da804               c744240401000000        MOVL $0x1, 0x4(SP)
  main.go:9             0x80da80c               c744240801000000        MOVL $0x1, 0x8(SP)
  main.go:9             0x80da814               e8d795ffff              CALL fmt.Println(SB)
  main.go:10            0x80da819               83c464                  ADDL $0x64, SP
  main.go:10            0x80da81c               c3                      RET
  main.go:7             0x80da81d               e88e97fdff              CALL runtime.morestack_noctxt(SB)
  main.go:7             0x80da822               e979feffff              JMP main.main(SB)

convTstring 方法涉及 堆内存分配 必知必会系列-interface

// convTstring should be an internal detail,
// but widely used packages access it using linkname.
// Notable members of the hall of shame include:
//   - github.com/bytedance/sonic
//
// Do not remove or change the type signature.
// See go.dev/issue/67401.
//
//go:linkname convTstring
func convTstring(val string) (x unsafe.Pointer) {
	if val == "" {
		x = unsafe.Pointer(&zeroVal[0])
	} else {
		x = mallocgc(unsafe.Sizeof(val), stringType, true)
		*(*string)(x) = val
	}
	return
}

旁路逻辑如何不增加主逻辑耗时?

使用闭包捕获需要的变量 最后异步执行,从而保证旁路逻辑不影响主流程耗时

type UpdLog struct {
	ToExec []func(*UpdLog)
}

type UpdRequest struct {
}

func Upd(ctx context.Context, upd *UpdRequest) {
	updLog := &UpdLog{}
	result1 := DoUpdStepOne()  // 主流程 1
	updLog.DoThingA(result1)   // 旁路逻辑 依赖主流程1 耗时操作
        
	result2 := DoUpdStepTwo()  // 主流程 2
	updLog.DoThingB(result2)   // 旁路逻辑 依赖主流程2 耗时操作
        
	DoUpdStepThree()           // 主流程 3
        
        // 异步处理
	go func() {
		updLog.DoThings()
	}()
}

type StepResult struct {
}

func DoUpdStepOne() *StepResult {
	return nil
}

func DoUpdStepTwo() *StepResult {
	return nil
}

func DoUpdStepThree() *StepResult {
	return nil
}

func (u *UpdLog) DoThingA(r *StepResult) {
	u.ToExec = append(u.ToExec, func(u *UpdLog) {
		// 耗时操作
	})
}

func (u *UpdLog) DoThingB(r *StepResult) {
	u.ToExec = append(u.ToExec, func(u *UpdLog) {
		// 耗时操作
	})
}

func (u *UpdLog) DoThings() {
	for _, f := range u.ToExec {
		f(u)
	}
}