Skip to content

Feat: Add adaptive input message batching - #3484

Open
FangzuoZhang wants to merge 3 commits into
apache:masterfrom
FangzuoZhang:feature/adaptive-input-message-batching
Open

Feat: Add adaptive input message batching#3484
FangzuoZhang wants to merge 3 commits into
apache:masterfrom
FangzuoZhang:feature/adaptive-input-message-batching

Conversation

@FangzuoZhang

@FangzuoZhang FangzuoZhang commented Aug 24, 2026

Copy link
Copy Markdown

What problem does this PR solve?

Issue Number: N/A

Related work:

Problem Summary:

InputMessenger currently schedules each parsed input message in a separate
bthread. For short message handlers and bursty traffic on a single connection,
bthread creation and scheduling may account for a significant portion of the
request-processing overhead.

This PR introduces optional input message batching. Messages parsed from the
same socket can be processed sequentially in one bthread, reducing scheduling
overhead while preserving the existing behavior by default.

What is changed and the side effects?

Design

Parsed messages
  m1  m2  m3  ...  mN
           |
           v
+--------------------------+
| Batch Size Controller    |
| fixed or adaptive (EMA)  |
| size: 1 / 2 / 4 / 8 / 16|
+------------+-------------+
             |
             v
+--------------------------+
| InputMessageBatch        |
| [m1, m2, ... , mk]       |
+------------+-------------+
             |
             v
      One bthread
             |
             v
  m1 -> m2 -> ... -> mk
  processed sequentially

Changed:

  • Add the experimental input_message_batch_process_size gflag:
    • 0 or 1: preserve the original one-message-per-bthread behavior.
    • Values greater than 1: use a fixed batch size.
    • -1: adaptively select a batch size from 1, 2, 4, 8, and 16.
    • Values smaller than -1 are rejected.
  • Add InputMessageBatch to own and process messages in their original order.
  • Add per-socket adaptive state based on an exponentially weighted moving
    average of messages parsed from each read.
  • Increase the adaptive batch size gradually and decrease it more quickly when
    the observed burst size drops.
  • Reset adaptive history after switching away from adaptive mode.
  • Support batch scheduling in TCP, RDMA, and UBShm/UBRing transports.
  • Preserve the existing last-message scheduling optimization.
  • Keep progressive-read messages on the individual-message path.
  • Disable batching when user code runs in coroutine mode.
  • Fall back to synchronous processing if batch allocation or bthread creation
    fails.

Side effects:

  • Performance effects:

    The default value is 0, so existing deployments retain the original
    scheduling behavior.

    When batching is enabled, it reduces bthread creation and scheduling overhead
    for bursty workloads. A larger fixed batch may increase the time that later
    messages wait behind earlier handlers. Adaptive mode limits the maximum batch
    size to 16 and decreases the batch size quickly when the observed burst size
    drops.

  • Breaking backward compatibility:

    There is no change to the public RPC protocol or default runtime behavior.

    The internal Transport interface gains a QueueMessages virtual method.
    Downstream custom transport implementations derived directly from
    Transport must implement this method.

Performance test

The RDMA performance example was used with a single connection and multiple
outstanding requests on that connection. Each attachment size was tested three
times.

  • Baseline: input_message_batch_process_size=0
  • Optimized: input_message_batch_process_size=-1
  • queue_depth must be greater than 1 to produce message bursts on the same
    connection.
  • Latency values are in microseconds.
  • CPU utilization may exceed 100% because it represents multi-core process CPU
    usage.
  • QPS improvement is calculated against the average QPS of the unoptimized
    baseline.

Average results

The following results were measured on a Kunpeng 950 server.
Each attachment size was tested three times, and the reported values are the
arithmetic averages of those runs.

  • Baseline: input_message_batch_process_size=0
  • Optimized: input_message_batch_process_size=-1
  • Latency values are in microseconds.
  • CPU utilization may exceed 100% because it represents multi-core process CPU
    usage.
  • QPS improvement is calculated against the average QPS of the baseline.
