-
Notifications
You must be signed in to change notification settings - Fork 18k
net: add Buffers.ReadFrom and Buffers.Write? #17607
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
Comments
FWIW I played with implementing readv (ReadFrom/Write) once (see cl/30102) and got some nice numbers (which I don't have anymore) in my tests. My implementation kept the current type and all calls to Write/ReadFrom just overwrote the old data. I didn't have a real use for it back than (and still currently don't have), but from the raw numbers I got it seemed like it could be easily used to speed up some tools (probably stuff like parsers, etc.)
As I understand your code ReadFrom would need to reslice net.Buffers to make WriteTo work properly. But what about Write? Would it append to the net.Buffers (and potentially adding a new slice) or only write as much as fits and return an error otherwise? If it appends a new slice users would need to remember to reuse it. Although Read/Write on net.Buffers will probably rarely be used, I'd like to have this clearly defined and though about. |
Yeah, that I don't care whether
|
As far as I can see, the only meaningful reason to use I guess I don't see why anybody would want to use the same |
@ianlancetaylor, I don't know that anyone would necessarily want to reuse the Buffers type, but it seemed worth thinking through what it might look like. I do want to make sure that we know how Buffers will support readv, or else that we know we will never want to support readv. If readv will need something different, the "Buffers" may not be the right name. I agree that |
It does sound like we agree that the current Buffers definition has a reasonable way to support readv, though, so I'll punt the actual addition of readv to Go 1.9. |
Making the And what about sendmmsg/recvmmsg? They are more general than writev/readv but not as portable. Unlike writev, sendmmsg can write to unconnected sockets and multiple addresses, these could be useful for udp servers like QUIC. For now, you can use |
+1 for supporting readv in net.Conn I am working on a network proxy project. A proxy workflow (per connection) now looks like below:
The problem is that it is difficult to determine the size of the buffer. Using a 32K or larger buffer can increase throughput for a single connection with large payload. But it increases (unnecessary) memory usage when there are many connections with little payload. With net.Buffers.ReadFrom, I can do the following:
In this way, the memory usage can automatic adapt for different network scenarios. |
Can someone comment on the recvmmsg usage? |
I don't see how the existing |
A quick follow-up on this. We have implemented our own version of readv() in V2Ray, based on The library is coupled with the rest of the project for the moment. If others are interested, we can make it standalone package. |
@VictoriaRaymond how did you deal with AFAIK, we have two options:
Both of above options are not great. I do not understand why https://go-review.googlesource.com/c/go/+/30102/ was abandoned due to missing application. One might argue that the following optimization is possbile:
However, on my Linux 4.9 system, above optimization results in two subsequent @ianlancetaylor is it worthwile to condense that case down to an executable microbenchmark or are there other (political / resource) constraints prohibiting implementation of |
@problame, CL/30102 was abandoned to "missing application" by the author's own admission. It is not a statement that no such application exists. |
@problame I don't think there is any opposition to (I'll note that I think it could be done today using the |
@ianlancetaylor what are your thoughts on the EAGAIN problem pointed out above? Is the assumption that, when using raw syscalls, we need to set the FD to blocking? Or is it possible to use read inside syscall.RawConn.Read? |
The |
Go 1.8 is adding net.Buffers, with Read and WriteTo methods. These both move data from the buffers to a target, either a []byte (for Read) or a Writer (for WriteTo). The implementation of WriteTo on one of the package net-implemented net.Conns uses writev. So far so good.
What about readv? What is that going to look like? I ask because it might influence some of the finer details of Buffers. For example, if we can't support readv with the same data structure, we may want to call it WriteBuffers and have a separate ReadBuffers. I do think we can support readv, but we should make sure.
For example you could imagine that readv works by setting up a Buffers with space in the []bytes between len and cap, like:
to read the first 16 bytes into the first slice and the next 1k into the second, for both
b.Write(data)
andb.ReadFrom(conn)
. In order to allow repeating the reads until you get all the data you want, you would need to define that Write and ReadFrom (unlike Read and WriteTo) do not remove full slices from Buffers: they just top off what's available. Probably only top off the final non-empty buffer. That is, if you haveand you read 5 bytes, where do they go? It seems wrong for them to go into the first slice, since that would stick them in the middle of the 4+15 bytes already semantically in the buffer. So probably they go into the middle slice, which has 5 (20-15) spare bytes of capacity. This implies that Write/ReadFrom needs to scan backward from the end to find the first non-empty buffer.
It would be nice if Read/WriteTo, as they pull data out of the slices, didn't throw away the slices entirely, so that you could imagine using a Buffers not unlike a bytes.Buffer, where once allocated to a particular size you could repeatedly Write into it and then Read out from it (or ReadFrom into it and WriteTo out of it). Unfortunately even if the writing operations did avoid cutting slices out of the buffer, they need some way to track what is left to be written from a particular slice, and the way to do that is to advance the base pointer, reducing len and cap. At the end of a complete write the lens of the buffers are necessarily 0, and there's no way to back them back up. Concretely, if I have:
and I write 99 bytes, I'm left now with Buffers{slice(len=1,cap=1)}. If I write 100 bytes, I'm left with Buffers{} (no slices). The desire to reuse a Buffer would suggest that maybe Read/WriteTo shouldn't pull slices out, but it's not terribly useful to leave them in when they'd have len (and likely cap) 0 so they are basically useless anyway.
This implies that reuse of a Buffers for new reading into the buffers after writing from the buffers is probably just hard. You'd need to do something like:
That seems OK to me. I just want to make sure we've thought about all this.
@bradfitz, what do you think? Does this seem like a reasonable basic plan for readv? Should we do it for Go 1.8 so that there's never a net.Buffers that only works for writev?
The text was updated successfully, but these errors were encountered: