Em sáb., 9 de abr. de 2022 às 05:36, Marcelo Zimbres Silva < mzimbres@gmail.com> escreveu:
[...] does the explanation above [example spanning several paragraphs] make things clearer?
Yes. The pattern is much clearer now. Thank you. I'll need to think more about the problem, but for now I'm tending to sympathize with your approach — consuming as it parses. Keeping the message in the buffer is not as much of a problem as you think. The memory usage will not be greater (std::string, for instance, will hold not only the string itself, but an extra possibly unused area reserved for SSO). However the pattern here might indeed favour fragmented allocations more. It might be more important to stress the allocators than trying to avoid them. Is there any redis command where anything resembles a need for a "streaming reply"? How does Aedies deal with it? Is there support to deserialize directly to an object type? For instance: struct MyType { int x; std::string y; }; Also, you should add a page on the documentation comparing Aedis to other libraries (e.g. cpp-bredis).
Golang's bufio.Scanner implementation will avoid excessive
memcopies to the head of the buffer by using a "moving window" over the buffer. It only uses the tail of the buffer to new read operations. Only when the buffer fully fills it'll memcpy the current message to the head of the buffer as to have more space.
That is something I would like to see in Asio. It would definitely improve performance.
You don't need to use dynamic buffers tho. Dynamic buffer tries to abstract buffered IO. The important trait present in dynamic buffer is: as the buffer is external to the IO object, it allows one to steal the buffered data to parse (as in finding the new end of message markers) and consume some slice using a different parser. Do not confuse this trait with the example you gave earlier about JSON; it's a different trait here. Honestly I think the "abstraction" dynamic buffer abstracts too little (it's quite literally a region of buffered data + capacity which you can implement by declaring one or two members in your own class) to offer a value worth pursuing. What really offers ease of use to the final user is formatted IO (which must be done on top of buffered IO — be it external or internal). Your library abstracts formatted redis IO. You could just as well buffer the data yourself (as in wrapping the underlying socket in a new class that keeps the state machine plus buffer there). For instance, C++'s <iostream> (which usually is *not* a good place to look for inspirations anyhow) merges formatted and buffered IO in a single place (and so do the standard scanners for many other languages), and that's fine. If you think composition of "parsers" is important (as in the trait enabled by dynamic buffer; which I don't think it matters to redis) to Aedis you can just add a method to return the buffered data and another to set_assume the new buffered data. It's a little more complex than that (you need to ensure the buffer will outlive the socket as to not UB on outstanding async operations, and plenty of other smallish details), but not rocket science either. Do notice how the *protocol* dictates the usage pattern for the buffered data here. It doesn't always make sense to decouple buffered IO and formatted IO. Keep the buffer yourself and ignore what everyone else is doing. You don't need to use every Boost.Asio class just because the class exists (that'd be absurd). The layers are: raw/low-level IO: Boost.Asio abstractions are good and were refined little-by-little over its many years of existence buffered IO: far too little to worry about; and Boost.Asio never really pursued the topic beyond an almost-toy abstraction formatted IO: complex and it helps to have abstractions from external libraries Do notice as well that buffered IO is a dual: input and output. We only talked about buffered input. For buffered output, what you really want is either (1) improving performance by writing data in batches, or (2) avoiding the problems that originate from composed operations and concurrent writers (and several approaches would exist anyway — from fiber-level mutexes to queueing sockets), and Boost.Asio doesn't even offer o solution here (which should be the job of buffered output). However, that doesn't concern Aedis. I'm just trying to persuade you to not worry too much about dynamic buffer. -- Vinícius dos Santos Oliveira https://vinipsmaker.github.io/