performance - 当我将slice参数作为值或指针传递时,为什么会有性能差异?

标签 performance go pointers slice

我有以下代码:

func AddToSliceByValue(mySlice []int) {
    for idx := range mySlice {
        mySlice[idx]++
    }
}

func AddToSliceByPointer(mySlice *[]int) {
    for idx := range *mySlice {
        (*mySlice)[idx]++
    }
}
我的第一个想法是性能应该几乎相同,因为按值传递会复制 slice header ,而按指针传递会迫使我取消对指针的引用,但我的基准显示了其他内容:
func BenchmarkAddByValue(b *testing.B) {
    mySlice := rand.Perm(1000)
    for n := 0; n < b.N; n++ {
        AddToSliceByValue(mySlice)
    }
}

func BenchmarkAddByPointer(b *testing.B) {
    mySlice := rand.Perm(1000)
    for n := 0; n < b.N; n++ {
        AddToSliceByPointer(&mySlice)
    }
}
BenchmarkAddByValue-12 1151256 1035 ns/op
BenchmarkAddByPointer-12 2145110 525 ns/op
谁能向我解释为什么性能差异如此之大?
我还为这两个函数添加了汇编代码。
按值传递的汇编代码:
TEXT main.AddToSliceByValue(SB) /go_test/pointer/pointer_value.go
  pointer_value.go:4    0x1056f60       488b442410      MOVQ 0x10(SP), AX   
  pointer_value.go:4    0x1056f65       488b4c2408      MOVQ 0x8(SP), CX    
  pointer_value.go:4    0x1056f6a       31d2            XORL DX, DX     
  pointer_value.go:4    0x1056f6c       eb0e            JMP 0x1056f7c       
  pointer_value.go:5    0x1056f6e       488b1cd1        MOVQ 0(CX)(DX*8), BX    
  pointer_value.go:5    0x1056f72       48ffc3          INCQ BX         
  pointer_value.go:5    0x1056f75       48891cd1        MOVQ BX, 0(CX)(DX*8)    
  pointer_value.go:4    0x1056f79       48ffc2          INCQ DX         
  pointer_value.go:4    0x1056f7c       4839c2          CMPQ AX, DX     
  pointer_value.go:4    0x1056f7f       7ced            JL 0x1056f6e        
  pointer_value.go:4    0x1056f81       c3          RET         
  :-1           0x1056f82       cc          INT $0x3        
  :-1           0x1056f83       cc          INT $0x3        
  :-1           0x1056f84       cc          INT $0x3        
  :-1           0x1056f85       cc          INT $0x3        
  :-1           0x1056f86       cc          INT $0x3        
  :-1           0x1056f87       cc          INT $0x3        
  :-1           0x1056f88       cc          INT $0x3        
  :-1           0x1056f89       cc          INT $0x3        
  :-1           0x1056f8a       cc          INT $0x3        
  :-1           0x1056f8b       cc          INT $0x3        
  :-1           0x1056f8c       cc          INT $0x3        
  :-1           0x1056f8d       cc          INT $0x3        
  :-1           0x1056f8e       cc          INT $0x3        
  :-1           0x1056f8f       cc          INT $0x3        

TEXT main.main(SB) /go_test/pointer/pointer_value.go
  pointer_value.go:9    0x1056f90       65488b0c2530000000  MOVQ GS:0x30, CX            
  pointer_value.go:9    0x1056f99       483b6110        CMPQ 0x10(CX), SP           
  pointer_value.go:9    0x1056f9d       0f86a8000000        JBE 0x105704b               
  pointer_value.go:9    0x1056fa3       4883ec70        SUBQ $0x70, SP              
  pointer_value.go:9    0x1056fa7       48896c2468      MOVQ BP, 0x68(SP)           
  pointer_value.go:9    0x1056fac       488d6c2468      LEAQ 0x68(SP), BP           
  pointer_value.go:11   0x1056fb1       488d7c2418      LEAQ 0x18(SP), DI           
  pointer_value.go:11   0x1056fb6       0f57c0          XORPS X0, X0                
  pointer_value.go:11   0x1056fb9       488d7fd0        LEAQ -0x30(DI), DI          
  pointer_value.go:11   0x1056fbd       48896c24f0      MOVQ BP, -0x10(SP)          
  pointer_value.go:11   0x1056fc2       488d6c24f0      LEAQ -0x10(SP), BP          
  pointer_value.go:11   0x1056fc7       e849c6ffff      CALL 0x1053615              
  pointer_value.go:11   0x1056fcc       488b6d00        MOVQ 0(BP), BP              
  pointer_value.go:11   0x1056fd0       48c744242001000000  MOVQ $0x1, 0x20(SP)         
  pointer_value.go:11   0x1056fd9       48c744242802000000  MOVQ $0x2, 0x28(SP)         
  pointer_value.go:11   0x1056fe2       48c744243003000000  MOVQ $0x3, 0x30(SP)         
  pointer_value.go:11   0x1056feb       48c744243804000000  MOVQ $0x4, 0x38(SP)         
  pointer_value.go:11   0x1056ff4       48c744244005000000  MOVQ $0x5, 0x40(SP)         
  pointer_value.go:11   0x1056ffd       48c744244806000000  MOVQ $0x6, 0x48(SP)         
  pointer_value.go:11   0x1057006       48c744245007000000  MOVQ $0x7, 0x50(SP)         
  pointer_value.go:11   0x105700f       48c744245808000000  MOVQ $0x8, 0x58(SP)         
  pointer_value.go:11   0x1057018       48c744246009000000  MOVQ $0x9, 0x60(SP)         
  pointer_value.go:12   0x1057021       488d442418      LEAQ 0x18(SP), AX           
  pointer_value.go:12   0x1057026       48890424        MOVQ AX, 0(SP)              
  pointer_value.go:12   0x105702a       48c74424080a000000  MOVQ $0xa, 0x8(SP)          
  pointer_value.go:12   0x1057033       48c74424100a000000  MOVQ $0xa, 0x10(SP)         
  pointer_value.go:12   0x105703c       e81fffffff      CALL main.AddToSliceByValue(SB)     
  pointer_value.go:13   0x1057041       488b6c2468      MOVQ 0x68(SP), BP           
  pointer_value.go:13   0x1057046       4883c470        ADDQ $0x70, SP              
  pointer_value.go:13   0x105704a       c3          RET                 
  pointer_value.go:9    0x105704b       e8909cffff      CALL runtime.morestack_noctxt(SB)   
  pointer_value.go:9    0x1057050       e93bffffff      JMP main.main(SB)           
