performance - 在 golang 中,为什么当我使用缓冲(异步) channel 时我的程序运行速度变慢?

标签 performance go channel

我对 golang 还很陌生,所以我敢肯定这个问题主要是由于我的一些概念上的缺陷。

在 golang 中,我们可以有两种类型的 channel :无缓冲和缓冲(分别为同步和异步)。

unbufferedChan := make(chan string)
bufferedChan := make(chan string, 100)

两个通过无缓冲 channel 通信的 goroutines 必须相互等待。也就是说,接收 goroutine 阻塞直到发送方发送,发送方阻塞直到接收方接收。

在缓冲的情况下,接收者只有在 channel 为空时才会阻塞。发件人仅在 channel 已满时才阻塞。

通过使用缓冲 channel ,我希望减少 goroutine 阻塞的时间,从而提高代码速度。相反,一切都变慢了。与我的预期相反(即“缓冲更有效”),无缓冲 channel 最终要快得多。

这是所有的代码。 您需要先go get code.google.com/p/go-html-transform/html/transform

package main

import (
    "fmt"
    "math/rand"
    "os"
    "sync"
    "time"
    sel "code.google.com/p/go-html-transform/css/selector"
    h5 "code.google.com/p/go-html-transform/h5"
    gnhtml "code.google.com/p/go.net/html"
)

// Find a specific HTML element and return its textual element children.
func main() {
    test := `
<html>
  <head>
    <title>This is the test document!</title>
    <style>
      header: color=blue;
    </style>
  </head>
  <body>
    <div id="h" class="header">This is some text</div>
  </body>
</html>`

    // Get a parse tree for this HTML
    h5tree, err := h5.NewFromString(test)
    if err != nil {
        die(err)
    }
    n := h5tree.Top()

    // Create a Chain object from a CSS selector statement
    chn, err := sel.Selector("#h")
    if err != nil {
        die(err)
    }

    // Find the item.  Should be a div node with the text "This is some text"
    h := chn.Find(n)[0]

    // run our little experiment this many times total
    var iter int = 100000

    // When buffering, how large shall the buffer be?
    var bufSize uint = 100

    // Keep a running total of the number of times we've tried buffered
    //   and unbuffered channels.
    var bufCount int = 0
    var unbufCount int =0

    // Keep a running total of the number of nanoseconds that have gone by.
    var bufSum int64 = 0
    var unbufSum int64 = 0

    // Call the function {iter} times, randomly choosing whether to use a
    //   buffered or unbuffered channel.
    for i := 0; i < iter; i++ {
        if rand.Float32() < 0.5 {
            // No buffering
            unbufCount += 1
            startTime := time.Now()
            getAllText(h, 0)
            unbufSum += time.Since(startTime).Nanoseconds()     
        } else {
            // Use buffering
            bufCount += 1
            startTime := time.Now()
            getAllText(h, bufSize)
            bufSum += time.Since(startTime).Nanoseconds()
        }
    }
    unbufAvg := unbufSum / int64(unbufCount)
    bufAvg := bufSum / int64(bufCount)

    fmt.Printf("Unbuffered average time (ns): %v\n", unbufAvg)
    fmt.Printf("Buffered average time (ns): %v\n", bufAvg)
}

// Kill the program and report the error
func die(err error) {
    fmt.Printf("Terminating: %v\n", err.Error())
    os.Exit(1)
}

// Walk through all of a nodes children and construct a string consisting
//   of c.Data where c.Type == TextNode
func getAllText(n *gnhtml.Node, bufSize uint) string {
    var texts chan string
    if bufSize == 0 {
        // unbuffered, synchronous
        texts = make(chan string)
    } else {
        // buffered, asynchronous
        texts = make(chan string, bufSize)
    }

    wg := sync.WaitGroup{}


    // Go walk through all n's child nodes, sending only textual data
    //   over the texts channel.
    wg.Add(1)
    nTree := h5.NewTree(n)
    go func () {
        nTree.Walk(func (c *gnhtml.Node) {
            if c.Type == gnhtml.TextNode {
                texts <- c.Data
            }
        })

        close(texts)
        wg.Done()
    }()

    // As text data comes in over the texts channel, build up finalString
    wg.Add(1)
    finalString := ""
    go func () {
        for t := range texts {
            finalString += t
        }

        wg.Done()
    }()

    // Return finalString once both of the goroutines have finished.
    wg.Wait()
    return finalString
}

程序运行10次,结果如下:

Buffered average time (ns): 32088
Buffered average time (ns): 32183
Buffered average time (ns): 35091
Buffered average time (ns): 35798
Buffered average time (ns): 36966
Buffered average time (ns): 38707
Buffered average time (ns): 39464
Buffered average time (ns): 40021
Buffered average time (ns): 41063
Buffered average time (ns): 46248
Unbuffered average time (ns): 18265
Unbuffered average time (ns): 18804
Unbuffered average time (ns): 20268
Unbuffered average time (ns): 20401
Unbuffered average time (ns): 21652
Unbuffered average time (ns): 22630
Unbuffered average time (ns): 22907
Unbuffered average time (ns): 23326
Unbuffered average time (ns): 24133
Unbuffered average time (ns): 27546

最佳答案

实际上,我可能刚刚想通了。我认为额外的时间是由于创建了一个 100 元素的缓冲 channel 。

我再次运行程序,这次使用的缓冲区大小为 10(仍然比我的小测试程序需要的多)而不是 100(原始缓冲区大小。)

结果如下:

Buffered average time (ns): 21930
Buffered average time (ns): 22721
Buffered average time (ns): 23011
Buffered average time (ns): 23707
Buffered average time (ns): 27701
Buffered average time (ns): 28325
Buffered average time (ns): 28851
Buffered average time (ns): 29641
Buffered average time (ns): 30417
Buffered average time (ns): 32600
Unbuffered average time (ns): 21077
Unbuffered average time (ns): 21490
Unbuffered average time (ns): 22332
Unbuffered average time (ns): 22584
Unbuffered average time (ns): 26438
Unbuffered average time (ns): 26824
Unbuffered average time (ns): 27322
Unbuffered average time (ns): 27926
Unbuffered average time (ns): 27985
Unbuffered average time (ns): 30322

这些数字彼此更接近。

关于performance - 在 golang 中,为什么当我使用缓冲(异步) channel 时我的程序运行速度变慢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16701870/

相关文章:

performance - 为什么数据结构对齐对性能很重要?

go - 调用 ExecuteTemplate 收到 i/o 超时错误

golang - 使用数学大包的模数

c# - 为什么 WCF/Mongo 抛出异常 : An item with the same key has already been added

走例程:Making concurrent API requests

jquery - 如何在jsp中创建搜索文本框?

performance - Couchdb 1.5 和 Node JS 查询服务器

c# - ConfigurationManager.AppSettings 性能问题

go - Golang 的 Gradle(在 Java 项目中使用/构建一些 .go)

flash - channel.position永远不会等于as3中的sound.length