Attachment Avg Latency P90 P99 Baseline QPS Batched QPS Avg Server CPU Client CPU QPS Improvement
0 bytes 351.00 543.00 810.00 2684.100 2886.265 1336.00% 3950.67% +7.53%
256 bytes 398.00 610.67 927.00 2377.408 2545.978 1315.00% 4798.00% +7.09%
1 KB 438.33 683.67 1044.67 2174.254 2320.362 1322.67% 3732.67% +6.72%
4 KB 660.67 1135.67 2123.33 1486.802 1544.824 1270.00% 2577.33% +3.90%
8 KB 1031.00 2310.00 5289.33 1049.357 1104.763 1177.00% 1993.33% +5.28%
100 KB 7388.67 12362.00 22406.67 134.602 138.385 925.33% 988.00% +2.81%

Check List:

@FangzuoZhang FangzuoZhang changed the title Add adaptive input message batching Feat: Add adaptive input message batching Aug 24, 2026
@chenBright
chenBright requested a lite review from Copilot August 24, 2026 14:19

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR adds an optional (experimental) input-message batching path to reduce bthread creation/scheduling overhead under bursty traffic by processing multiple parsed messages from the same socket sequentially in a single bthread, while preserving the existing behavior by default.

Changes:

  • Introduces -input_message_batch_process_size (0/1 disabled, fixed >1, adaptive -1) and implements InputMessageBatch + batch scheduling in InputMessenger.
  • Extends the internal Transport interface with QueueMessages(...) and implements it in TCP/RDMA/UBShm transports.
  • Adds per-socket adaptive batching state (EMA + current batch size) and exposes debug output for the new stats.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/brpc/input_messenger.h Adds InputMessageBatch type and batching-related helper declarations.
src/brpc/input_messenger.cpp Implements batching flag, adaptive sizing logic, and batch enqueueing/flush behavior.
src/brpc/transport.h Adds new pure-virtual QueueMessages API for transports.
src/brpc/tcp_transport.h Declares TCP transport support for QueueMessages.
src/brpc/tcp_transport.cpp Implements batch enqueue via bthread for TCP transport.
src/brpc/rdma_transport.h Declares RDMA transport support for QueueMessages.
src/brpc/rdma_transport.cpp Implements batch enqueue via bthread for RDMA transport.
src/brpc/ubshm_transport.h Declares UBShm transport support for QueueMessages.
src/brpc/ubshm_transport.cpp Implements batch enqueue via bthread for UBShm transport.
src/brpc/socket.h Adds per-socket adaptive batching state fields.
src/brpc/socket.cpp Initializes/resets new per-socket adaptive state; adds debug printing.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/brpc/input_messenger.cpp Outdated
Comment thread src/brpc/ubshm_transport.cpp Outdated
Comment thread src/brpc/rdma_transport.cpp Outdated
@wwbmmm
wwbmmm requested a lite review from Copilot August 27, 2026 12:36

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.

Comment thread src/brpc/input_messenger.h Outdated
Comment thread src/brpc/input_messenger.cpp Outdated
Comment on lines +106 to +125
void TcpTransport::QueueMessages(InputMessageBatch* input_msgs,
int* num_bthread_created) {
if (!input_msgs || input_msgs->empty()) {
delete input_msgs;
return;
}
bthread_t th;
bthread_attr_t tmp =
(FLAGS_usercode_in_pthread ? BTHREAD_ATTR_PTHREAD : BTHREAD_ATTR_NORMAL) |
BTHREAD_NOSIGNAL;
tmp.keytable_pool = _socket->keytable_pool();
tmp.tag = bthread_self_tag();
if (!FLAGS_usercode_in_coroutine && bthread_start_background(
&th, &tmp, ProcessInputMessageBatch, input_msgs) == 0) {
++*num_bthread_created;
} else {
input_msgs->Run();
delete input_msgs;
}
}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Addressed in commit 95368db.

I extracted the common batch scheduling logic into Transport::QueueInputMessageBatch. The shared helper now handles:

  • empty batches;
  • transport-specific synchronous execution;
  • bthread attributes, name, keytable pool, and tag;
  • synchronous fallback when bthread creation fails;
  • num_bthread_created accounting.

TcpTransport, RdmaTransport, and UBShmTransport now only provide their transport-specific synchronous execution condition and delegate the remaining work to the shared helper.

The unit tests cover the empty-batch path, synchronous execution, asynchronous scheduling, and bthread creation accounting.

