英文:
Which channel type uses the least amount of memory in Go?
问题
我发现自己经常使用通道来停止事物。在这些情况下,通道仅被用作信号的手段,而没有实际使用任何数据。
例如:
package main
import (
"fmt"
"time"
)
func routine(stopChan chan bool) {
fmt.Println("goroutine: I've started!")
<-stopChan
fmt.Println("goroutine: Cya'round pal")
}
func main() {
fmt.Println("main: Sample program run started")
stopChan := make(chan bool)
go routine(stopChan)
fmt.Println("main: Fired up the goroutine")
stopChan <- true
time.Sleep(1 * time.Second)
fmt.Println("main: Sample program run finished")
}
// 示例输出:
//
// main: Sample program run started
// main: Fired up the goroutine
// goroutine: I've started!
// goroutine: Cya'round pal
// main: Sample program run finished
如果您愿意,请在golang playground上运行/查看它。
我的问题是:
在Go语言中,哪种通道类型的内存占用最小?
例如,bool
通道是否比empty struct{}
通道需要更少的开销?
chan bool
chan byte
chan interface{}
chan struct{}
...
其他的什么?
英文:
I find myself frequently using channels to get things to stop. In these cases the channel is being used solely as a means of signaling, and none of the data is actually used.
For example:
package main
import (
"fmt"
"time"
)
func routine(stopChan chan bool) {
fmt.Println("goroutine: I've started!")
<-stopChan
fmt.Println("goroutine: Cya'round pal")
}
func main() {
fmt.Println("main: Sample program run started")
stopChan := make(chan bool)
go routine(stopChan)
fmt.Println("main: Fired up the goroutine")
stopChan <- true
time.Sleep(1 * time.Second)
fmt.Println("main: Sample program run finished")
}
// Sample output:
//
// main: Sample program run started
// main: Fired up the goroutine
// goroutine: I've started!
// goroutine: Cya'round pal
// main: Sample program run finished
Run/view it if you please on the golang playground.
My question is:
Which channel type has the lightest memory footprint in Go?
e.g. Is a bool chan going to require any less overhead than an empty struct{} chan?
chan bool
chan byte
chan interface{}
chan struct{}
...
something else?
答案1
得分: 15
看着这个通道的最新实现,它并不是一个简单的结构:
type hchan struct {
qcount uint // 队列中的数据总数
dataqsiz uint // 循环队列的大小
buf unsafe.Pointer // 指向包含dataqsiz个元素的数组
elemsize uint16
closed uint32
elemtype *_type // 元素类型
sendx uint // 发送索引
recvx uint // 接收索引
recvq waitq // 接收等待者列表
sendq waitq // 发送等待者列表
lock mutex
}
等待者队列的元素也相当重:
type sudog struct {
g *g
selectdone *uint32
next *sudog
prev *sudog
elem unsafe.Pointer // 数据元素
releasetime int64
nrelease int32 // -1 表示获取
waitlink *sudog // g.waiting 列表
}
你看,非常多的字节。即使为一个空通道创建任何元素,这也是可以忽略不计的。
然而,我期望所有空通道占用相同的空间,不论底层类型如何,所以如果你只打算关闭通道,就没有区别(实际的元素似乎由指针持有)。一个快速的测试支持这一点:
package main
import (
"fmt"
"time"
)
func main() {
// 通道类型
type xchan chan [64000]byte
a := make([]xchan, 10000000) // 1000 万
for n := range a {
a[n] = make(xchan)
}
fmt.Println("done")
time.Sleep(time.Minute)
}
在我的 64 位机器上,我看不出chan struct{}
和chan [64000]byte
之间的区别,两者都导致大约 1GB 的内存使用,这使我相信创建一个单独的通道的开销大约在 100 字节左右。
总之,这并不重要。个人而言,我会使用struct{}
,因为它是唯一真正的空类型(确实大小为 0),清楚地表明没有发送任何有效载荷的意图。
英文:
Looking at the latest implementation of the channel, it's not a trivial structure:
type hchan struct {
qcount uint // total data in the queue
dataqsiz uint // size of the circular queue
buf unsafe.Pointer // points to an array of dataqsiz elements
elemsize uint16
closed uint32
elemtype *_type // element type
sendx uint // send index
recvx uint // receive index
recvq waitq // list of recv waiters
sendq waitq // list of send waiters
lock mutex
}
Elements of waiter queues are also quite heavy:
type sudog struct {
g *g
selectdone *uint32
next *sudog
prev *sudog
elem unsafe.Pointer // data element
releasetime int64
nrelease int32 // -1 for acquire
waitlink *sudog // g.waiting list
}
You see, many bytes. Even if any element would be created for an empty channel, this would be negligible.
However, I expect all empty channels to take the same amount of space, regardless of underlying type, so if you intend to only close the channel, there'll be no difference (an actual element seems to be hold by a pointer). A quick test backs it up:
package main
import (
"fmt"
"time"
)
func main() {
// channel type
type xchan chan [64000]byte
a := make([]xchan, 10000000) // 10 million
for n := range a {
a[n] = make(xchan)
}
fmt.Println("done")
time.Sleep(time.Minute)
}
I see no difference between chan struct{}
and chan [64000]byte
, both leads to ~1GB of usage on my 64-bit machine, which makes me believe that overhead of creating a single channel in somewhere around 100 bytes.
In conclusion, it doesn't really matter. Personally I would use struct{}
as it's the only really empty type (of size 0 indeed), clearly indicating there is no intention of any payload being sent.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论