From b8b3ed56ecd1aff05abe7c2e5085da40e9ffad5f Mon Sep 17 00:00:00 2001 From: Philip Pronin Date: Sat, 1 Apr 2023 21:15:10 -0700 Subject: [PATCH] fix lazy serialization performance in pathological case Summary: `rewriteDouble()` currently walks the whole output queue, which means in a pathological case of serializing container of lazy objects we get accidentally quadratic performance. Reviewed By: luciang Differential Revision: D44607256 fbshipit-source-id: 4072a1f76c97d37089aa97b1f419d78a3701a38b --- folly/io/Cursor.h | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/folly/io/Cursor.h b/folly/io/Cursor.h index ea680b7581f..8520131cae3 100644 --- a/folly/io/Cursor.h +++ b/folly/io/Cursor.h @@ -908,6 +908,23 @@ class RWCursor : public detail::CursorBase, IOBuf>, explicit RWCursor(IOBufQueue& queue) : RWCursor((queue.flushCache(), queue.head_.get())) {} + // Efficient way to advance to position cursor to the end of the queue, + // using cached length instead of a walk via advanceToEnd(). + struct AtEnd {}; + RWCursor(IOBufQueue& queue, AtEnd) : RWCursor(queue) { + if (!queue.options().cacheChainLength) { + this->advanceToEnd(); + } else { + this->crtBuf_ = this->buffer_->prev(); + this->crtBegin_ = this->crtBuf_->data(); + this->crtEnd_ = this->crtBuf_->tail(); + this->crtPos_ = this->crtEnd_; + this->absolutePos_ = + queue.chainLength() - (this->crtPos_ - this->crtBegin_); + DCHECK_EQ(this->getCurrentPosition(), queue.chainLength()); + } + } + template explicit RWCursor(const detail::CursorBase& cursor) : detail::CursorBase, IOBuf>(cursor), @@ -1260,6 +1277,14 @@ class QueueAppender : public detail::Writable { return RWCursor(*queueCache_.queue()); } + template + RWCursor tail(size_t n) { + RWCursor result( + *queueCache_.queue(), typename RWCursor::AtEnd{}); + result -= n; + return result; + } + void trimEnd(size_t n) { queueCache_.queue()->trimEnd(n); } private: