-
Notifications
You must be signed in to change notification settings - Fork 30
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add ReaderFrom/WriterTo for Linux (splice) #29
base: main
Are you sure you want to change the base?
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Although the specific case may actually be EINVAL...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit
// amouont of data it copied is what we put into it. | |
// amount of data it copied is what we put into it. |
On Linux, we can use
splice
to 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.