FAQ
Hello,

I'd like to use a select loop and channels to multiplex queues of requests.
  However, I find that adding a case to a select loop can drastically lower
performance. I have a little benchmark which compares a select loop,
varying the number of cases, with a loop based on checking time. Each
iteration I read a map and increment an integer. The code is
here: https://gist.github.com/narula/67db0e1af075c389525b

Here are the results:

cases: -1 nitr: 22085384 time: 2.000022843s rate: 1.1042565877333833e+07
// for loop
cases: 0 nitr: 21855653 time: 2.000045463s rate: 1.0927578099758424e+07
// select loop with default case only
cases: 1 nitr: 26015331 time: 2.016238151s rate: 1.2902905833369482e+07
// select loop with default + 1 case
cases: 2 nitr: 6073945 time: 2.016959415s rate: 3.011436400171691e+06
// select loop with default + 2 cases
cases: 3 nitr: 5544622 time: 2.015555947s rate: 2.7509144602275826e+06 //
select loop with default + 3 cases

Why the significant drop in throughput? Here's the cpuprofiling for all
the cases. As you can see, once there is more than one case, more and more
of the cpu time is spent in selectgo.

Should I use a select loop when I have <= 1 case and a for loop otherwise?
  Is something especially slow about a ticker channel?

cases = -1
Total: 200 samples
       92 46.0% 46.0% 104 52.0% runtime.mapaccess1_fast64
       58 29.0% 75.0% 58 29.0% ExternalCode
       33 16.5% 91.5% 142 71.0% main.func·001
       12 6.0% 97.5% 12 6.0% runtime.aeshash64
        4 2.0% 99.5% 5 2.5% time.Now
        1 0.5% 100.0% 1 0.5% time.now
        0 0.0% 100.0% 58 29.0% System
        0 0.0% 100.0% 142 71.0% runtime.gosched0

cases = 0
Total: 199 samples
       94 47.2% 47.2% 108 54.3% runtime.mapaccess1_fast64
       46 23.1% 70.4% 46 23.1% ExternalCode
       37 18.6% 88.9% 153 76.9% main.func·001
       14 7.0% 96.0% 14 7.0% runtime.aeshash64
        5 2.5% 98.5% 8 4.0% time.Now
        3 1.5% 100.0% 3 1.5% time.now
        0 0.0% 100.0% 46 23.1% System
        0 0.0% 100.0% 153 76.9% runtime.gosched0

cases = 1
Total: 200 samples
      100 50.0% 50.0% 113 56.5% runtime.mapaccess1_fast64
       39 19.5% 69.5% 39 19.5% runtime.xchg
       16 8.0% 77.5% 200 100.0% main.func·001
       13 6.5% 84.0% 13 6.5% runtime.aeshash64
       10 5.0% 89.0% 34 17.0% runtime.unlock
        9 4.5% 93.5% 63 31.5% chanrecv
        8 4.0% 97.5% 71 35.5% runtime.selectnbrecv
        5 2.5% 100.0% 20 10.0% runtime.lock
        0 0.0% 100.0% 200 100.0% runtime.gosched0

cases = 2
Total: 200 samples
       38 19.0% 19.0% 43 21.5% runtime.mapaccess1_fast64
       29 14.5% 33.5% 84 42.0% selectgo
       24 12.0% 45.5% 24 12.0% runtime.xchg
       15 7.5% 53.0% 24 12.0% runtime.mallocgc
       11 5.5% 58.5% 36 18.0% newselect
       10 5.0% 63.5% 199 99.5% main.func·001
        9 4.5% 68.0% 17 8.5% runtime.selectrecv
        6 3.0% 71.0% 6 3.0% runtime.memclr
        6 3.0% 74.0% 7 3.5% runtime.mlookup
        5 2.5% 76.5% 5 2.5% runtime.aeshash64

