FAQ
I have built a buffer with peek functionality, however the `Peek` function
is called so often that performance is hindered by the fact that it does
not inline. About 1/3rd of the time is spent on this function.

          . . 59:// Peek returns the ith byte relative to the
end position and possibly does an allocation. Calling Peek may invalidate
previous returned byte slices by Bytes or Shift, unless IsEOF returns true.
          . . 60:// Peek returns zero when an error has
occurred, Err return the error.
      208ms 208ms 61:func (z *Shifter) Peek(i int) byte {
       15ms 15ms 62: end := z.end + i
       15ms 15ms 63: if end >= len(z.buf) {
        1ms 1ms 64: if z.err != nil {
        1ms 1ms 65: return 0
          . . 66: }
          . . 67:
          . . 68: // reallocate a new buffer
(possibly larger)
          . . 69: c := cap(z.buf)
          . . 70: d := len(z.buf) - z.pos
          . . 71: var buf []byte
          . . 72: if 2*d > c {
          . . 73: buf = make([]byte, d, 2*c+
end-z.pos)
          . . 74: } else {
          . . 75: buf = z.buf[:d]
          . . 76: }
          . . 77: copy(buf, z.buf[z.pos:])
          . . 78:
          . . 79: // read in to fill the buffer till
capacity
          . . 80: var n int
          . . 81: n, z.err = z.r.Read(buf[d:cap(buf)])
          . . 82: z.eof = (z.err == io.EOF)
          . . 83: end -= z.pos
          . . 84: z.end -= z.pos
          . . 85: z.pos, z.buf = 0, buf[:d+n]
          . . 86: if n == 0 {
          . . 87: return 0
          . . 88: }
          . . 89: }
       70ms 70ms 90: return z.buf[end]
          . . 91:}

This function is pretty small, except when `end >= len(z.buf)` is true. So
if the contents of the if-statement was a function, the Peek function could
benefit from being inlined.

