小编典典

Golang 函数参数是否作为写时复制传递?

go

我有以下功能:

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

我想知道,因为excludedPatterns从不改变,我应该通过使 var 全局化(而不是每次都将它传递给函数)来优化它,还是 Golang 已经通过将它们作为写时复制传递来处理这个问题?

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


阅读 180

收藏
2021-12-14

共1个答案

小编典典

从您的函数名称来看,性能并不是那么重要,甚至考虑将参数移动到全局变量只是为了节省将它们作为参数传递所需的时间/空间(像检查文件这样的 IO 操作比调用函数和将值传递给他们)。

Go 中的切片只是小的描述符,类似于带有指向后备数组的指针和 2 ints、长度和容量的结构体。无论后备数组有多大,传递切片总是有效的,您甚至不应该考虑向它们传递指针,除非您当然想修改切片头。

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

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

如果您想要性能的确切数字,请进行基准测试!

这里有一些基准测试代码,它显示了 2 个解决方案之间没有区别(将切片作为参数传递或访问全局切片)。将它保存到一个文件中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
2021-12-14