FAQ
Hi all,

I have been doing some benchmarking for 2 processes communicating via a
couple of os.Pipe()s on Linux/amd64 using go 1.2.1 and got some interesting
results.

pong.go:

package main

import (
         "os"
)

var BYTE = []byte{'\n'}

func main() {
         buf := []byte{'\n'}
         for {
                 _, err := os.Stdin.Read(buf)
                 if err != nil {
                         break
                 }
                 _, err = os.Stdout.Write(BYTE)
                 if err != nil {
                         break
                 }
         }
}



ping2.go:

package main

import (
"fmt"
"os"
"time"
)

var BYTE = []byte{'\n'}

func main() {
localR, remoteW, _ := os.Pipe()
remoteR, localW, _ := os.Pipe()
var procAttr os.ProcAttr
procAttr.Files = []*os.File{remoteR, remoteW, nil}
_, err := os.StartProcess("/usr/local/go/bin/go", []string{"go", "run",
"pong.go"}, &procAttr)
if err != nil {
panic(err)
}

ticker := time.NewTicker(3 * time.Second)
count := 5
seq := 0
buf := []byte{'\n'}
for count > 0 {
localW.Write(BYTE)
_, err := localR.Read(buf)
if err != nil {
panic(err)
}
seq++
select {
case <-ticker.C:
fmt.Printf("Rate: %d req/sec\n", seq/3)
count--
seq = 0
default:

}
}
}

Running "go run ping2.go" produces the following result:

Rate: 121750 req/sec
Rate: 126298 req/sec
Rate: 132724 req/sec
Rate: 114733 req/sec
Rate: 122553 req/sec


If I move the reading to a separate goroutine and use a channel, like this:

ping1.go:

package main

import (
"fmt"
"os"
"runtime"
"time"
)

var BYTE = []byte{'\r'}

func main() {
runtime.GOMAXPROCS(1)

localR, remoteW, _ := os.Pipe()
remoteR, localW, _ := os.Pipe()

var procAttr os.ProcAttr
procAttr.Files = []*os.File{remoteR, remoteW, nil}
_, err := os.StartProcess("/usr/local/go/bin/go", []string{"go", "run",
"pong.go"}, &procAttr)
if err != nil {
panic(err)
}
ch := make(chan byte, 1)
go func() {
buf := []byte{'\n'}
for {
_, err := localR.Read(buf)
if err != nil {
panic(err)
}
ch <- buf[0]
}
}()


ticker := time.NewTicker(3 * time.Second)
count := 5
seq := 0
for count > 0 {

localW.Write(BYTE)

L:
for {
select {
case <-ticker.C:
fmt.Printf("Rate: %d req/sec\n", seq/3)
count--
seq = 0
case <-ch:
seq++
break L
}
}
}
}

I get a radically different result:

Rate: 5692 req/sec
Rate: 6032 req/sec
Rate: 6002 req/sec
Rate: 6485 req/sec
Rate: 5850 req/sec

strace shows there are 2 select() timeouts involved:

