golang 函数传参 string 和*string 效率一样么 - V2EX
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 8140 次点击
    这是一个创建于 2425 天前的主题,其中的信息可能已经有所发展或是发生改变。


    我测了一下发现差不多(循环 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, gclocals1a65e721a2ccc325b382662e7ffee780(SB)
    0x000e 00014 (teststr.go:3) FUNCDATA $1, gclocals69c1753bd5f81501d95132d08af04464(SB)
    0x000e 00014 (teststr.go:3) FUNCDATA $3, gclocals9fb7f0986f647f17cb53dda1484e0f7a(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, gclocals1a65e721a2ccc325b382662e7ffee780(SB)
    0x000e 00014 (teststr.go:7) FUNCDATA $1, gclocals69c1753bd5f81501d95132d08af04464(SB)
    0x000e 00014 (teststr.go:7) FUNCDATA $3, gclocals33cdeccccebe80329f1fdbee7f5874cb(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     Solana     5080 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 51ms UTC 05:43 PVG 13:43 LAX 22:43 JFK 01:43
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86