Skip to content
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

Use double-buffering to fetch the records from the one during iteration #60

Conversation

nemanja-boric-sociomantic
Copy link
Contributor

The previous approach where the client would ask for the batch from the
node was prone to the latency problems - if the time to prepare and send
batch is non-negligible the performance of the client application would
suffer. This patch improves the situation and decreases the latency in
the following way:

  • As soon as client get the new batch from the node, it will request another one
  • When the new batch arrives the client will store it in the back
    buffer, ready for processing as soon as it finish with the previous one
  • After processing the previous batch and the new batch already arrived,
    the client will just swap the buffers, reading from the new one.

private void[]*[2] batch_buffers;

/// Index of the batch we're currently iterating over
private int current_buffer;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would use bool...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would still prefer int though, because it doesn't restrict the number of the buffers artificially to two (it's easy to generalize this to N buffers this way). For the same reason I would stick with modulo arithmetic.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As an alternative you could use void[]* front_buffer, back_buffer and swap them in the swap method. This would avoid the need for current_buffer and the buffer getter methods.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, that's more neat. I'll take that suggestion 👍

/// Swaps the front and back buffer
private void swap ()
{
this.current_buffer = (this.current_buffer + 1) % 2;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this.current_buffer = !this.current_buffer

?

private int current_buffer;

/// Initializes the double buffer
void init (void[]* delegate() getVoidBuffer)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lazy void[]* if you want to be cool ;)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I think the way it is consistent with rest of the swarm - we're passing acquires as delegates.

@nemanja-boric-sociomantic
Copy link
Contributor Author

Adapted to @david-eckardt-sociomantic 's proposal with fields.

{
auto tmp = this.front_buffer;
this.front_buffer = this.back_buffer;
this.back_buffer = tmp;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typeid(void[]*).swap(&this.front_buffer, &this.back_buffer) :trollface:

(just kidding)

private void[]* back_buffer;

/// Initializes the double buffer
void init (void[]* delegate() getVoidBuffer)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

private?

@nemanja-boric-sociomantic
Copy link
Contributor Author

Updated with private init and with disallowing the client to stop without going through the back buffer.

/// Acquired buffers to store batches of records. Reader always
/// appends to the back one and the RecordStream always uses the
/// front one.
private void[]* front_buffer;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could even name them reader_buffer and record_stream_buffer, to make it super clear which is which.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, or just output and input. (The _buffer suffix isn't needed.)

// Grab the back buffer and move it to the front
this.buffers.swap();

return !this.stopped || this.buffers.front_buffer.length;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tiny adjustment: (*this.buffers.front_buffer).length, for consistency.

@gavin-norman-sociomantic

A higher level idea: would it be possible to completely encapsulate the buffer handling, so that the reader and record stream fibers don't need to know anything about it? They would just push and pop records to/from the buffer.

@nemanja-boric-sociomantic
Copy link
Contributor Author

Note that the Reader already doesn't know anything about the buffer. I'll see if I can encapsulate it so the RecordStream doesn't know anything.

@nemanja-boric-sociomantic
Copy link
Contributor Author

Updated with encapsulation of empty() method. I've tried making it higher-level, but I'm not happy with the end results. The problem is that now the RecordStream doesn't know anything about buffers, it's not just the point of the swapping, but it also doesn't know when it should send Continue signal to the node. This requires delegating the send continue signal from the RecordStream to the buffer class, but that then makes this Buffer not just maintaining two buffers & swap, but to know that the data needs to be requested and that it doesn't come automatically.

I think right now the complexity is quite low, I wouldn't try going further than this, but I'm open to comments.

@gavin-norman-sociomantic

Updated with encapsulation of empty() method. I've tried making it higher-level, but I'm not happy with the end results. The problem is that now the RecordStream doesn't know anything about buffers, it's not just the point of the swapping, but it also doesn't know when it should send Continue signal to the node. This requires delegating the send continue signal from the RecordStream to the buffer class, but that then makes this Buffer not just maintaining two buffers & swap, but to know that the data needs to be requested and that it doesn't come automatically.

Fair enough. I think it probably could be done in a nice way, but it's not incredibly important, right now.

/// ditto
private void[]* input;

/// Initializes the double buffer

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey! I just noticed you're trying to sneak in a new method doc format! Cheeky ;)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How would you ever set the precedent without sneaking it in? :-)

/// front one.
private void[]* output;

/// ditto

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be clearer for each buffer to have its own comment.


**********************************************************************/

private bool waitForRecords ()
{
this.suspendFiber(FiberSuspended.WaitingForRecords);
return !this.stopped;
// In case we already have the batch, don't wait for it

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would make more sense reversed: "Wait for the next batch, unless we already have one.".


if (!(*this.batch_buffer).length)
enableStomping(*this.batch_buffer);
if (!(*batch_buffer).length)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would seem sensible (and simple) to encapsulate this in some kind of push method.

The previous approach where the client would ask for the batch from the
node was prone to the latency problems - if the time to prepare and send
batch is non-negligible the performance of the client application would
suffer. This patch improves the situation and decreases the latency in
the following way:

- As soon as client get the new batch from the node, it will request another one
- When the new batch arrives the client will store it in the back
  buffer, ready for processing as soon as it finish with the previous one
- After processing the previous batch and the new batch already arrived,
  the client will just swap the buffers, reading from the new one.
@nemanja-boric-sociomantic
Copy link
Contributor Author

Updated with the addressed comments by @gavin-norman-sociomantic .

@nemanja-boric-sociomantic nemanja-boric-sociomantic merged commit 11dba97 into sociomantic-tsunami:neo Feb 19, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants