-
Notifications
You must be signed in to change notification settings - Fork 8
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 zero-copy serialization APIs. #357
base: master
Are you sure you want to change the base?
Conversation
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.
Left some small comments but LGTM, nice work!
buffer.go
Outdated
// Keep the buffer alive during the write, to prevent the GC from | ||
// collecting the memory while it's being used. | ||
n, err := w.Write(unsafe.Slice((*byte)(cbuffer), writeSize)) | ||
runtime.KeepAlive(b) |
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 since b
is the object that implements this method the keepalive isn't needed
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.
Removed. I was being extra cautious, but if we call b.WriteTo(w)
, it becomes clear that b
is being kept somewhere reachable.
serialize.go
Outdated
return &buffer, nil | ||
} | ||
|
||
// SerializeArrayNonEmptyDomainToBuffer gets and serializes the array nonempty domain and returns a Buffer object containing the payload. |
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.
// SerializeArrayNonEmptyDomainToBuffer gets and serializes the array nonempty domain and returns a Buffer object containing the payload. | |
// SerializeArrayNonEmptyDomain gets and serializes the array nonempty domain. |
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.
Fixed, thanks for catching it.
} | ||
assert.NotEmpty(t, bytes.Bytes()) | ||
runtime.GC() | ||
runtime.GC() |
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 don't understand the purpose of the last few lines in this test, but I see there was one like it just below so I'm probably missing something.
Should there be a check here that buffer
was garbage collected, or is the test verifying something else with the explicit calls to runtime.GC()
?
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 copied the GC calls from the existing test, I don't know what their purpose was.
buffer_list.go
Outdated
@@ -44,6 +45,36 @@ func (b *BufferList) Context() *Context { | |||
return b.context | |||
} | |||
|
|||
// WriteTo writes the contents of a BufferList to an io.Writer. | |||
func (b *BufferList) WriteTo(w io.Writer) (n int64, err error) { |
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.
Were the named return values intentional? Feel free to disregard, just pointing it out since I don't see these names being directly used in the function body.
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.
Must have been from a previous iteration of the code. Removed.
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.
Thanks for this very useful optimization, great job! One question, is this Core PR a prerequisite? If yes let's mark this one as Draft so that we don't accidentally merge it before Go has bundled to a Core version that includes that change.
Strictly speaking no, this PR can be merged without waiting for the Core PR. The Core PR is a prerequisite for fully taking advantage of the APIs added in this PR in the REST server (and even this might not be true, I have thought of a potential workaround). |
It will make some stuff easier in the REST server.
fcfc6ff
to
38155e3
Compare
SC-58009
This PR updates the
Buffer
andBufferList
types to implement theio.WriterTo
interface, by writing the data directly to anio.Writer
without an intermediate copy to Go-backed memory. ForBuffer
we do that with theUnsafe.Slice
function (it is safe as long as theWriter
follows the contract and does not retain the slice after the call toWrite
), and forBufferList
we callWriteTo
for each buffer.In order to take advantage of this, new serialization APIs were added that return a
Buffer
instead of abyte[]
(and unavoidably had to make a copy to Go-managed memory). The old serialization APIs, as well as theBuffer.Serialize()
andBufferList.Flatten()
functions were deprecated.