13186 write(6, "\r", 1 <unfinished ...>
*13182 select(0, NULL, NULL, NULL, {0, 20} <unfinished ...>*
13186 <... write resumed> ) = 1
13186 futex(0xc21002b8e8, FUTEX_WAIT, 0, NULL <unfinished ...>
13198 <... futex resumed> ) = 0
*13198 select(0, NULL, NULL, NULL, {0, 20} <unfinished ...>*
13182 <... select resumed> ) = 0 (Timeout)
13182 futex(0x57e4f8, FUTEX_WAIT, 0, NULL <unfinished ...>
13181 <... read resumed> "\n", 1) = 1
13181 futex(0x57e4f8, FUTEX_WAKE, 1 <unfinished ...>
13198 <... select resumed> ) = 0 (Timeout)
13182 <... futex resumed> ) = 0
*13198 select(0, NULL, NULL, NULL, {0, 20} <unfinished ...>*
*13182 select(0, NULL, NULL, NULL, {0, 20} <unfinished ...>*
13181 <... futex resumed> ) = 1

which explains the difference, but I'm wondering why is there a need for
the timeouts? Does it indicate a problem with the scheduler?

Changing GOMAXPROCS to 2 results in a significant improvement and no select
timeouts showing in strace:

Rate: 64265 req/sec
Rate: 67554 req/sec
Rate: 70268 req/sec
Rate: 68530 req/sec
Rate: 68378 req/sec

which is a surprise because the protocol does not involve any level of
concurrency.

Could somebody shed some light please?


--
Dmitry

--
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

  • Ian Lance Taylor at Apr 30, 2014 at 12:54 am

    On Tue, Apr 29, 2014 at 8:51 AM, Dmitry Panov wrote:
    which explains the difference, but I'm wondering why is there a need for the
    timeouts? Does it indicate a problem with the scheduler?
    The scheduler assumes that a system call will not block. The sysmon
    thread sleeps for 20us. When it wakes up, if there is a goroutine
    blocked in a system call, it releases the thread to run a different
    goroutine. This generally works well for programs when system calls
    normally do not block and/or when there is work that the program can
    do in parallel. Your program does not meet either criterion, and so
    for your program the behaviour is actually harmful. Fortunately your
    program is relatively unusual.

    Ian

    --
    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 Panov at Apr 30, 2014 at 6:04 pm

    On Wednesday, 30 April 2014 01:54:34 UTC+1, Ian Lance Taylor wrote:

    The scheduler assumes that a system call will not block. The sysmon
    thread sleeps for 20us. When it wakes up, if there is a goroutine
    blocked in a system call, it releases the thread to run a different
    goroutine. This generally works well for programs when system calls
    normally do not block and/or when there is work that the program can
    do in parallel. Your program does not meet either criterion, and so
    for your program the behaviour is actually harmful. Fortunately your
    program is relatively unusual.
    I see, thanks for clarifying. One thing I still don't get though is how the
    work that the program can do in parallel could help if there is only one
    active application thread and it's blocked on a syscall until the sysmon
    thread unblocks it.

    Also, the real program I had the issue with is a bi-directional RPC service
    where requests may arrive from either side, therefore I have to constantly
    read from the input. Also for that reason Dmitry's suggestion is not really
    possible.

    --
    Dmitry

    --
    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.
  • Ian Lance Taylor at Apr 30, 2014 at 7:48 pm

    On Wed, Apr 30, 2014 at 2:04 PM, Dmitry Panov wrote:
    On Wednesday, 30 April 2014 01:54:34 UTC+1, Ian Lance Taylor wrote:


    The scheduler assumes that a system call will not block. The sysmon
    thread sleeps for 20us. When it wakes up, if there is a goroutine
    blocked in a system call, it releases the thread to run a different
    goroutine. This generally works well for programs when system calls
    normally do not block and/or when there is work that the program can
    do in parallel. Your program does not meet either criterion, and so
    for your program the behaviour is actually harmful. Fortunately your
    program is relatively unusual.
    I see, thanks for clarifying. One thing I still don't get though is how the
    work that the program can do in parallel could help if there is only one
    active application thread and it's blocked on a syscall until the sysmon
    thread unblocks it.
    I guess my question would be: why is there only one active application
    thread?

    The more general question is: can the scheduler do a better job for
    programs that have concurrent work and make blocking system calls
    while leaving GOMAXPROCS set to the default of 1? It's hard for the
    scheduler to know whether a system call will block. It's
    significantly faster for the scheduler to assume that a non-blocking
    system call will not block. In practice most system calls do not
    block for a noticeable amount of time. I don't immediately see a
    better default behaviour.

    Your program does work with the default GOMAXPROCS, and your program
    does work better when you set GOMAXPROCS to reflect the fact that your
    program can do work in parallel. If we can make your program work
    better by default, that would be nice. Ultimately, though, the best
    performance for a program is always going to require some tuning.

    Ian

    --
    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 May 2, 2014 at 4:38 pm

    On Wed, Apr 30, 2014 at 8:48 PM, Ian Lance Taylor wrote:
    On Wed, Apr 30, 2014 at 2:04 PM, Dmitry Panov wrote:

    On Wednesday, 30 April 2014 01:54:34 UTC+1, Ian Lance Taylor wrote:


    The scheduler assumes that a system call will not block. The sysmon
    thread sleeps for 20us. When it wakes up, if there is a goroutine
    blocked in a system call, it releases the thread to run a different
    goroutine. This generally works well for programs when system calls
    normally do not block and/or when there is work that the program can
    do in parallel. Your program does not meet either criterion, and so
    for your program the behaviour is actually harmful. Fortunately your
    program is relatively unusual.
    I see, thanks for clarifying. One thing I still don't get though is how the
    work that the program can do in parallel could help if there is only one
    active application thread and it's blocked on a syscall until the sysmon
    thread unblocks it.
    I guess my question would be: why is there only one active application
    thread?

    The more general question is: can the scheduler do a better job for
    programs that have concurrent work and make blocking system calls
    while leaving GOMAXPROCS set to the default of 1? It's hard for the
    scheduler to know whether a system call will block. It's
    significantly faster for the scheduler to assume that a non-blocking
    system call will not block. In practice most system calls do not
    block for a noticeable amount of time. I don't immediately see a
    better default behaviour.
    We probably could do some guessing and supply some defaults for some
    syscalls (e.g. pipe read does block). But it does not look like a good
    solution long-term and it will obscure performance characteristics
    even more.

    The long-term solution is to support user-space scheduling where
    available (when kernel notifies user-space when a thread blocks,
    https://code.google.com/p/go/issues/detail?id=7876) + non-blocking
    operations for disk/pipe/etc IO operations
    (https://code.google.com/p/go/issues/detail?id=6817).

    Your program does work with the default GOMAXPROCS, and your program
    does work better when you set GOMAXPROCS to reflect the fact that your
    program can do work in parallel. If we can make your program work
    better by default, that would be nice. Ultimately, though, the best
    performance for a program is always going to require some tuning.

    Ian

    --
    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.
  • Dmitry Panov at May 4, 2014 at 2:26 pm

    On Friday, 2 May 2014 17:37:26 UTC+1, Dmitry Vyukov wrote:

    The long-term solution is to support user-space scheduling where
    available (when kernel notifies user-space when a thread blocks,
    https://code.google.com/p/go/issues/detail?id=7876) + non-blocking
    operations for disk/pipe/etc IO operations
    (https://code.google.com/p/go/issues/detail?id=6817).

    "one more reason to expose the poller?" I'd say definitely yes. My
    understanding is if it was available for pipes, it would solve the issue.



    --
    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.
  • Fredistic at May 5, 2014 at 3:20 pm
    In the short run, someone more knowledgeable than I could add a few
    sentences to the documentation. Probably to os.Pipe().

    --
    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 May 5, 2014 at 3:30 pm
    what exactly do you want to add?
    we do not promise anything about performance, and the documentation is
    not the right place for performance-oriented usage suggestions.

    On Mon, May 5, 2014 at 7:20 PM, wrote:
    In the short run, someone more knowledgeable than I could add a few
    sentences to the documentation. Probably to os.Pipe().

    --
    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.
  • Dmitry Panov at May 5, 2014 at 4:13 pm
    I think documentation is the right place because it is the place most
    likely to be read by someone who is considering an IPC solution. It should
    advise about the current implementation caveat and suggest using
    alternative mechanisms (such as unix sockets). It would have saved me a lot
    of time if there had been such a warning.

    Pipes would be a first choice for many as they are in general faster than
    unix sockets. Also, unix sockets are less portable.

    --
    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 May 5, 2014 at 4:17 pm
    can you outline the comment?

    note that if pipes are faster than sockets in OS, then still can be faster in Go
    On Mon, May 5, 2014 at 8:13 PM, Dmitry Panov wrote:
    I think documentation is the right place because it is the place most likely
    to be read by someone who is considering an IPC solution. It should advise
    about the current implementation caveat and suggest using alternative
    mechanisms (such as unix sockets). It would have saved me a lot of time if
    there had been such a warning.

    Pipes would be a first choice for many as they are in general faster than
    unix sockets. Also, unix sockets are less portable.

    --
    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.
  • Dmitry Panov at May 5, 2014 at 5:37 pm
    Sorry, what do you mean by outline the comment?

    On Monday, 5 May 2014 17:16:58 UTC+1, Dmitry Vyukov wrote:

    can you outline the comment?

    note that if pipes are faster than sockets in OS, then still can be faster
    in Go
    On Mon, May 5, 2014 at 8:13 PM, Dmitry Panov wrote:
    I think documentation is the right place because it is the place most likely
    to be read by someone who is considering an IPC solution. It should advise
    about the current implementation caveat and suggest using alternative
    mechanisms (such as unix sockets). It would have saved me a lot of time if
    there had been such a warning.

    Pipes would be a first choice for many as they are in general faster than
    unix sockets. Also, unix sockets are less portable.

    --
    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...@googlegroups.com <javascript:>.
    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.
  • Dmitry Vyukov at May 5, 2014 at 5:54 pm

    On Mon, May 5, 2014 at 6:37 PM, Dmitry Panov wrote:
    Sorry, what do you mean by outline the comment?
    write a draft
    On Monday, 5 May 2014 17:16:58 UTC+1, Dmitry Vyukov wrote:

    can you outline the comment?

    note that if pipes are faster than sockets in OS, then still can be faster
    in Go
    On Mon, May 5, 2014 at 8:13 PM, Dmitry Panov wrote:
    I think documentation is the right place because it is the place most
    likely
    to be read by someone who is considering an IPC solution. It should
    advise
    about the current implementation caveat and suggest using alternative
    mechanisms (such as unix sockets). It would have saved me a lot of time
    if
    there had been such a warning.

    Pipes would be a first choice for many as they are in general faster
    than
    unix sockets. Also, unix sockets are less portable.

    --
    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...@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.
    --
    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 Panov at May 6, 2014 at 9:47 pm
    It would be better if a native speaker did this, I would add to the
    description of the os.Pipe() function something like 'Note that reading
    from a pipe when no data is available will block the process thread for up
    to 20us. This will be addressed in a future release, in the mean time
    please consider other alternatives such as unix sockets, or set
    runtime.GOMAXPROCS() to a value greater than the number of goroutines that
    might read from pipes simultaneously."

    Of course this assumes there is an intention to fix this (which I hope is
    the case).

    On Monday, 5 May 2014 18:53:41 UTC+1, Dmitry Vyukov wrote:
    On Mon, May 5, 2014 at 6:37 PM, Dmitry Panov wrote:
    Sorry, what do you mean by outline the comment?
    write a draft
    On Monday, 5 May 2014 17:16:58 UTC+1, Dmitry Vyukov wrote:

    can you outline the comment?

    note that if pipes are faster than sockets in OS, then still can be
    faster
    in Go
    On Mon, May 5, 2014 at 8:13 PM, Dmitry Panov wrote:
    I think documentation is the right place because it is the place most
    likely
    to be read by someone who is considering an IPC solution. It should
    advise
    about the current implementation caveat and suggest using alternative
    mechanisms (such as unix sockets). It would have saved me a lot of
    time
    if
    there had been such a warning.

    Pipes would be a first choice for many as they are in general faster
    than
    unix sockets. Also, unix sockets are less portable.

    --
    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...@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...@googlegroups.com <javascript:>.
    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.
  • Dmitry Vyukov at May 7, 2014 at 6:36 am
    Ian, what do you think?
    On Wed, May 7, 2014 at 1:46 AM, Dmitry Panov wrote:
    It would be better if a native speaker did this, I would add to the
    description of the os.Pipe() function something like 'Note that reading from
    a pipe when no data is available will block the process thread for up to
    20us. This will be addressed in a future release, in the mean time please
    consider other alternatives such as unix sockets, or set
    runtime.GOMAXPROCS() to a value greater than the number of goroutines that
    might read from pipes simultaneously."

    Of course this assumes there is an intention to fix this (which I hope is
    the case).

    On Monday, 5 May 2014 18:53:41 UTC+1, Dmitry Vyukov wrote:
    On Mon, May 5, 2014 at 6:37 PM, Dmitry Panov wrote:
    Sorry, what do you mean by outline the comment?
    write a draft
    On Monday, 5 May 2014 17:16:58 UTC+1, Dmitry Vyukov wrote:

    can you outline the comment?

    note that if pipes are faster than sockets in OS, then still can be
    faster
    in Go
    On Mon, May 5, 2014 at 8:13 PM, Dmitry Panov wrote:
    I think documentation is the right place because it is the place most
    likely
    to be read by someone who is considering an IPC solution. It should
    advise
    about the current implementation caveat and suggest using alternative
    mechanisms (such as unix sockets). It would have saved me a lot of
    time
    if
    there had been such a warning.

    Pipes would be a first choice for many as they are in general faster
    than
    unix sockets. Also, unix sockets are less portable.

    --
    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...@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...@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.
    --
    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.
  • Ian Lance Taylor at May 7, 2014 at 2:10 pm

    On Tue, May 6, 2014 at 11:36 PM, Dmitry Vyukov wrote:
    Ian, what do you think?
    On Wed, May 7, 2014 at 1:46 AM, Dmitry Panov wrote:
    It would be better if a native speaker did this, I would add to the
    description of the os.Pipe() function something like 'Note that reading from
    a pipe when no data is available will block the process thread for up to
    20us. This will be addressed in a future release, in the mean time please
    consider other alternatives such as unix sockets, or set
    runtime.GOMAXPROCS() to a value greater than the number of goroutines that
    might read from pipes simultaneously."

    Of course this assumes there is an intention to fix this (which I hope is
    the case).

    I think it would be fine to document the scheduler behaviour a bit,
    though I don't know where.

    It does not make sense to me to add a note to os.Pipe. As far as I
    can see this same comment applies to all file operations--consider
    reading from a file on NFS. The os.Pipe docs are not the place to
    discuss this kind of thing.

    Ian


    On Monday, 5 May 2014 18:53:41 UTC+1, Dmitry Vyukov wrote:
    On Mon, May 5, 2014 at 6:37 PM, Dmitry Panov wrote:
    Sorry, what do you mean by outline the comment?
    write a draft
    On Monday, 5 May 2014 17:16:58 UTC+1, Dmitry Vyukov wrote:

    can you outline the comment?

    note that if pipes are faster than sockets in OS, then still can be
    faster
    in Go
    On Mon, May 5, 2014 at 8:13 PM, Dmitry Panov wrote:
    I think documentation is the right place because it is the place most
    likely
    to be read by someone who is considering an IPC solution. It should
    advise
    about the current implementation caveat and suggest using alternative
    mechanisms (such as unix sockets). It would have saved me a lot of
    time
    if
    there had been such a warning.

    Pipes would be a first choice for many as they are in general faster
    than
    unix sockets. Also, unix sockets are less portable.

    --
    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...@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...@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.
    --
    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 Apr 30, 2014 at 7:02 am
    One way to restore the performance is to not read the reply until the
    request is sent:
    http://play.golang.org/p/YOsFAgkhDk
    This prevents the issue in the scheduler that Ian has described.


    On Tue, Apr 29, 2014 at 4:51 PM, Dmitry Panov wrote:
    Hi all,

    I have been doing some benchmarking for 2 processes communicating via a
    couple of os.Pipe()s on Linux/amd64 using go 1.2.1 and got some interesting
    results.

    pong.go:

    package main

    import (
    "os"
    )

    var BYTE = []byte{'\n'}

    func main() {
    buf := []byte{'\n'}
    for {
    _, err := os.Stdin.Read(buf)
    if err != nil {
    break
    }
    _, err = os.Stdout.Write(BYTE)
    if err != nil {
    break
    }
    }
    }



    ping2.go:

    package main

    import (
    "fmt"
    "os"
    "time"
    )

    var BYTE = []byte{'\n'}

    func main() {
    localR, remoteW, _ := os.Pipe()
    remoteR, localW, _ := os.Pipe()
    var procAttr os.ProcAttr
    procAttr.Files = []*os.File{remoteR, remoteW, nil}
    _, err := os.StartProcess("/usr/local/go/bin/go", []string{"go", "run",
    "pong.go"}, &procAttr)
    if err != nil {
    panic(err)
    }

    ticker := time.NewTicker(3 * time.Second)
    count := 5
    seq := 0
    buf := []byte{'\n'}
    for count > 0 {
    localW.Write(BYTE)
    _, err := localR.Read(buf)
    if err != nil {
    panic(err)
    }
    seq++
    select {
    case <-ticker.C:
    fmt.Printf("Rate: %d req/sec\n", seq/3)
    count--
    seq = 0
    default:

    }
    }
    }

    Running "go run ping2.go" produces the following result:

    Rate: 121750 req/sec
    Rate: 126298 req/sec
    Rate: 132724 req/sec
    Rate: 114733 req/sec
    Rate: 122553 req/sec


    If I move the reading to a separate goroutine and use a channel, like this:

    ping1.go:

    package main

    import (
    "fmt"
    "os"
    "runtime"
    "time"
    )

    var BYTE = []byte{'\r'}

    func main() {
    runtime.GOMAXPROCS(1)

    localR, remoteW, _ := os.Pipe()
    remoteR, localW, _ := os.Pipe()

    var procAttr os.ProcAttr
    procAttr.Files = []*os.File{remoteR, remoteW, nil}
    _, err := os.StartProcess("/usr/local/go/bin/go", []string{"go", "run",
    "pong.go"}, &procAttr)
    if err != nil {
    panic(err)
    }
    ch := make(chan byte, 1)
    go func() {
    buf := []byte{'\n'}
    for {
    _, err := localR.Read(buf)
    if err != nil {
    panic(err)
    }
    ch <- buf[0]
    }
    }()


    ticker := time.NewTicker(3 * time.Second)
    count := 5
    seq := 0
    for count > 0 {

    localW.Write(BYTE)

    L:
    for {
    select {
    case <-ticker.C:
    fmt.Printf("Rate: %d req/sec\n", seq/3)
    count--
    seq = 0
    case <-ch:
    seq++
    break L
    }
    }
    }
    }

    I get a radically different result:

    Rate: 5692 req/sec
    Rate: 6032 req/sec
    Rate: 6002 req/sec
    Rate: 6485 req/sec
    Rate: 5850 req/sec

    strace shows there are 2 select() timeouts involved:

    13186 write(6, "\r", 1 <unfinished ...>
    13182 select(0, NULL, NULL, NULL, {0, 20} <unfinished ...>
    13186 <... write resumed> ) = 1
    13186 futex(0xc21002b8e8, FUTEX_WAIT, 0, NULL <unfinished ...>
    13198 <... futex resumed> ) = 0
    13198 select(0, NULL, NULL, NULL, {0, 20} <unfinished ...>
    13182 <... select resumed> ) = 0 (Timeout)
    13182 futex(0x57e4f8, FUTEX_WAIT, 0, NULL <unfinished ...>
    13181 <... read resumed> "\n", 1) = 1
    13181 futex(0x57e4f8, FUTEX_WAKE, 1 <unfinished ...>
    13198 <... select resumed> ) = 0 (Timeout)
    13182 <... futex resumed> ) = 0
    13198 select(0, NULL, NULL, NULL, {0, 20} <unfinished ...>
    13182 select(0, NULL, NULL, NULL, {0, 20} <unfinished ...>
    13181 <... futex resumed> ) = 1

    which explains the difference, but I'm wondering why is there a need for the
    timeouts? Does it indicate a problem with the scheduler?

    Changing GOMAXPROCS to 2 results in a significant improvement and no select
    timeouts showing in strace:

    Rate: 64265 req/sec
    Rate: 67554 req/sec
    Rate: 70268 req/sec
    Rate: 68530 req/sec
    Rate: 68378 req/sec

    which is a surprise because the protocol does not involve any level of
    concurrency.

    Could somebody shed some light please?


    --
    Dmitry

    --
    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.

Related Discussions

Discussion Navigation
viewthread | post
Discussion Overview
groupgolang-nuts @
categoriesgo
postedApr 29, '14 at 12:51p
activeMay 7, '14 at 2:10p
posts16
users4
websitegolang.org

People

Translate

site design / logo © 2022 Grokbase