@wasphin

wasphin commented Aug 28, 2026

Copy link
Copy Markdown
Member

Please also add the corresponding unit tests.

@FangzuoZhang

Copy link
Copy Markdown
Author

Please also add the corresponding unit tests.

Thanks. Corresponding unit tests have been added in commit 95368db.

The tests cover:

  • in-order and exactly-once batch processing;
  • inclusion of the final message in the current batch;
  • unchanged behavior when batching is disabled;
  • unchanged progressive-read behavior;
  • nothrow destruction and handler exception containment;
  • cleanup of remaining messages;
  • shared transport scheduling for empty, synchronous, and asynchronous paths;
  • bthread creation accounting;
  • gflag validation;
  • adaptive batch growth, reduction, sample capping, and socket reset.

zhangfangzuo added 3 commits September 11, 2026 11:33
Centralize batch scheduling for TCP, RDMA, and UBShm transports. Make input batch cleanup exception-safe and cover batching behavior with unit tests.
@FangzuoZhang
FangzuoZhang force-pushed the feature/adaptive-input-message-batching branch from 95368db to 2f2dc88 Compare September 11, 2026 03:51
@wwbmmm
wwbmmm requested a lite review from Copilot September 11, 2026 03:52
@FangzuoZhang

Copy link
Copy Markdown
Author

The merge conflicts have been resolved, and this branch has been rebased onto the latest master commit (d638a0cb).

During conflict resolution, input batching was adapted to the newly introduced InputMessengerProcessor architecture. The adaptive batching state is now maintained by each processor, keeping the TCP and RDMA input paths isolated.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Critical and moderate issues remain, including ownership, exception-safety, and test compilation or synchronization problems.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (3)

src/brpc/input_messenger.cpp:184

  • This updated comment still says that all messages except the last are processed in separate bthreads or batches, but the batching path below appends last_msg to the batch and processes it there as well. Please distinguish the unbatched last-message optimization from the batched path so the comment describes the new behavior accurately.
    // - If the socket has several messages, all messages will be parsed (
    //   meaning cutting from butil::IOBuf. serializing from protobuf is part of
    //   "process") in this bthread. All messages except the last one will be
    //   processed in separate bthreads, or in batches when
    //   -input_message_batch_process_size is -1 or greater than 1. To minimize

src/brpc/input_messenger_processor.cpp:272

  • The tests exercise UpdateAdaptiveBatchSize directly, but none drives ProcessNewMessage with input_message_batch_process_size=-1. That leaves the per-socket EMA update, the real adaptive batch selection, and the reset when switching away from adaptive mode unverified; add an integration test for this branch.
    const bool adaptive_batch_process =
        configured_batch_size == -1 && !FLAGS_usercode_in_coroutine;
    size_t batch_size = configured_batch_size > 0
                        ? static_cast<size_t>(configured_batch_size) : 1;
    if (adaptive_batch_process) {

test/brpc_input_messenger_unittest.cpp:411

  • Waiting for values.size() does not guarantee the worker has finished the handler: ProcessBatchTestMessage records the value before its DestroyingPtr calls RecordDestroy. The test then destroys the stack recorder without waiting for those callbacks, so a queued batch can access a dead recorder. Wait for all four destructions before leaving the test.
    ASSERT_TRUE(recorder.WaitForSize(4));
  • Files reviewed: 14/14 changed files
  • Comments generated: 5
  • Review effort level: Lite

Comment on lines +100 to +102
InputMessageBatch() {}
explicit InputMessageBatch(size_t capacity);
~InputMessageBatch() noexcept;
Comment on lines +271 to +272
batch_size = brpc::InputMessengerProcessor::UpdateAdaptiveBatchSize(
&ema_q8, batch_size, std::numeric_limits<size_t>::max());
Comment on lines +396 to +400
socket->_transport->QueueMessages(batch, &num_bthread_created);
EXPECT_EQ((std::vector<int>{1, 2}), recorder.Snapshot());
EXPECT_EQ(0, num_bthread_created);

socket->_transport->QueueMessages(
Comment on lines +200 to +201
batch->reset(new (std::nothrow) InputMessageBatch(batch_size));
}
last_msg, num_bthread_created, false);
return;
}
(*batch)->add(msg);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants