Skip to content
This repository has been archived by the owner on Oct 17, 2018. It is now read-only.

Message writer perf improvements #27

Merged
merged 10 commits into from
Jun 6, 2018
Merged

Message writer perf improvements #27

merged 10 commits into from
Jun 6, 2018

Conversation

cw9
Copy link
Contributor

@cw9 cw9 commented Jun 5, 2018

  • Instead have all message writers across all consumer services to share the same global message pool, this pr creates a message pool per consumer service, which reduces lock contention on the pool.

  • remove unnecessary atomic usage in message.go

  • move the following expensive operations away from Write() to the background retry thread.

    • reset the atomic.Bool in message
    • add the message to the ack map

retryAtNanos *atomic.Int64
retried *atomic.Int64
isAcked *atomic.Bool
retryAtNanos int64
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this only accessed from one goroutine?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep

Copy link
Contributor

Choose a reason for hiding this comment

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

Cool, do you still need isAcked to be atomic then? Or is it that some of these are accessed only from one goroutine, and others could be accessed concurrently? If the latter, would recommend adding short comments next to these field to explain their access patterns to save future readers any confusion around this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah modified the comment

@@ -132,6 +131,8 @@ func newConsumerServiceWriter(
return nil, errUnknownConsumptionType
}
router := newAckRouter(int(numShards))
mPool := newMessagePool(opts.MessagePoolOptions())
Copy link
Contributor

Choose a reason for hiding this comment

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

FYI in some cases adding a pool does more harm than good to performance due to the overhead of lock contention trumping the benefit of reduced GC. Not necessarily true for your case, but I've observed that for the samples on the agg tier (you need one sample per timer value), so something that's worth keeping in mind.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, I do notice the lock contention in the pool here is heavy as well, but it still saves tons of allocation and gc in my case, I feel one pool per consumer service is kinda ok for now, in worst case I could try one pool per message writer, but I'm not gonna go that far right now...

Copy link
Contributor

Choose a reason for hiding this comment

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

Have you tried disabling the pool completely and observe the CPU impact? Again it may very well be the case that pooling still does you more good than harm, but I'm just curious.

@@ -24,7 +24,6 @@ import (
"github.com/m3db/m3msg/generated/proto/msgpb"
"github.com/m3db/m3msg/producer"
"github.com/m3db/m3msg/protocol/proto"
Copy link
Contributor

Choose a reason for hiding this comment

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

New line

Copy link
Contributor Author

Choose a reason for hiding this comment

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

sg

m.Close()
w.mPool.Put(m)
if w.mPool != nil {
m.Close()
Copy link
Contributor

Choose a reason for hiding this comment

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

So if pool is nil, you don't need to close the message?

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 don't, that close Close() is only needed when the message can be reused

@@ -335,8 +342,10 @@ func (w *messageWriterImpl) retryBatchWithLock(
// do not stay in memory forever.
w.Ack(m.Metadata())
w.queue.Remove(e)
m.Close()
w.mPool.Put(m)
if w.mPool != nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd actually pull this out into a member function e.g., freeMessage of the writer so you can reuse.

Copy link
Contributor

Choose a reason for hiding this comment

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

same goes for newMessage above.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

sg

Copy link
Contributor

@xichen2020 xichen2020 left a comment

Choose a reason for hiding this comment

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

LGTM

@cw9 cw9 merged commit 805602a into master Jun 6, 2018
@cw9 cw9 deleted the chao-message-pool branch June 6, 2018 18:59
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants