在Go语言中,哪种通道类型使用的内存最少?

huangapple go评论82阅读模式
英文:

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 (
    &quot;fmt&quot;
    &quot;time&quot;
)

func routine(stopChan chan bool) {
    fmt.Println(&quot;goroutine: I&#39;ve started!&quot;)
    &lt;-stopChan
    fmt.Println(&quot;goroutine: Cya&#39;round pal&quot;)
}

func main() {
    fmt.Println(&quot;main: Sample program run started&quot;)

    stopChan := make(chan bool)

    go routine(stopChan)

    fmt.Println(&quot;main: Fired up the goroutine&quot;)

    stopChan &lt;- true

    time.Sleep(1 * time.Second)

    fmt.Println(&quot;main: Sample program run finished&quot;)
}

// Sample output:
//
//	main: Sample program run started
//	main: Fired up the goroutine
//	goroutine: I&#39;ve started!
//	goroutine: Cya&#39;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 (
	&quot;fmt&quot;
	&quot;time&quot;
)

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(&quot;done&quot;)
	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.

huangapple
  • 本文由 发表于 2015年6月11日 05:19:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/30767510.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定