Zero-copy in Go: sendfile, splice, and the cost of io.Copy

(segflow.github.io)

68 points | by mrngm 13 hours ago ago

15 comments

  • sanxiyn 11 hours ago ago

    A good reminder. It is surprising first time you encounter it.

    Same for Rust. As https://doc.rust-lang.org/stable/std/io/fn.copy.html says, std::io::copy can use copy_file_range(2), sendfile(2), or splice(2).

  • drivebyhooting 9 hours ago ago

    How is the byte counting reader supposed to work in user space without putting the buffer in user space? The article claims there is a way but I want to see what is meant by counting bytes in that case.

    • flakes 9 hours ago ago

      sendfile(2) and io.ReaderFrom both return the number of bytes transmitted. The issue is that users are unaware of (or forget about) the optional interface upgrades and fail to define all the methods required for interface upgrades on their wrapper structs. You can definitely make a counting reader with a minimal performance loss, but the proper solution is less obvious than it ideally should be.

      • drivebyhooting 8 hours ago ago

        Isn’t this a symptom of structural/duck typing where interfaces are not declared? In Java for all its faults this wouldn’t happen because you’d be forced to implement all the interfaces.

        • flakes 8 hours ago ago

          > Isn’t this a symptom of structural/duck typing where interfaces are not declared

          Yes, essentially duck typing. See https://github.com/golang/go/blob/65504872cbca64d77f45828409...

          The logic uses a type assertion to safely verify if the value backing the provided io.Reader interface also implements the io.ReaderFrom interface. If it matches, then it will use the more efficient implementation

              if rf, ok := dst.(ReaderFrom); ok {
                  return rf.ReadFrom(src)
              }
          
          > In Java for all its faults this wouldn’t happen because you’d be forced to implement all the interfaces.

          I don't think I would go that far. In Java, many libraries make heavy use of the `instanceof` keyword, which is more or less the same as Go type assertions.

          • pjmlp 3 hours ago ago

            Yes, but contrary to Go, if you change an interface it will be a compiler error if additional methods are missing, unless they have default implementations.

            In Java type assertions are mostly used when writing code pre-generics style, like downcasting from a common subclasse into the actual implementation, not to see if an interface is supported, as it is a given from the type system.

  • mike_hock 11 hours ago ago

    Zero-Copy in Go: Why magic is an antipattern, and: performance is observable behavior.

    • sanxiyn 11 hours ago ago

      What would you prefer?

      I do think it is criminal this is not documented (https://pkg.go.dev/io#Copy), but I think io.Copy is fine as an API.

      • arccy 10 hours ago ago

        it is documented by saying it calls ReadFrom or WriteTo

        • 9 hours ago ago
          [deleted]
  • sly010 8 hours ago ago

    Beware, there are versions of go where sendfile is broken and only sends the first 4k of a file on macos.

  • joaohaas 11 hours ago ago

    Interesting premise for a post, but I had to stop midway due to the AI slop writing adding meaningless information.

  • throwrioawfo 11 hours ago ago

    Ugh, AI slop writing.

  • inigyou 10 hours ago ago

    This is almost like the expression problem. Copy is a new operation, and you introduced a new type, thus creating a new grid cell nobody from either side could have reasonably known about - except for the fact Copy is in the standard library so you could have known about it but not done anything.