Skip to content

Commit

Permalink
Perf[BMQ,MQB]: do not build tmp functions on ACKs
Browse files Browse the repository at this point in the history
Signed-off-by: Evgeny Malygin <[email protected]>
  • Loading branch information
678098 committed Nov 3, 2024
1 parent 198843c commit b821442
Show file tree
Hide file tree
Showing 4 changed files with 176 additions and 11 deletions.
55 changes: 51 additions & 4 deletions src/applications/bmqtool/m_bmqtool_application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,54 @@ namespace {

BALL_LOG_SET_NAMESPACE_CATEGORY("BMQTOOL.APPLICATION");

/// Stack-built functor to pass to `bmqp::ProtocolUtil::buildEvent`
struct BuildConfirmFunctor
: public bmqp::ProtocolUtil::BuildEventActionFunctor {
// DATA
bmqa::ConfirmEventBuilder& d_confirmBuilder;
const bmqa::Message& d_message;

// CREATORS
inline explicit BuildConfirmFunctor(
bmqa::ConfirmEventBuilder& confirmBuilder,
const bmqa::Message& message)
: d_confirmBuilder(confirmBuilder)
, d_message(message)
{
// NOTHING
}

// MANIPULATORS
inline bmqt::EventBuilderResult::Enum operator()() BSLS_KEYWORD_OVERRIDE
{
return d_confirmBuilder.addMessageConfirmation(d_message);
}
};

/// Stack-built functor to pass to `bmqp::ProtocolUtil::buildEvent`
struct BuildConfirmOverflowFunctor
: public bmqp::ProtocolUtil::BuildEventOverflowFunctor {
// DATA
bmqa::Session& d_session;
bmqa::ConfirmEventBuilder& d_builder;

// CREATORS
inline explicit BuildConfirmOverflowFunctor(
bmqa::Session& session,
bmqa::ConfirmEventBuilder& builder)
: d_session(session)
, d_builder(builder)
{
// NOTHING
}

// MANIPULATORS
inline void operator()() BSLS_KEYWORD_OVERRIDE
{
d_session.confirmMessages(&d_builder);
}
};

} // close unnamed namespace

// -----------------
Expand Down Expand Up @@ -685,10 +733,9 @@ void Application::onMessageEvent(const bmqa::MessageEvent& event)

bmqt::EventBuilderResult::Enum rc =
bmqp::ProtocolUtil::buildEvent(
bdlf::BindUtil::bind(f, &confirmBuilder, message),
bdlf::BindUtil::bind(&bmqa::Session::confirmMessages,
d_session_mp.get(),
&confirmBuilder));
BuildConfirmFunctor(confirmBuilder, message),
BuildConfirmOverflowFunctor(*d_session_mp.get(),
confirmBuilder));

BSLS_ASSERT_SAFE(rc == 0);
(void)rc; // compiler happiness
Expand Down
18 changes: 18 additions & 0 deletions src/groups/bmq/bmqp/bmqp_protocolutil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,24 @@ const char k_HEX_TO_INT_TABLE[24] = {0, 1, 2, 3, 4, 5, 6, 7,
99, 10, 11, 12, 13, 14, 15, 99};
} // close unnamed namespace

// --------------------------------------------
// struct ProtocolUtil::BuildEventActionFunctor
// --------------------------------------------

ProtocolUtil::BuildEventActionFunctor::~BuildEventActionFunctor()
{
// NOTHING
}

// ----------------------------------------------
// struct ProtocolUtil::BuildEventOverflowFunctor
// ----------------------------------------------

ProtocolUtil::BuildEventOverflowFunctor::~BuildEventOverflowFunctor()
{
// NOTHING
}

