为什么将指针传递给通道速度较慢
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 语句。
其次:Nanoseconds
和Nanosecond
有一个重要的区别
我试图以这种方式评估你的观察*:
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
我是 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 语句。
其次:Nanoseconds
和Nanosecond
我试图以这种方式评估你的观察*:
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