cases = 3
Total: 200 samples
       34 17.0% 17.0% 89 44.5% selectgo
       29 14.5% 31.5% 39 19.5% runtime.mapaccess1_fast64
       23 11.5% 43.0% 23 11.5% runtime.xchg
       13 6.5% 49.5% 23 11.5% runtime.mallocgc
       12 6.0% 55.5% 12 6.0% selectrecv
       10 5.0% 60.5% 200 100.0% main.func·001
       10 5.0% 65.5% 10 5.0% runtime.aeshash64
        7 3.5% 69.0% 22 11.0% runtime.selectrecv
        7 3.5% 72.5% 16 8.0% sellock
        6 3.0% 75.5% 6 3.0% runtime.fastrand1

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Search Discussions

  • Dmitry Vyukov at Aug 23, 2014 at 9:37 am

    On Thu, Aug 21, 2014 at 8:34 PM, Neha Narula wrote:
    Hello,

    I'd like to use a select loop and channels to multiplex queues of requests.
    However, I find that adding a case to a select loop can drastically lower
    performance. I have a little benchmark which compares a select loop,
    varying the number of cases, with a loop based on checking time. Each
    iteration I read a map and increment an integer. The code is here:
    https://gist.github.com/narula/67db0e1af075c389525b

    Here are the results:

    cases: -1 nitr: 22085384 time: 2.000022843s rate: 1.1042565877333833e+07
    // for loop
    cases: 0 nitr: 21855653 time: 2.000045463s rate: 1.0927578099758424e+07 //
    select loop with default case only
    cases: 1 nitr: 26015331 time: 2.016238151s rate: 1.2902905833369482e+07 //
    select loop with default + 1 case
    cases: 2 nitr: 6073945 time: 2.016959415s rate: 3.011436400171691e+06 //
    select loop with default + 2 cases
    cases: 3 nitr: 5544622 time: 2.015555947s rate: 2.7509144602275826e+06 //
    select loop with default + 3 cases

    Why the significant drop in throughput? Here's the cpuprofiling for all the
    cases. As you can see, once there is more than one case, more and more of
    the cpu time is spent in selectgo.

    Should I use a select loop when I have <= 1 case and a for loop otherwise?
    Is something especially slow about a ticker channel?

    A select with only 1 case + default special-cased by compiler into
    what we call "non-blocking receive" or "non-blocking send". As opposed
    to a general N-case select algorithm it proceeds as: try to receive
    from the channnel, and if it's not ready then just execute the default
    case.

    So it's expected that there is a drop in performance starting at 2 cases.

    The select must already become faster at tip, and I have another
    change that must make 2+ case selects faster:
    https://codereview.appspot.com/112990043/

    --
    You received this message because you are subscribed to the Google Groups "golang-nuts" group.
    To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Brad Fitzpatrick at Aug 23, 2014 at 6:53 pm
    Josh,

    What ever happened to your CL to pre-compute the random orders for select
    cases up to some size?


    On Thu, Aug 21, 2014 at 9:34 AM, Neha Narula wrote:

    Hello,

    I'd like to use a select loop and channels to multiplex queues of
    requests. However, I find that adding a case to a select loop can
    drastically lower performance. I have a little benchmark which compares a
    select loop, varying the number of cases, with a loop based on checking
    time. Each iteration I read a map and increment an integer. The code is
    here: https://gist.github.com/narula/67db0e1af075c389525b

    Here are the results:

    cases: -1 nitr: 22085384 time: 2.000022843s rate: 1.1042565877333833e+07
    // for loop
    cases: 0 nitr: 21855653 time: 2.000045463s rate: 1.0927578099758424e+07
    // select loop with default case only
    cases: 1 nitr: 26015331 time: 2.016238151s rate: 1.2902905833369482e+07
    // select loop with default + 1 case
    cases: 2 nitr: 6073945 time: 2.016959415s rate: 3.011436400171691e+06
    // select loop with default + 2 cases
    cases: 3 nitr: 5544622 time: 2.015555947s rate: 2.7509144602275826e+06
    // select loop with default + 3 cases

    Why the significant drop in throughput? Here's the cpuprofiling for all
    the cases. As you can see, once there is more than one case, more and more
    of the cpu time is spent in selectgo.

    Should I use a select loop when I have <= 1 case and a for loop otherwise?
    Is something especially slow about a ticker channel?

    cases = -1
    Total: 200 samples
    92 46.0% 46.0% 104 52.0% runtime.mapaccess1_fast64
    58 29.0% 75.0% 58 29.0% ExternalCode
    33 16.5% 91.5% 142 71.0% main.func·001
    12 6.0% 97.5% 12 6.0% runtime.aeshash64
    4 2.0% 99.5% 5 2.5% time.Now
    1 0.5% 100.0% 1 0.5% time.now
    0 0.0% 100.0% 58 29.0% System
    0 0.0% 100.0% 142 71.0% runtime.gosched0

    cases = 0
    Total: 199 samples
    94 47.2% 47.2% 108 54.3% runtime.mapaccess1_fast64
    46 23.1% 70.4% 46 23.1% ExternalCode
    37 18.6% 88.9% 153 76.9% main.func·001
    14 7.0% 96.0% 14 7.0% runtime.aeshash64
    5 2.5% 98.5% 8 4.0% time.Now
    3 1.5% 100.0% 3 1.5% time.now
    0 0.0% 100.0% 46 23.1% System
    0 0.0% 100.0% 153 76.9% runtime.gosched0

    cases = 1
    Total: 200 samples
    100 50.0% 50.0% 113 56.5% runtime.mapaccess1_fast64
    39 19.5% 69.5% 39 19.5% runtime.xchg
    16 8.0% 77.5% 200 100.0% main.func·001
    13 6.5% 84.0% 13 6.5% runtime.aeshash64
    10 5.0% 89.0% 34 17.0% runtime.unlock
    9 4.5% 93.5% 63 31.5% chanrecv
    8 4.0% 97.5% 71 35.5% runtime.selectnbrecv
    5 2.5% 100.0% 20 10.0% runtime.lock
    0 0.0% 100.0% 200 100.0% runtime.gosched0

    cases = 2
    Total: 200 samples
    38 19.0% 19.0% 43 21.5% runtime.mapaccess1_fast64
    29 14.5% 33.5% 84 42.0% selectgo
    24 12.0% 45.5% 24 12.0% runtime.xchg
    15 7.5% 53.0% 24 12.0% runtime.mallocgc
    11 5.5% 58.5% 36 18.0% newselect
    10 5.0% 63.5% 199 99.5% main.func·001
    9 4.5% 68.0% 17 8.5% runtime.selectrecv
    6 3.0% 71.0% 6 3.0% runtime.memclr
    6 3.0% 74.0% 7 3.5% runtime.mlookup
    5 2.5% 76.5% 5 2.5% runtime.aeshash64

    cases = 3
    Total: 200 samples
    34 17.0% 17.0% 89 44.5% selectgo
    29 14.5% 31.5% 39 19.5% runtime.mapaccess1_fast64
    23 11.5% 43.0% 23 11.5% runtime.xchg
    13 6.5% 49.5% 23 11.5% runtime.mallocgc
    12 6.0% 55.5% 12 6.0% selectrecv
    10 5.0% 60.5% 200 100.0% main.func·001
    10 5.0% 65.5% 10 5.0% runtime.aeshash64
    7 3.5% 69.0% 22 11.0% runtime.selectrecv
    7 3.5% 72.5% 16 8.0% sellock
    6 3.0% 75.5% 6 3.0% runtime.fastrand1

    --
    You received this message because you are subscribed to the Google Groups
    "golang-nuts" group.
    To unsubscribe from this group and stop receiving emails from it, send an
    email to golang-nuts+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
    --
    You received this message because you are subscribed to the Google Groups "golang-nuts" group.
    To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Benjamin Measures at Aug 24, 2014 at 7:02 am
    pre-compute the random orders
    Fair dice roll?
    http://xkcd.com/221/

    --
    You received this message because you are subscribed to the Google Groups "golang-nuts" group.
    To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Josh Bleecher Snyder at Aug 24, 2014 at 4:35 pm
    Should I use a select loop when I have <= 1 case and a for loop otherwise?
    A select loop and a for loop have different semantics. You should use
    whichever one does what you need. Some concrete code might help clarify a
    bit.

    What ever happened to your CL to pre-compute the random orders for select
    cases up to some size?

    I keep putting it off due to a combination of code freezes and awesomeness
    from Dmitry being on the horizon. I'll look at it again once Dmitry's
    CL lands, since the broader channel revamp has been shelved (sad)...unless
    the Go 1.4 freeze hits first. :)

    Dmitry's work will have a much bigger impact, anyway, and may make my
    tweaks irrelevant. We'll see.

    -josh

    --
    You received this message because you are subscribed to the Google Groups "golang-nuts" group.
    To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Dmitry Vyukov at Aug 24, 2014 at 4:58 pm

    On Sun, Aug 24, 2014 at 8:34 PM, Josh Bleecher Snyder wrote:
    Should I use a select loop when I have <= 1 case and a for loop otherwise?
    A select loop and a for loop have different semantics. You should use
    whichever one does what you need. Some concrete code might help clarify a
    bit.

    What ever happened to your CL to pre-compute the random orders for select
    cases up to some size?
    I keep putting it off due to a combination of code freezes and awesomeness
    from Dmitry being on the horizon. I'll look at it again once Dmitry's CL
    lands, since the broader channel revamp has been shelved (sad)...unless the
    Go 1.4 freeze hits first. :)

    Dmitry's work will have a much bigger impact, anyway, and may make my tweaks
    irrelevant. We'll see.

    There are chances that https://codereview.appspot.com/112990043/ will
    be submitted in few days.

    Actually the faster are other parts, the more % your change gives.
    fastrand1 will be an observable part of selectgo execution after
    112990043.

    --
    You received this message because you are subscribed to the Google Groups "golang-nuts" group.
    To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Neha Narula at Aug 25, 2014 at 2:50 pm
    Dmitry your change sped things up quite a bit:

    cases: -1 nitr: 25596070 time: 2.00004825s rate: 1.2797726254854102e+07
    cases: 0 nitr: 25838315 time: 2.000109111s rate: 1.2918452727352234e+07
    cases: 1 nitr: 34842988 time: 2.014927332s rate: 1.7292429084981017e+07
    cases: 2 nitr: 10895467 time: 2.013499262s rate: 5.411209830381353e+06
    cases: 3 nitr: 8910674 time: 2.015765834s rate: 4.420490639191972e+06

    42% of the time is spent in selectgo and what it calls; it looks like
    the major culprits are

    86 128 641: j = runtime·fastrand1()%(i+1);

    and

      43 78 663: if((c->dataqsiz > 0 &&
    (uintgo)runtime·atomicloadp((void**)&c->qcount) > 0) ||
          . . 664: (c->dataqsiz == 0 && c->sendq.first != nil) ||
          . . 665: runtime·atomicload(&c->closed) != 0) {

    I'll keep an eye out for any changes that make things faster. Thanks!
    On Sun, Aug 24, 2014 at 12:57 PM, Dmitry Vyukov wrote:
    On Sun, Aug 24, 2014 at 8:34 PM, Josh Bleecher Snyder
    wrote:
    Should I use a select loop when I have <= 1 case and a for loop otherwise?
    A select loop and a for loop have different semantics. You should use
    whichever one does what you need. Some concrete code might help clarify a
    bit.

    What ever happened to your CL to pre-compute the random orders for select
    cases up to some size?
    I keep putting it off due to a combination of code freezes and awesomeness
    from Dmitry being on the horizon. I'll look at it again once Dmitry's CL
    lands, since the broader channel revamp has been shelved (sad)...unless the
    Go 1.4 freeze hits first. :)

    Dmitry's work will have a much bigger impact, anyway, and may make my tweaks
    irrelevant. We'll see.

    There are chances that https://codereview.appspot.com/112990043/ will
    be submitted in few days.

    Actually the faster are other parts, the more % your change gives.
    fastrand1 will be an observable part of selectgo execution after
    112990043.
    --
    You received this message because you are subscribed to the Google Groups "golang-nuts" group.
    To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Dmitry Vyukov at Aug 25, 2014 at 3:16 pm

    On Mon, Aug 25, 2014 at 6:49 PM, Neha Narula wrote:
    Dmitry your change sped things up quite a bit:

    cases: -1 nitr: 25596070 time: 2.00004825s rate: 1.2797726254854102e+07
    cases: 0 nitr: 25838315 time: 2.000109111s rate: 1.2918452727352234e+07
    cases: 1 nitr: 34842988 time: 2.014927332s rate: 1.7292429084981017e+07
    cases: 2 nitr: 10895467 time: 2.013499262s rate: 5.411209830381353e+06
    cases: 3 nitr: 8910674 time: 2.015765834s rate: 4.420490639191972e+06 nice!
    42% of the time is spent in selectgo and what it calls; it looks like
    the major culprits are

    86 128 641: j = runtime·fastrand1()%(i+1);
    Josh, it seems your patch will be to the point.
    Keith won't do selects until Thu, so we have a few day window.

    and

    43 78 663: if((c->dataqsiz > 0 &&
    (uintgo)runtime·atomicloadp((void**)&c->qcount) > 0) ||
    . . 664: (c->dataqsiz == 0 && c->sendq.first != nil) ||
    . . 665: runtime·atomicload(&c->closed) != 0) {

    I'll keep an eye out for any changes that make things faster. Thanks!
    On Sun, Aug 24, 2014 at 12:57 PM, Dmitry Vyukov wrote:
    On Sun, Aug 24, 2014 at 8:34 PM, Josh Bleecher Snyder
    wrote:
    Should I use a select loop when I have <= 1 case and a for loop otherwise?
    A select loop and a for loop have different semantics. You should use
    whichever one does what you need. Some concrete code might help clarify a
    bit.

    What ever happened to your CL to pre-compute the random orders for select
    cases up to some size?
    I keep putting it off due to a combination of code freezes and awesomeness
    from Dmitry being on the horizon. I'll look at it again once Dmitry's CL
    lands, since the broader channel revamp has been shelved (sad)...unless the
    Go 1.4 freeze hits first. :)

    Dmitry's work will have a much bigger impact, anyway, and may make my tweaks
    irrelevant. We'll see.

    There are chances that https://codereview.appspot.com/112990043/ will
    be submitted in few days.

    Actually the faster are other parts, the more % your change gives.
    fastrand1 will be an observable part of selectgo execution after
    112990043.
    --
    You received this message because you are subscribed to the Google Groups "golang-nuts" group.
    To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.

Related Discussions

Discussion Navigation
viewthread | post
Discussion Overview
groupgolang-nuts @
categoriesgo
postedAug 23, '14 at 3:40a
activeAug 25, '14 at 3:16p
posts8
users5
websitegolang.org

People

Translate

site design / logo © 2021 Grokbase