首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何有效利用渠道

如何有效利用渠道
EN

Stack Overflow用户
提问于 2021-02-03 07:27:26
回答 1查看 86关注 0票数 1

我在优步风格指南上读到,最多应该使用1的通道长度。

虽然我很清楚,使用100或1000个通道大小是非常糟糕的做法,但是我想知道为什么10的通道大小不能被认为是一个有效的选择。为了得出正确的结论,,我遗漏了一些部分。

下面,您可以遵循我的论点(和反参数)支持的一些基准测试。

我知道,如果你的两个去例程,负责从这个频道写或读,会在连续的写作或阅读之间被其他的IO动作打断,那么从一个更高的通道缓冲器中得不到任何收益,我同意1是最好的选择。

但是,让我们说,除了对通道的写/读所导致的隐式锁定和解锁之外,没有其他重要的常规切换需要。然后我要总结如下:

考虑使用大小为1和10的通道缓冲区在通道上处理100个值时,上下文开关的数量(GR =go-例程)

  • Buffer=1:(GR1插入1值,GR2读取1值)X 100 ~ 200 go-例程开关
  • Buffer=10:(GR1插入10个值,GR2读取10个值)X 10 ~20个go-例程开关

我做了一些基准测试,以证明这实际上更快:

代码语言:javascript
复制
package main

import (
    "testing"
)

type a struct {
    b [100]int64
}

func BenchmarkBuffer1(b *testing.B) {
    count := 0
    c := make(chan a, 1)
    go func() {

        for i := 0; i < b.N; i++ {
            c <- a{}
        }
        close(c)
    }()
    for v := range c {
        for i := range v.b {
            count += i
        }
    }
}

func BenchmarkBuffer10(b *testing.B) {
    count := 0
    c := make(chan a, 10)
    go func() {

        for i := 0; i < b.N; i++ {
            c <- a{}
        }
        close(c)
    }()
    for v := range c {
        for i := range v.b {
            count += i
        }
    }
}

结果在比较简单读写+非阻塞处理时:

代码语言:javascript
复制
BenchmarkBuffer1-12              5072902               266 ns/op
BenchmarkBuffer10-12             6029602               179 ns/op
PASS
BenchmarkBuffer1-12              5228782               256 ns/op
BenchmarkBuffer10-12             5392410               216 ns/op
PASS
BenchmarkBuffer1-12              4806208               287 ns/op
BenchmarkBuffer10-12             4637842               233 ns/op
PASS

然而,如果我每10次读取一次睡眠,它不会产生任何更好的结果。

代码语言:javascript
复制
import (
    "testing"
    "time"
)

func BenchmarkBuffer1WithSleep(b *testing.B) {
    count := 0
    c := make(chan int, 1)
    go func() {
        for i := 0; i < b.N; i++ {
            c <- i
        }
        close(c)
    }()
    for a := range c {
        count++
        if count%10 == 0 {
            time.Sleep(time.Duration(a) * time.Nanosecond)
        }
    }
}

func BenchmarkBuffer10WithSleep(b *testing.B) {
    count := 0
    c := make(chan int, 10)
    go func() {
        for i := 0; i < b.N; i++ {
            c <- i
        }
        close(c)
    }()
    for a := range c {
        count++
        if count%10 == 0 {
            time.Sleep(time.Duration(a) * time.Nanosecond)
        }
    }
}

结果每10次增加一次睡眠时:

代码语言:javascript
复制
BenchmarkBuffer1WithSleep-12              856886             53219 ns/op
BenchmarkBuffer10WithSleep-12             929113             56939 ns/op

FYI:我还用一个CPU做了一次测试,得到了以下结果:

代码语言:javascript
复制
BenchmarkBuffer1                 5831193               207 ns/op
BenchmarkBuffer10                6226983               180 ns/op
BenchmarkBuffer1WithSleep         556635             35510 ns/op
BenchmarkBuffer10WithSleep        984472             61434 ns/op
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-02-03 07:57:42

当然,上限500的信道没有任何问题,例如,如果使用此信道作为信号量。

您阅读的样式指南建议不要使用缓冲通道,比方说第64页,“因为这看起来是个不错的数字”。但是这个推荐并不是因为性能!(顺便说一句:你的微基准是无用的微基准,它们不衡量任何相关的东西。)

无缓冲信道是一种同步原语,我们非常有用。

缓冲通道可能会在发送方和接收方之间缓冲,而这种缓冲对于观察、调优和调试代码可能会有问题(因为创建和消耗是进一步解耦的)。这就是为什么样式指南推荐未缓冲的通道(或者最多为1,因为这有时是正确性所必需的!)。

它也不禁止更大的缓冲区上限:

除0或1大小以外的任何其他大小都必须接受高级别的审查。考虑大小是如何确定的,在加载和阻塞写入器下,是什么阻止了通道的填充,以及当发生这种情况时会发生什么。空的。我的

如果您可以解释为什么27 (而不是22或31)以及这将如何影响程序行为(不仅是性能!),您可以使用27上限。如果缓冲区已被填充。

大多数人高估了表现。正确性、操作稳定性和可维护性是第一位的。这就是这个风格指南的意义所在。

票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/66022963

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档