// -------------------
// struct ProtocolUtil
// -------------------
Expand Down
50 changes: 50 additions & 0 deletions src/groups/bmq/bmqp/bmqp_protocolutil.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,32 @@ namespace bmqp {
struct ProtocolUtil {
// PUBLIC TYPES

/// The common interface for functors passed to `buildEvent`.
/// These functors exist as an alternative of using `bsl::function` on
/// critical performance paths, since they could be built on stack without
/// arguments copying and memory allocations/deallocations. The price of
/// using virtual table is small compared to `bsl::function`.
/// Usage: inherit from this functor locally, override the () operator and
/// store some arguments in the custom functor if needed.
struct BuildEventActionFunctor {
virtual ~BuildEventActionFunctor();

virtual bmqt::EventBuilderResult::Enum operator()() = 0;
};

/// The common interface for functors passed to `buildEvent`.
/// These functors exist as an alternative of using `bsl::function` on
/// critical performance paths, since they could be built on stack without
/// arguments copying and memory allocations/deallocations. The price of
/// using virtual table is small compared to `bsl::function`.
/// Usage: inherit from this functor locally, override the () operator and
/// store some arguments in the custom functor if needed.
struct BuildEventOverflowFunctor {
virtual ~BuildEventOverflowFunctor();

virtual void operator()() = 0;
};

template <class VALUE>
struct QueueInfo {
struct StreamInfo {
Expand Down Expand Up @@ -399,10 +425,19 @@ struct ProtocolUtil {
/// Invoke the specified `action` and if it returns e_EVENT_TOO_BIG then
/// invoke the specified `overflowCb` and call `action` again. Return
/// result code returned from `action`
/// DEPRECATED: use `buildEvent` with functors instead, to avoid loss of
/// performance from `bsl::function` on critical paths.
static bmqt::EventBuilderResult::Enum buildEvent(
const bsl::function<bmqt::EventBuilderResult::Enum(void)>& action,
const bsl::function<void(void)>& overflowCb);

/// Invoke the specified `action` and if it returns e_EVENT_TOO_BIG then
/// invoke the specified `overflowCb` and call `action` again. Return
/// result code returned from `action`
static bmqt::EventBuilderResult::Enum
buildEvent(BuildEventActionFunctor& action,
BuildEventOverflowFunctor& overflowCb);

/// Encode Receipt into the specified `blob` for the specified
/// `partitionId`, `primaryLeaseId`, and `sequenceNumber`.
static void buildReceipt(bdlbb::Blob* blob,
Expand Down Expand Up @@ -708,6 +743,21 @@ inline bmqt::EventBuilderResult::Enum ProtocolUtil::buildEvent(
return rc;
};

inline bmqt::EventBuilderResult::Enum
ProtocolUtil::buildEvent(BuildEventActionFunctor& action,
BuildEventOverflowFunctor& overflowCb)
{
bmqt::EventBuilderResult::Enum rc = action();
if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(
bmqt::EventBuilderResult::e_EVENT_TOO_BIG == rc)) {
BSLS_PERFORMANCEHINT_UNLIKELY_HINT;

overflowCb();
rc = action();
}
return rc;
};

inline void ProtocolUtil::buildReceipt(bdlbb::Blob* blob,
int partitionId,
unsigned int primaryLeaseId,
Expand Down
64 changes: 57 additions & 7 deletions src/groups/mqb/mqba/mqba_clientsession.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,57 @@ void finalizeClosedHandle(bsl::string description,
<< handle->handleParameters();
}

/// Stack-built functor to pass to `bmqp::ProtocolUtil::buildEvent`
struct BuildAckFunctor : public bmqp::ProtocolUtil::BuildEventActionFunctor {
// DATA
bmqp::AckEventBuilder& d_ackBuilder;
int d_status;
int d_correlationId;
const bmqt::MessageGUID& d_messageGUID;
int d_queueId;

// CREATORS
inline explicit BuildAckFunctor(bmqp::AckEventBuilder& ackBuilder,
int status,
int correlationId,
const bmqt::MessageGUID& messageGUID,
int queueId)
: d_ackBuilder(ackBuilder)
, d_status(status)
, d_correlationId(correlationId)
, d_messageGUID(messageGUID)
, d_queueId(queueId)
{
// NOTHING
}

// MANIPULATORS
inline bmqt::EventBuilderResult::Enum operator()() BSLS_KEYWORD_OVERRIDE
{
return d_ackBuilder.appendMessage(d_status,
d_correlationId,
d_messageGUID,
d_queueId);
}
};

/// Stack-built functor to pass to `bmqp::ProtocolUtil::buildEvent`
struct BuildAckOverflowFunctor
: public bmqp::ProtocolUtil::BuildEventOverflowFunctor {
// DATA
mqba::ClientSession& d_session;

// CREATORS
inline explicit BuildAckOverflowFunctor(mqba::ClientSession& session)
: d_session(session)
{
// NOTHING
}

// MANIPULATORS
inline void operator()() BSLS_KEYWORD_OVERRIDE { d_session.flush(); }
};

} // close unnamed namespace

// -------------------------
Expand Down Expand Up @@ -560,13 +611,12 @@ void ClientSession::sendAck(bmqt::AckResult::Enum status,

// Append the ACK to the ackBuilder
bmqt::EventBuilderResult::Enum rc = bmqp::ProtocolUtil::buildEvent(
bdlf::BindUtil::bind(&bmqp::AckEventBuilder::appendMessage,
&d_state.d_ackBuilder,
bmqp::ProtocolUtil::ackResultToCode(status),
correlationId,
messageGUID,
queueId),
bdlf::BindUtil::bind(&ClientSession::flush, this));
BuildAckFunctor(d_state.d_ackBuilder,
bmqp::ProtocolUtil::ackResultToCode(status),
correlationId,
messageGUID,
queueId),
BuildAckOverflowFunctor(*this));

if (rc != bmqt::EventBuilderResult::e_SUCCESS) {
BALL_LOG_ERROR << "Failed to append ACK [rc: " << rc << ", source: '"
Expand Down

0 comments on commit b821442

Please sign in to comment.