What are my options here? Can I redesign this to encourage inlining?
Thoughts?

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

  • Alb Donizetti at Sep 25, 2015 at 9:54 am
    You could move the check outside the function and call it only when
    it has real work to do.

    Il giorno venerdì 25 settembre 2015 11:46:16 UTC+2, tacod...@gmail.com ha
    scritto:
    I have built a buffer with peek functionality, however the `Peek` function
    is called so often that performance is hindered by the fact that it does
    not inline. About 1/3rd of the time is spent on this function.

    . . 59:// Peek returns the ith byte relative to the
    end position and possibly does an allocation. Calling Peek may invalidate
    previous returned byte slices by Bytes or Shift, unless IsEOF returns true.
    . . 60:// Peek returns zero when an error has
    occurred, Err return the error.
    208ms 208ms 61:func (z *Shifter) Peek(i int) byte {
    15ms 15ms 62: end := z.end + i
    15ms 15ms 63: if end >= len(z.buf) {
    1ms 1ms 64: if z.err != nil {
    1ms 1ms 65: return 0
    . . 66: }
    . . 67:
    . . 68: // reallocate a new buffer
    (possibly larger)
    . . 69: c := cap(z.buf)
    . . 70: d := len(z.buf) - z.pos
    . . 71: var buf []byte
    . . 72: if 2*d > c {
    . . 73: buf = make([]byte, d, 2*c+
    end-z.pos)
    . . 74: } else {
    . . 75: buf = z.buf[:d]
    . . 76: }
    . . 77: copy(buf, z.buf[z.pos:])
    . . 78:
    . . 79: // read in to fill the buffer
    till capacity
    . . 80: var n int
    . . 81: n, z.err = z.r.Read(buf[d:cap(buf
    )])
    . . 82: z.eof = (z.err == io.EOF)
    . . 83: end -= z.pos
    . . 84: z.end -= z.pos
    . . 85: z.pos, z.buf = 0, buf[:d+n]
    . . 86: if n == 0 {
    . . 87: return 0
    . . 88: }
    . . 89: }
    70ms 70ms 90: return z.buf[end]
    . . 91:}

    This function is pretty small, except when `end >= len(z.buf)` is true.
    So if the contents of the if-statement was a function, the Peek function
    could benefit from being inlined.

    What are my options here? Can I redesign this to encourage inlining?
    Thoughts?
    --
    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.
  • Chris Kastorff at Sep 26, 2015 at 6:15 am
    I think your best bet is to split this into two functions, one to
    handle the edge case (probably unexported), and the other to handle
    the common case and the if statement which passes control over to the
    other if needed. The common-case function then becomes WAY smaller (in
    this case, 5 lines, one of which doesn't have any expressions in it)
    and is more likely to be inlined because of that.

    There's a compiler flag you can pass to gc to get it to tell you about
    the inlining decisions it's making (among other things), which you can
    use to make sure it's being inlined properly when you want it to be:
    go build -gcflags='-m'
    On Fri, Sep 25, 2015 at 2:54 AM, wrote:
    You could move the check outside the function and call it only when
    it has real work to do.


    Il giorno venerdì 25 settembre 2015 11:46:16 UTC+2, tacod...@gmail.com ha
    scritto:
    I have built a buffer with peek functionality, however the `Peek` function
    is called so often that performance is hindered by the fact that it does not
    inline. About 1/3rd of the time is spent on this function.

    . . 59:// Peek returns the ith byte relative to the
    end position and possibly does an allocation. Calling Peek may invalidate
    previous returned byte slices by Bytes or Shift, unless IsEOF returns true.
    . . 60:// Peek returns zero when an error has
    occurred, Err return the error.
    208ms 208ms 61:func (z *Shifter) Peek(i int) byte {
    15ms 15ms 62: end := z.end + i
    15ms 15ms 63: if end >= len(z.buf) {
    1ms 1ms 64: if z.err != nil {
    1ms 1ms 65: return 0
    . . 66: }
    . . 67:
    . . 68: // reallocate a new buffer
    (possibly larger)
    . . 69: c := cap(z.buf)
    . . 70: d := len(z.buf) - z.pos
    . . 71: var buf []byte
    . . 72: if 2*d > c {
    . . 73: buf = make([]byte, d,
    2*c+end-z.pos)
    . . 74: } else {
    . . 75: buf = z.buf[:d]
    . . 76: }
    . . 77: copy(buf, z.buf[z.pos:])
    . . 78:
    . . 79: // read in to fill the buffer till
    capacity
    . . 80: var n int
    . . 81: n, z.err =
    z.r.Read(buf[d:cap(buf)])
    . . 82: z.eof = (z.err == io.EOF)
    . . 83: end -= z.pos
    . . 84: z.end -= z.pos
    . . 85: z.pos, z.buf = 0, buf[:d+n]
    . . 86: if n == 0 {
    . . 87: return 0
    . . 88: }
    . . 89: }
    70ms 70ms 90: return z.buf[end]
    . . 91:}

    This function is pretty small, except when `end >= len(z.buf)` is true. So
    if the contents of the if-statement was a function, the Peek function could
    benefit from being inlined.

    What are my options here? Can I redesign this to encourage inlining?
    Thoughts?
    --
    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.
  • Taco de Wolff at Sep 26, 2015 at 7:55 am
    My thought exactly, however then the small function still calls the other
    function, meaning it is not a leaf and thus the compiler won't inline it.

    Moving the check outside the function is not really an option, because it
    is called in a lot of places...there isn't a comment like `//+inline` that
    inlines a function, is there?
    On Sat, Sep 26, 2015 at 8:15 AM, Chris Kastorff wrote:

    I think your best bet is to split this into two functions, one to
    handle the edge case (probably unexported), and the other to handle
    the common case and the if statement which passes control over to the
    other if needed. The common-case function then becomes WAY smaller (in
    this case, 5 lines, one of which doesn't have any expressions in it)
    and is more likely to be inlined because of that.

    There's a compiler flag you can pass to gc to get it to tell you about
    the inlining decisions it's making (among other things), which you can
    use to make sure it's being inlined properly when you want it to be:
    go build -gcflags='-m'
    On Fri, Sep 25, 2015 at 2:54 AM, wrote:
    You could move the check outside the function and call it only when
    it has real work to do.


    Il giorno venerdì 25 settembre 2015 11:46:16 UTC+2, tacod...@gmail.com ha
    scritto:
    I have built a buffer with peek functionality, however the `Peek`
    function
    is called so often that performance is hindered by the fact that it
    does not
    inline. About 1/3rd of the time is spent on this function.

    . . 59:// Peek returns the ith byte relative to
    the
    end position and possibly does an allocation. Calling Peek may
    invalidate
    previous returned byte slices by Bytes or Shift, unless IsEOF returns
    true.
    . . 60:// Peek returns zero when an error has
    occurred, Err return the error.
    208ms 208ms 61:func (z *Shifter) Peek(i int) byte {
    15ms 15ms 62: end := z.end + i
    15ms 15ms 63: if end >= len(z.buf) {
    1ms 1ms 64: if z.err != nil {
    1ms 1ms 65: return 0
    . . 66: }
    . . 67:
    . . 68: // reallocate a new buffer
    (possibly larger)
    . . 69: c := cap(z.buf)
    . . 70: d := len(z.buf) - z.pos
    . . 71: var buf []byte
    . . 72: if 2*d > c {
    . . 73: buf = make([]byte, d,
    2*c+end-z.pos)
    . . 74: } else {
    . . 75: buf = z.buf[:d]
    . . 76: }
    . . 77: copy(buf, z.buf[z.pos:])
    . . 78:
    . . 79: // read in to fill the buffer
    till
    capacity
    . . 80: var n int
    . . 81: n, z.err =
    z.r.Read(buf[d:cap(buf)])
    . . 82: z.eof = (z.err == io.EOF)
    . . 83: end -= z.pos
    . . 84: z.end -= z.pos
    . . 85: z.pos, z.buf = 0, buf[:d+n]
    . . 86: if n == 0 {
    . . 87: return 0
    . . 88: }
    . . 89: }
    70ms 70ms 90: return z.buf[end]
    . . 91:}

    This function is pretty small, except when `end >= len(z.buf)` is true.
    So
    if the contents of the if-statement was a function, the Peek function
    could
    benefit from being inlined.

    What are my options here? Can I redesign this to encourage inlining?
    Thoughts?
    --
    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.
  • Alb Donizetti at Sep 26, 2015 at 1:16 pm
    You can use the -l flag to control the aggressiveness
    of the inliner, and that's it.

    go build -gcflags="-l -l -l -l -m" test.go

    Il giorno sabato 26 settembre 2015 09:56:03 UTC+2, Taco de Wolff ha scritto:
    My thought exactly, however then the small function still calls the other
    function, meaning it is not a leaf and thus the compiler won't inline it.

    Moving the check outside the function is not really an option, because it
    is called in a lot of places...there isn't a comment like `//+inline` that
    inlines a function, is there?

    On Sat, Sep 26, 2015 at 8:15 AM, Chris Kastorff <encr...@gmail.com
    <javascript:>> wrote:
    I think your best bet is to split this into two functions, one to
    handle the edge case (probably unexported), and the other to handle
    the common case and the if statement which passes control over to the
    other if needed. The common-case function then becomes WAY smaller (in
    this case, 5 lines, one of which doesn't have any expressions in it)
    and is more likely to be inlined because of that.

    There's a compiler flag you can pass to gc to get it to tell you about
    the inlining decisions it's making (among other things), which you can
    use to make sure it's being inlined properly when you want it to be:
    go build -gcflags='-m'

    On Fri, Sep 25, 2015 at 2:54 AM, <alb.do...@gmail.com <javascript:>>
    wrote:
    You could move the check outside the function and call it only when
    it has real work to do.


    Il giorno venerdì 25 settembre 2015 11:46:16 UTC+2, tacod...@gmail.com ha
    scritto:
    I have built a buffer with peek functionality, however the `Peek`
    function
    is called so often that performance is hindered by the fact that it
    does not
    inline. About 1/3rd of the time is spent on this function.

    . . 59:// Peek returns the ith byte relative to
    the
    end position and possibly does an allocation. Calling Peek may
    invalidate
    previous returned byte slices by Bytes or Shift, unless IsEOF returns
    true.
    . . 60:// Peek returns zero when an error has
    occurred, Err return the error.
    208ms 208ms 61:func (z *Shifter) Peek(i int) byte {
    15ms 15ms 62: end := z.end + i
    15ms 15ms 63: if end >= len(z.buf) {
    1ms 1ms 64: if z.err != nil {
    1ms 1ms 65: return 0
    . . 66: }
    . . 67:
    . . 68: // reallocate a new buffer
    (possibly larger)
    . . 69: c := cap(z.buf)
    . . 70: d := len(z.buf) - z.pos
    . . 71: var buf []byte
    . . 72: if 2*d > c {
    . . 73: buf = make([]byte, d,
    2*c+end-z.pos)
    . . 74: } else {
    . . 75: buf = z.buf[:d]
    . . 76: }
    . . 77: copy(buf, z.buf[z.pos:])
    . . 78:
    . . 79: // read in to fill the buffer
    till
    capacity
    . . 80: var n int
    . . 81: n, z.err =
    z.r.Read(buf[d:cap(buf)])
    . . 82: z.eof = (z.err == io.EOF)
    . . 83: end -= z.pos
    . . 84: z.end -= z.pos
    . . 85: z.pos, z.buf = 0, buf[:d+n]
    . . 86: if n == 0 {
    . . 87: return 0
    . . 88: }
    . . 89: }
    70ms 70ms 90: return z.buf[end]
    . . 91:}

    This function is pretty small, except when `end >= len(z.buf)` is
    true. So
    if the contents of the if-statement was a function, the Peek function
    could
    benefit from being inlined.

    What are my options here? Can I redesign this to encourage inlining?
    Thoughts?
    --
    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.
  • Luuk van Dijk at Sep 27, 2015 at 7:09 pm
    the repeated -l option is really to flush bugs out of the compiler, it is not guaranteed to produce working binaries.

    --
    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.
  • Luuk van Dijk at Sep 27, 2015 at 7:25 pm
    to answer your earlier question: no there is no //-inline or #pragma inline or any user control over inlining. one '-l' lets you /disable/ inlining, repeating it more often is deliberately undocumented in the help text, and explicitly warned against in the code.

    The reason transitive inlining isn't there is because runtime.Caller's current implementation could not handle that. I have no idea where it is on the agenda.

    to come back to your overall point: your peek is about as fast as it is going to be, but if your program spends 30% of its time in there, maybe you can expose the current unread size, and read up to that into a buffer. E.g. bufio.Reader allows exactly that with .Buffered(), and it's Peek() looks a lot like yours.

    --
    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
postedSep 25, '15 at 9:46a
activeSep 27, '15 at 7:25p
posts7
users4
websitegolang.org

People

Translate

site design / logo © 2021 Grokbase