为什么将指针传递给通道速度较慢

Why passing pointers to channel is slower

我是 golang 的新手,正在尝试用 golang 重写我的 java 服务器项目。

我发现,与传递值相比,将指针传递到通道会导致性能下降近 30%。

这是一个示例片段: 包主 进口 ( "time" "fmt" )

var c = make(chan t, 1024)
// var c = make(chan *t, 1024)
type t struct {
    a uint
    b uint
}

func main() {

    start := time.Now()
    for i := 0; i < 1000; i++ {
        b := t{a:3, b:5}
        // c <- &b
        c <- b
    }
    elapsed := time.Since(start)
    fmt.Println(t2)
}

更新。修复包丢失

作为一个值,它可以被堆栈分配:

go run -gcflags '-m' tmp.go
# command-line-arguments
./tmp.go:18: inlining call to time.Time.Nanosecond
./tmp.go:24: inlining call to time.Time.Nanosecond
./tmp.go:25: t2 escapes to heap
./tmp.go:25: main ... argument does not escape
63613

作为指针,逃逸到堆中:

go run -gcflags '-m' tmp.go
# command-line-arguments
./tmp.go:18: inlining call to time.Time.Nanosecond
./tmp.go:24: inlining call to time.Time.Nanosecond
./tmp.go:21: &b escapes to heap <-- Additional GC pressure
./tmp.go:20: moved to heap: b   <-- 
./tmp.go:25: t2 escapes to heap
./tmp.go:25: main ... argument does not escape
122513

转义到堆会引入一些开销/GC 压力。

看汇编,指针版本还引入了额外的指令,包括:

go run -gcflags '-S' tmp.go
0x0055 00085 (...tmp.go:18) CALL    runtime.newobject(SB)

非指针变体在调用 runtime.chansend1 之前不会产生此开销。

作为对的补充,必须补充一点,你测量的方式很可疑。此类小程序的性能差异很大,因此应反复测量。你的例子也有一些错误。

首先:它没有编译,因为缺少 package 语句。

其次:NanosecondsNanosecond

有一个重要的区别

我试图以这种方式评估你的观察*:

package main

import (
    "time"
    "fmt"
)

const (
    chan_size = 1000
    cycle_count = 1000
)

var (
    v_ch = make(chan t, chan_size)
    p_ch = make(chan *t, chan_size)
)

type t struct {
    a uint
    b uint
}

func fill_v() {
    for i := 0; i < chan_size; i++ {
        b := t{a:3, b:5}
        v_ch <- b
    }
}

func fill_p() {
    for i := 0; i < chan_size; i++ {
        b := t{a:3, b:5}
        p_ch <- &b
    }
}

func measure_f(f func()) int64 {
    start := time.Now()
    f();
    elapsed := time.Since(start)
    return elapsed.Nanoseconds()
}

func main() {

    var v_nanos int64 = 0
    var p_nanos int64 = 0
    for i := 0; i<cycle_count; i++ {
        v_nanos += measure_f(fill_v);
        for i := 0; i < chan_size; i++ {
            _ = <- v_ch
        }
    }
    for i := 0; i<cycle_count; i++ {
        p_nanos += measure_f(fill_p);
        for i := 0; i < chan_size; i++ {
            _ = <- p_ch
        }
    }
    fmt.Println(
        "v:",v_nanos/cycle_count, 
        " p:", p_nanos/cycle_count, 
        "ratio (v/p):", float64(v_nanos)/float64(p_nanos))
}

确实有可测量的性能下降(我这样定义下降drop=1-(candidate/optimum)),但是尽管我重复代码 1000 次,它在 25% 和50%,我什至不确定堆是如何回收的,什么时候回收的,所以可能很难量化


*ideone

上查看 "running" 演示

...请注意,stdout 已冻结:v: 34875 p: 59420 ratio (v/p)0.586923845267128

由于某些原因,无法 运行 this code in the Go Playground