4

私はgolangにかなり慣れていないので、この質問は主に私の側の概念的な欠点によるものだと確信しています。

golang では、非バッファ型とバッファ型 (それぞれ同期と非同期) の 2 種類のチャネルを使用できます。

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

バッファリングされていないチャネルを介して通信する 2 つのゴルーチンは、互いに待機する必要があります。つまり、受信ゴルーチンは送信者が送信するまでブロックし、送信者は受信者が受信するまでブロックします。

バッファリングの場合、レシーバはチャネルが空の場合にのみブロックします。送信者は、チャネルがいっぱいの場合にのみブロックします。

バッファリングされたチャネルを使用することで、ゴルーチンがブロックしている時間を短縮し、コードの速度を向上できると期待していました。代わりに、すべてが遅くなりました。私の予想に反して (つまり、「バッファリングはより効率的です」)、バッファリングされていないチャネルはかなり高速になりました。

これがすべてのコードです。最初に必要になります。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
4

2 に答える 2

2

テストでのバッファの作成を外部化します:

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
            texts := make(chan string)
            startTime := time.Now()
            getAllText(h, 0, texts)
            unbufSum += time.Since(startTime).Nanoseconds()     
        } else {
            // Use buffering
            bufCount += 1
            texts := make(chan string, bufSize)
            startTime := time.Now()
            getAllText(h, bufSize, texts)
            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, texts chan string) string {
    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
}

これにより(タイマーを開始する前にチャネル作成を移動)、次の結果が得られます。

Buffered average time (ns): 2649
Buffered average time (ns): 2655
Buffered average time (ns): 2657
Buffered average time (ns): 2695
Buffered average time (ns): 2695
Buffered average time (ns): 2699
Buffered average time (ns): 2719
Buffered average time (ns): 2724
Buffered average time (ns): 2793
Buffered average time (ns): 3101
Unbuffered average time (ns): 2997
Unbuffered average time (ns): 3005
Unbuffered average time (ns): 3027
Unbuffered average time (ns): 3031
Unbuffered average time (ns): 3048
Unbuffered average time (ns): 3062
Unbuffered average time (ns): 3086
Unbuffered average time (ns): 3092
Unbuffered average time (ns): 3095
Unbuffered average time (ns): 3191

改善するための解決策は、可能であればチャネルを開いたままにしておくことです。

于 2013-05-28T19:00:12.550 に答える