指针传递的汇编代码:
TEXT main.AddToSliceByPointer(SB) /go_test/pointer/pointer_ref.go
  pointer_ref.go:3  0x1056f60       4883ec18        SUBQ $0x18, SP          
  pointer_ref.go:3  0x1056f64       48896c2410      MOVQ BP, 0x10(SP)       
  pointer_ref.go:3  0x1056f69       488d6c2410      LEAQ 0x10(SP), BP       
  pointer_ref.go:4  0x1056f6e       488b542420      MOVQ 0x20(SP), DX       
  pointer_ref.go:4  0x1056f73       488b5a08        MOVQ 0x8(DX), BX        
  pointer_ref.go:4  0x1056f77       31c0            XORL AX, AX         
  pointer_ref.go:4  0x1056f79       eb0e            JMP 0x1056f89           
  pointer_ref.go:5  0x1056f7b       488b3cc6        MOVQ 0(SI)(AX*8), DI        
  pointer_ref.go:5  0x1056f7f       48ffc7          INCQ DI             
  pointer_ref.go:5  0x1056f82       48893cc6        MOVQ DI, 0(SI)(AX*8)        
  pointer_ref.go:4  0x1056f86       48ffc0          INCQ AX             
  pointer_ref.go:4  0x1056f89       4839d8          CMPQ BX, AX         
  pointer_ref.go:4  0x1056f8c       7d0e            JGE 0x1056f9c           
  pointer_ref.go:5  0x1056f8e       488b4a08        MOVQ 0x8(DX), CX        
  pointer_ref.go:5  0x1056f92       488b32          MOVQ 0(DX), SI          
  pointer_ref.go:5  0x1056f95       4839c8          CMPQ CX, AX         
  pointer_ref.go:5  0x1056f98       72e1            JB 0x1056f7b            
  pointer_ref.go:5  0x1056f9a       eb0a            JMP 0x1056fa6           
  pointer_ref.go:4  0x1056f9c       488b6c2410      MOVQ 0x10(SP), BP       
  pointer_ref.go:4  0x1056fa1       4883c418        ADDQ $0x18, SP          
  pointer_ref.go:4  0x1056fa5       c3          RET             
  pointer_ref.go:5  0x1056fa6       e8b5c4ffff      CALL runtime.panicIndex(SB) 
  pointer_ref.go:5  0x1056fab       90          NOPL                
  :-1           0x1056fac       cc          INT $0x3            
  :-1           0x1056fad       cc          INT $0x3            
  :-1           0x1056fae       cc          INT $0x3            
  :-1           0x1056faf       cc          INT $0x3            

TEXT main.main(SB) /go_test/pointer/pointer_ref.go
  pointer_ref.go:9  0x1056fb0       65488b0c2530000000  MOVQ GS:0x30, CX            
  pointer_ref.go:9  0x1056fb9       483b6110        CMPQ 0x10(CX), SP           
  pointer_ref.go:9  0x1056fbd       0f86b2000000        JBE 0x1057075               
  pointer_ref.go:9  0x1056fc3       4883ec78        SUBQ $0x78, SP              
  pointer_ref.go:9  0x1056fc7       48896c2470      MOVQ BP, 0x70(SP)           
  pointer_ref.go:9  0x1056fcc       488d6c2470      LEAQ 0x70(SP), BP           
  pointer_ref.go:11 0x1056fd1       488d7c2408      LEAQ 0x8(SP), DI            
  pointer_ref.go:11 0x1056fd6       0f57c0          XORPS X0, X0                
  pointer_ref.go:11 0x1056fd9       488d7fd0        LEAQ -0x30(DI), DI          
  pointer_ref.go:11 0x1056fdd       48896c24f0      MOVQ BP, -0x10(SP)          
  pointer_ref.go:11 0x1056fe2       488d6c24f0      LEAQ -0x10(SP), BP          
  pointer_ref.go:11 0x1056fe7       e829c6ffff      CALL 0x1053615              
  pointer_ref.go:11 0x1056fec       488b6d00        MOVQ 0(BP), BP              
  pointer_ref.go:11 0x1056ff0       48c744241001000000  MOVQ $0x1, 0x10(SP)         
  pointer_ref.go:11 0x1056ff9       48c744241802000000  MOVQ $0x2, 0x18(SP)         
  pointer_ref.go:11 0x1057002       48c744242003000000  MOVQ $0x3, 0x20(SP)         
  pointer_ref.go:11 0x105700b       48c744242804000000  MOVQ $0x4, 0x28(SP)         
  pointer_ref.go:11 0x1057014       48c744243005000000  MOVQ $0x5, 0x30(SP)         
  pointer_ref.go:11 0x105701d       48c744243806000000  MOVQ $0x6, 0x38(SP)         
  pointer_ref.go:11 0x1057026       48c744244007000000  MOVQ $0x7, 0x40(SP)         
  pointer_ref.go:11 0x105702f       48c744244808000000  MOVQ $0x8, 0x48(SP)         
  pointer_ref.go:11 0x1057038       48c744245009000000  MOVQ $0x9, 0x50(SP)         
  pointer_ref.go:11 0x1057041       488d442408      LEAQ 0x8(SP), AX            
  pointer_ref.go:11 0x1057046       4889442458      MOVQ AX, 0x58(SP)           
  pointer_ref.go:11 0x105704b       48c74424600a000000  MOVQ $0xa, 0x60(SP)         
  pointer_ref.go:11 0x1057054       48c74424680a000000  MOVQ $0xa, 0x68(SP)         
  pointer_ref.go:12 0x105705d       488d442458      LEAQ 0x58(SP), AX           
  pointer_ref.go:12 0x1057062       48890424        MOVQ AX, 0(SP)              
  pointer_ref.go:12 0x1057066       e8f5feffff      CALL main.AddToSliceByPointer(SB)   
  pointer_ref.go:13 0x105706b       488b6c2470      MOVQ 0x70(SP), BP           
  pointer_ref.go:13 0x1057070       4883c478        ADDQ $0x78, SP              
  pointer_ref.go:13 0x1057074       c3          RET                 
  pointer_ref.go:9  0x1057075       e8669cffff      CALL runtime.morestack_noctxt(SB)   
  pointer_ref.go:9  0x105707a       e931ffffff      JMP main.main(SB)           

        

最佳答案

我无法复制您的基准...

package main_test

import (
    "math/rand"
    "testing"
)

func AddToSliceByValue(mySlice []int) {
    for idx := range mySlice {
        mySlice[idx]++
    }
}

func AddToSliceByPointer(mySlice *[]int) {
    for idx := range *mySlice {
        (*mySlice)[idx]++
    }
}

func BenchmarkAddByValue(b *testing.B) {
    mySlice := rand.Perm(1000)
    for n := 0; n < b.N; n++ {
        AddToSliceByValue(mySlice)
    }
}

func BenchmarkAddByPointer(b *testing.B) {
    mySlice := rand.Perm(1000)
    for n := 0; n < b.N; n++ {
        AddToSliceByPointer(&mySlice)
    }
}
$ go test -bench=. -benchmem -count=4
goos: linux
goarch: amd64
pkg: test/bencslice
BenchmarkAddByValue-4        3010280           385 ns/op           0 B/op          0 allocs/op
BenchmarkAddByValue-4        3118990           385 ns/op           0 B/op          0 allocs/op
BenchmarkAddByValue-4        3117450           384 ns/op           0 B/op          0 allocs/op
BenchmarkAddByValue-4        3109251           386 ns/op           0 B/op          0 allocs/op
BenchmarkAddByPointer-4      2012487           610 ns/op           0 B/op          0 allocs/op
BenchmarkAddByPointer-4      2009690           594 ns/op           0 B/op          0 allocs/op
BenchmarkAddByPointer-4      2009222           594 ns/op           0 B/op          0 allocs/op
BenchmarkAddByPointer-4      1850820           596 ns/op           0 B/op          0 allocs/op
PASS
ok      test/bencslice  13.476s
$ go version
go version go1.15.2 linux/amd64
无论如何,行为可能取决于许多因素,首先是运行时的版本。只要您可以测试,复制和监视,了解内在的知识就没什么用了。

关于performance - 当我将slice参数作为值或指针传递时,为什么会有性能差异?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64889763/

相关文章:

java - 我可以进一步改善此正则表达式的性能吗

go - 在 Go 可执行文件中包含模板文件

go - 如何通过寻呼机将缓冲区打印到标准输出?

postgresql - Golang SSH 隧道连接到远程 postgres 数据库

c - 调用 free() 时出现段错误

C指针循环中的奇怪行为

c - 二维字符数组中的 C 指针问题

java - 在 Java 中实现 Serializable 的惩罚?

java - 跟踪频率时线程安全哈希表放置的可扩展模式

database - 在 SQL Server 中查询只读数据库时,可用 RAM 的充分增加是否可以消除 tempdb 的使用?