Add ReaderFrom/WriterTo for Linux (splice)#29
Add ReaderFrom/WriterTo for Linux (splice)#29cpuguy83 wants to merge 1 commit intocontainerd:mainfrom
Conversation
0a81acb to
abf9d52
Compare
| // splice not supported on kernel | ||
| atomic.StoreInt32(&spliceSupported, 0) | ||
| return true | ||
| case syscall.EINVAL, syscall.EOPNOTSUPP, syscall.EPERM: |
There was a problem hiding this comment.
Would syscall.EOPNOTSUPP here indicate the process is unable to perform the syscall regardless of whether the kernel supports it? If this doesn't depend on input would it make sense to treat it like ENOSYS?
There was a problem hiding this comment.
I think EOPNOTSUP is more about the transport than the actual system.
For instance, I'd expect EOPNOTSUP if the read side is a unix socket which does not support splicing... but I have not tested this... it may actually be worth adding tests for this.
There was a problem hiding this comment.
Although the specific case may actually be EINVAL...
There was a problem hiding this comment.
That's fair, its not super clear when these standard return values would be returned in this case and better not to set a global value
6e8e675 to
2e799de
Compare
On Linux, we can use `splice` to optimize copies to happen in kernel when copying to other files. Since this is already a pipe, this should always work when copying to/from another file. Signed-off-by: Brian Goff <cpuguy83@gmail.com>
2e799de to
19b9833
Compare
|
Updated this, added some new test cases and improved the implementation slightly:
|
|
I'm going to do some more testing on this as well. |
|
FYI, ended up making a new repo to play around with this a bit more, benchmarks, etc: https://github.com/cpuguy83/pipes I've made some changes to the implementation that make it a bit cleaner which I'll move here as well. |
Silly question; the benchmark in the readme doesn't show a delta; is that because some option wasn't set? (Performance improvement sounds great though!) |
|
Hmm I'm not sure. |
|
@cpuguy83 @kzys @dmcgowan what's the status on this one? Do we want to have this merged and included in a release? I went looking what changes are in |
|
Sorry. I have missed the ping. Let me take a look next week. |
samuelkarp
left a comment
There was a problem hiding this comment.
A few questions and nits, but otherwise LGTM.
| select { | ||
| case <-f.opened: | ||
| return f.readFrom(r) | ||
| default: | ||
| } | ||
| select { | ||
| case <-f.opened: | ||
| return f.readFrom(r) |
There was a problem hiding this comment.
What's the purpose of trying to read from <-f.opened twice here?
| if !ok { | ||
| return copyBuffer(f.file, r) | ||
| } |
There was a problem hiding this comment.
Does this error case also need to handle lr != nil?
| if !ok { | |
| return copyBuffer(f.file, r) | |
| } | |
| if !ok { | |
| if lr != nil { | |
| r = lr | |
| } | |
| return copyBuffer(f.file, r) | |
| } |
| remain = spliceMax | ||
| } | ||
|
|
||
| // Hear the RawConn Read/Write methods allow us to utilize the go runtime |
There was a problem hiding this comment.
nit
| // Hear the RawConn Read/Write methods allow us to utilize the go runtime | |
| // Here the RawConn Read/Write methods allow us to utilize the go runtime |
| case nil: | ||
| handled = true | ||
| if n == 0 { | ||
| // At EOF | ||
| return true | ||
| } | ||
| case unix.EINTR: | ||
| continue |
There was a problem hiding this comment.
nit: I think it'd be slightly more readable to have an explicit continue in here.
| case nil: | |
| handled = true | |
| if n == 0 { | |
| // At EOF | |
| return true | |
| } | |
| case unix.EINTR: | |
| continue | |
| case nil: | |
| handled = true | |
| if n == 0 { | |
| // At EOF | |
| return true | |
| } | |
| continue | |
| case unix.EINTR: | |
| continue |
Also, what do you think about reordering the switch to order successful cases before errors and have all the errors grouped together? Something like nil, unix.EINTR, unix.EAGAIN, unix.ENOSYS, syscall.EINVAL, syscall.EOPNOTSUPP, syscall.EPERM, default?
| select { | ||
| case <-f.opened: | ||
| return f.writeTo(w) | ||
| default: | ||
| } | ||
|
|
||
| select { | ||
| case <-f.opened: | ||
| return f.writeTo(w) |
There was a problem hiding this comment.
Same question here about the repeated read from f.opened.
| data := strings.Repeat("This is a test, this is only a test.", 1000) | ||
|
|
||
| // For these test cases we only call ReadFrom and validate there is no error and the | ||
| // amouont of data it copied is what we put into it. |
There was a problem hiding this comment.
nit
| // amouont of data it copied is what we put into it. | |
| // amount of data it copied is what we put into it. |
|
@cpuguy83 Have any bandwidth to try and push this forward? If not I'm happy to help, this would be sweet to have |
On Linux, we can use
spliceto optimize copies to happen in kernelwhen copying to other files.
Since this is already a pipe, this should always work when copying
to/from another file.