Boost logo

Boost :

From: Vinícius dos Santos Oliveira (vini.ipsmaker_at_[hidden])
Date: 2022-04-09 18:45:37


Em sáb., 9 de abr. de 2022 às 05:36, Marcelo Zimbres Silva <
mzimbres_at_[hidden]> 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/

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk