Golang函数参数是否作为copy-on-write传递?

时间:2015-11-30 09:49:46

标签: go parameter-passing copy-on-write

我有以下功能:

func checkFiles(path string, excludedPatterns []string) {
    // ...
}

我想知道,因为excludedPatterns永远不会改变,我应该通过使var全局(而不是每次都将它传递给函数)来优化它,或者Golang是否已经通过将它们作为副本传递来处理它上写?

编辑:我想我可以将切片作为指针传递,但我仍然想知道写时复制行为(如果它存在)以及一般来说我是否应该担心通过值传递或通过指针。

2 个答案:

答案 0 :(得分:10)

从功能名称来看,即使考虑将参数移动到全局变量只是为了节省将它们作为参数传递所需的时间/空间,性能也是至关重要的(像检查文件这样的IO操作非常多比调用函数和向它们传递值慢。

Go中的切片只是一个小描述符,类似于带有指向后备数组的指针的结构,以及2 int s,长度和容量。无论后备数组有多大,传递切片总是有效的,你甚至不应该考虑将指针传递给它们,除非你想要修改切片头。

Go中的参数始终按值传递,并传递值的副本。如果传递指针,则将复制并传递指针值。传递切片时,将复制并传递切片值(这是一个小描述符) - 这将指向相同的后备数组(不会被复制)。

此外,如果您需要在函数中多次访问切片,则参数通常是额外的增益,因为编译器可以进行进一步的优化/缓存,而如果它是全局变量,则必须更加小心。

有关切片及其内部的更多信息:Go Slices: usage and internals

如果你想要性能上的确切数字,基准!

这里有一个基准测试代码,它显示了两个解决方案之间没有区别(将切片作为参数传递或访问全局切片)。将其保存到slices_test.go等文件中,然后使用go test -bench .

运行
package main

import (
    "testing"
)

var gslice = make([]string, 1000)

func global(s string) {
    for i := 0; i < 100; i++ { // Cycle to access slice may times
        _ = s
        _ = gslice // Access global-slice
    }
}

func param(s string, ss []string) {
    for i := 0; i < 100; i++ { // Cycle to access slice may times
        _ = s
        _ = ss // Access parameter-slice
    }
}

func BenchmarkParameter(b *testing.B) {
    for i := 0; i < b.N; i++ {
        param("hi", gslice)
    }
}

func BenchmarkGlobal(b *testing.B) {
    for i := 0; i < b.N; i++ {
        global("hi")
    }
}

示例输出:

testing: warning: no tests to run
PASS
BenchmarkParameter-2    30000000                55.4 ns/op
BenchmarkGlobal-2       30000000                55.1 ns/op
ok      _/V_/workspace/IczaGo/src/play  3.569s

答案 1 :(得分:0)

Pi带@icza的出色答案,还有另一种方法将切片作为参数传递:指向切片的指针。

当您需要修改基础切片时,可以使用全局变量切片,但是将其作为参数不起作用传递,则可以有效地处理副本。为了减轻这种情况,实际上可以将切片作为指针传递。

足够有趣,它实际上比访问全局变量要快:

package main

import (
    "testing"
)

var gslice = make([]string, 1000000)

func global(s string) {
    for i := 0; i < 100; i++ { // Cycle to access slice may times
        _ = s
        _ = gslice // Access global-slice
    }
}

func param(s string, ss []string) {
    for i := 0; i < 100; i++ { // Cycle to access slice may times
        _ = s
        _ = ss // Access parameter-slice
    }
}

func paramPointer(s string, ss *[]string) {
    for i := 0; i < 100; i++ { // Cycle to access slice may times
        _ = s
        _ = ss // Access parameter-slice
    }
}

func BenchmarkParameter(b *testing.B) {
    for i := 0; i < b.N; i++ {
        param("hi", gslice)
    }
}

func BenchmarkParameterPointer(b *testing.B) {
    for i := 0; i < b.N; i++ {
        paramPointer("hi", &gslice)
    }
}

func BenchmarkGlobal(b *testing.B) {
    for i := 0; i < b.N; i++ {
        global("hi")
    }
}

结果:

goos: darwin
goarch: amd64
pkg: untitled
BenchmarkParameter-8            24437403                48.2 ns/op
BenchmarkParameterPointer-8     27483115                40.3 ns/op
BenchmarkGlobal-8               25631470                46.0 ns/op