V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
EEer
V2EX  ›  Go 编程语言

golang 函数传参 string 和*string 效率一样么

  •  
  •   EEer · 2019-03-04 00:17:21 +08:00 · 7796 次点击
    这是一个创建于 2125 天前的主题,其中的信息可能已经有所发展或是发生改变。


    我测了一下发现差不多(循环 1000000000 比较调用时间),string 不是传值拷贝么,效率不明显低点?
    代码

    
    package main
    
    import (
    	"fmt"
    	"time"
    )
    
    func test(str *string) int{
    
    	return len(*str)
    }
    func test2(str string) int{
    
    	return len(str)
    }
    func main() {
    	str := "abcdgjsiogjerhodsifjtudfmjadskdospfiorejtgdfkhkhijdsuyfuirnfclkvjkhiiddusyfuewndsodsdsafdfsyfuewndsodsdsafdfsyfuewndsodsdsafdfsyfuewndsodsdsafdfsyfuewndsodsdsafdfsyfuewndsodsdsafdfsyfuewndsodsdsafdfsyfuewndsodsdsafdfsyfuewndsodsdsafdfsyfuewndsodsdsafdfsyfuewndsodsdsafdf"
    
    	time1:=time.Now().UnixNano() / int64(time.Millisecond)
    	for i:=0;i<1000000000;i++{
    		test2(str)
    	}
    	time2 := time.Now().UnixNano() / int64(time.Millisecond)
    
    	for i:=0;i<1000000000;i++{
    		test(&str)
    	}
    	time3 :=time.Now().UnixNano() / int64(time.Millisecond)
    	fmt.Println("milliseconds:,",time2-time1)
    	fmt.Println("milliseconds,",time3-time2)
    }
    
    


    运行结果

    milliseconds:, 535

    milliseconds, 523
    就慢 12ms,2%。。。好像没啥差距啊。(多跑几次甚至有时候后者更慢)

    15 条回复    2019-03-04 11:26:08 +08:00
    pkokp8
        1
    pkokp8  
       2019-03-04 00:27:36 +08:00 via Android
    试试修改字符串?
    没有修改,会不会被优化了,刚开始学 go,我猜的
    blless
        2
    blless  
       2019-03-04 00:31:27 +08:00 via Android
    没记错 go 的 string 应该是一个特殊的 Slice,所谓的值传递只是传这个特殊 slice 的结构体而已。
    zzzzzzzzzp
        3
    zzzzzzzzzp  
       2019-03-04 00:34:12 +08:00 via iPhone
    String 不可变,实际上传递的就是指针,所以理论上应该传 string*更慢一些?
    jybox
        4
    jybox  
       2019-03-04 00:40:27 +08:00
    https://go101.org/article/string.html

    按照这里的说法 string 是一个特殊的内部类型,在复制的时候复制的是指向底层字节数组的指针而不是字符串本身。然后因为 string 类型是不可变的,所以也不需要考虑字符串被修改的问题。
    rrfeng
        5
    rrfeng  
       2019-03-04 00:48:14 +08:00 via Android
    显然应该拿 struct 试啊
    wisej
        6
    wisej  
       2019-03-04 00:57:40 +08:00 via Android   ❤️ 1
    string 实际上是不可变的字节切片,所以虽然是值传递,且切片是值类型,但是由于切片结构体并不是存储的底层数组,而是指向数组的指针,所以实际上开销很小
    sulinehk
        7
    sulinehk  
       2019-03-04 05:05:49 +08:00 via Android
    string 包含指向字节数组的指针和表示字节数的 int
    ethego
        8
    ethego  
       2019-03-04 07:56:21 +08:00
    没有什么区别,string 传递时也是指针。
    reus
        9
    reus  
       2019-03-04 08:54:30 +08:00
    复制 string,相当于复制 reflect.StringHeader,64bit 机器上是 16 字节,*string 是 8 字节。复制 16 字节和复制 8 字节是没有区别的,你观察到的性能差距只是抖动而已。
    xfriday
        10
    xfriday  
       2019-03-04 09:38:24 +08:00
    cow
    madiks
        11
    madiks  
       2019-03-04 09:46:31 +08:00
    type stringStruct struct {
    str unsafe.Pointer
    len int
    }

    https://golang.org/src/runtime/string.go
    wweir
        12
    wweir  
       2019-03-04 10:01:14 +08:00
    string 是引用类型,用指针反而多了层指针的转换
    wingoo
        13
    wingoo  
       2019-03-04 10:14:15 +08:00
    lz 应该比较的是 string vs []byte
    whitehack
        14
    whitehack  
       2019-03-04 11:16:56 +08:00
    我觉得你应该从汇编的角度来分析。

    teststr.go
    ```
    package main

    func test(str *string) int {

    return len(*str)
    }
    func test2(str string) int {

    return len(str)
    }

    func main() {
    s :="a"

    test2(s)
    test(&s)


    }


    ```

    汇编

    ```
    "".test STEXT nosplit size=53 args=0x10 locals=0x10
    0x0000 00000 (teststr.go:3) TEXT "".test(SB), NOSPLIT, $16-16
    0x0000 00000 (teststr.go:3) SUBQ $16, SP
    0x0004 00004 (teststr.go:3) MOVQ BP, 8(SP)
    0x0009 00009 (teststr.go:3) LEAQ 8(SP), BP
    0x000e 00014 (teststr.go:3) FUNCDATA $0, gclocals·1a65e721a2ccc325b382662e7ffee780(SB)
    0x000e 00014 (teststr.go:3) FUNCDATA $1, gclocals·69c1753bd5f81501d95132d08af04464(SB)
    0x000e 00014 (teststr.go:3) FUNCDATA $3, gclocals·9fb7f0986f647f17cb53dda1484e0f7a(SB)
    0x000e 00014 (teststr.go:3) PCDATA $2, $0
    0x000e 00014 (teststr.go:3) PCDATA $0, $0
    0x000e 00014 (teststr.go:3) MOVQ $0, "".~r1+32(SP)
    0x0017 00023 (teststr.go:5) PCDATA $2, $1
    0x0017 00023 (teststr.go:5) PCDATA $0, $1
    0x0017 00023 (teststr.go:5) MOVQ "".str+24(SP), AX
    0x001c 00028 (teststr.go:5) TESTB AL, (AX)
    0x001e 00030 (teststr.go:5) PCDATA $2, $0
    0x001e 00030 (teststr.go:5) MOVQ 8(AX), AX
    0x0022 00034 (teststr.go:5) MOVQ AX, ""..autotmp_2(SP)
    0x0026 00038 (teststr.go:5) MOVQ AX, "".~r1+32(SP)
    0x002b 00043 (teststr.go:5) MOVQ 8(SP), BP
    0x0030 00048 (teststr.go:5) ADDQ $16, SP
    0x0034 00052 (teststr.go:5) RET

    ```


    ```
    "".test2 STEXT nosplit size=47 args=0x18 locals=0x10
    0x0000 00000 (teststr.go:7) TEXT "".test2(SB), NOSPLIT, $16-24
    0x0000 00000 (teststr.go:7) SUBQ $16, SP
    0x0004 00004 (teststr.go:7) MOVQ BP, 8(SP)
    0x0009 00009 (teststr.go:7) LEAQ 8(SP), BP
    0x000e 00014 (teststr.go:7) FUNCDATA $0, gclocals·1a65e721a2ccc325b382662e7ffee780(SB)
    0x000e 00014 (teststr.go:7) FUNCDATA $1, gclocals·69c1753bd5f81501d95132d08af04464(SB)
    0x000e 00014 (teststr.go:7) FUNCDATA $3, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
    0x000e 00014 (teststr.go:7) PCDATA $2, $0
    0x000e 00014 (teststr.go:7) PCDATA $0, $0
    0x000e 00014 (teststr.go:7) MOVQ $0, "".~r1+40(SP)
    0x0017 00023 (teststr.go:9) PCDATA $0, $1
    0x0017 00023 (teststr.go:9) MOVQ "".str+32(SP), AX
    0x001c 00028 (teststr.go:9) MOVQ AX, ""..autotmp_2(SP)
    0x0020 00032 (teststr.go:9) MOVQ AX, "".~r1+40(SP)
    0x0025 00037 (teststr.go:9) MOVQ 8(SP), BP
    0x002a 00042 (teststr.go:9) ADDQ $16, SP
    0x002e 00046 (teststr.go:9) RET

    ```

    test 指针参数代码都比 不传指针的多。

    指针的计算要多一个操作


    下面是调用



    ```
    // 定义字符串
    0x0024 00036 (teststr.go:13) LEAQ go.string."a"(SB), AX
    0x002b 00043 (teststr.go:13) MOVQ AX, "".s+24(SP)
    0x0030 00048 (teststr.go:13) MOVQ $1, "".s+32(SP)


    // 传值
    0x0039 00057 (teststr.go:15) PCDATA $2, $0
    // StringSlice 结构
    0x0039 00057 (teststr.go:15) MOVQ AX, (SP)
    0x003d 00061 (teststr.go:15) MOVQ $1, 8(SP)
    0x0046 00070 (teststr.go:15) CALL "".test2(SB)


    // 传指针
    0x004b 00075 (teststr.go:16) LEAQ "".s+24(SP), AX
    0x0050 00080 (teststr.go:16) PCDATA $2, $0
    // Stringslice 指针
    0x0050 00080 (teststr.go:16) MOVQ AX, (SP)
    0x0054 00084 (teststr.go:16) CALL "".test(SB)
    ```
    wweir
        15
    wweir  
       2019-03-04 11:26:08 +08:00
    @wingoo 都是引用类型,效率是一致的
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5923 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 43ms · UTC 06:19 · PVG 14:19 · LAX 22:19 · JFK 01:19
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.