Skip to content

Conversation

@jamespud
Copy link

  • Optimize getBytes/setBytes/readBytes/writeBytes methods
  • Avoid intermediate byte array copy when source/destination is NettyBackedChannelBuffer
  • Directly use Netty's ByteBuf transfer capabilities
  • Add fallback to intermediate array copy for other ChannelBuffer implementations
  • Improve performance by 4-5x for Netty-to-Netty buffer transfers

Below are test code:

import org.apache.dubbo.remoting.buffer.ByteBufferBackedChannelBuffer;
import org.apache.dubbo.remoting.buffer.ChannelBuffer;
import org.apache.dubbo.remoting.transport.netty4.NettyBackedChannelBuffer;
import org.apache.dubbo.remoting.transport.netty4.NettyBackedChannelBufferOld;

import java.nio.ByteBuffer;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.PooledByteBufAllocator;
import org.junit.jupiter.api.Test;

public class NettyBackedChannelBufferBenchmarkTest {

    private static final int BUFFER_SIZE = 1024 * 1024; // 1MB
    private static final int TEST_ITERATIONS = 1000;

    @Test
    public void testPerformance() {
        byte[] data = new byte[BUFFER_SIZE];
        for (int i = 0; i < data.length; i++) {
            data[i] = (byte) (i % 256);
        }

        ByteBuf nettyBuffer = PooledByteBufAllocator.DEFAULT.buffer(BUFFER_SIZE);
        nettyBuffer.writeBytes(data);
        ChannelBuffer nettySource = new NettyBackedChannelBuffer(nettyBuffer);

        // Netty->Netty vs Netty->Other (getBytes)
        double nettyToNettyGet = testGetBytes(
                nettySource, new NettyBackedChannelBuffer(PooledByteBufAllocator.DEFAULT.buffer(BUFFER_SIZE)));
        double nettyToOtherGet = testGetBytes(
                nettySource, new NettyBackedChannelBufferOld(PooledByteBufAllocator.DEFAULT.buffer(BUFFER_SIZE)));
        System.out.printf(
                "getBytes: Netty->Netty %.2f MB/s, Netty->Other %.2f MB/s, ratio %.2f\n",
                nettyToNettyGet, nettyToOtherGet, (nettyToOtherGet > 0 ? nettyToNettyGet / nettyToOtherGet : 0.0));

        // Netty->Netty vs Netty->Other (setBytes)
        double nettyToNettySet = testSetBytes(
                nettySource, new NettyBackedChannelBuffer(PooledByteBufAllocator.DEFAULT.buffer(BUFFER_SIZE)));
        double nettyToOtherSet = testSetBytes(
                nettySource, new NettyBackedChannelBufferOld(PooledByteBufAllocator.DEFAULT.buffer(BUFFER_SIZE)));
        System.out.printf(
                "setBytes: Netty->Netty %.2f MB/s, Netty->Other %.2f MB/s, ratio %.2f\n",
                nettyToNettySet, nettyToOtherSet, (nettyToOtherSet > 0 ? nettyToNettySet / nettyToOtherSet : 0.0));

        System.out.println("-----------------------------------");

        ByteBufferBackedChannelBuffer byteBufferSource =
                new ByteBufferBackedChannelBuffer(ByteBuffer.allocate(BUFFER_SIZE));
        
        if (byteBufferSource.readableBytes() < BUFFER_SIZE) {
            byteBufferSource.clear();
            byteBufferSource.writeBytes(data);
        }

        double otherToNettyGet = testGetBytes(
                byteBufferSource, new NettyBackedChannelBuffer(PooledByteBufAllocator.DEFAULT.buffer(BUFFER_SIZE)));
        double otherToOtherGet = testGetBytes(
                byteBufferSource, new NettyBackedChannelBufferOld(PooledByteBufAllocator.DEFAULT.buffer(BUFFER_SIZE)));
        System.out.printf(
                "getBytes: Other->Netty %.2f MB/s, Other->Other %.2f MB/s, ratio %.2f\n",
                otherToNettyGet, otherToOtherGet, (otherToOtherGet > 0 ? otherToNettyGet / otherToOtherGet : 0.0));

        double otherToNettySet = testSetBytes(
                byteBufferSource, new NettyBackedChannelBuffer(PooledByteBufAllocator.DEFAULT.buffer(BUFFER_SIZE)));
        double otherToOtherSet = testSetBytes(
                byteBufferSource, new NettyBackedChannelBufferOld(PooledByteBufAllocator.DEFAULT.buffer(BUFFER_SIZE)));
        System.out.printf(
                "setBytes: Other->Netty %.2f MB/s, Other->Other %.2f MB/s, ratio %.2f\n",
                otherToNettySet, otherToOtherSet, (otherToOtherSet > 0 ? otherToNettySet / otherToOtherSet : 0.0));

        nettyBuffer.release();
    }

    private static double testGetBytes(ChannelBuffer src, ChannelBuffer dest) {
        if (src.readableBytes() < BUFFER_SIZE) {
            byte[] fill = new byte[BUFFER_SIZE];
            for (int i = 0; i < fill.length; i++) {
                fill[i] = (byte) (i % 256);
            }
            src.clear();
            src.writeBytes(fill);
        }

        for (int i = 0; i < TEST_ITERATIONS / 10; i++) {
            dest.clear(); 
            src.getBytes(0, dest, 0, BUFFER_SIZE);
        }

        long startTime = System.nanoTime();
        for (int i = 0; i < TEST_ITERATIONS; i++) {
            dest.clear();
            src.getBytes(0, dest, 0, BUFFER_SIZE);
        }
        long endTime = System.nanoTime();
        long totalTime = endTime - startTime;
        
        dest.release();

        return TEST_ITERATIONS / (totalTime / 1_000_000_000.0);
    }

    private static double testSetBytes(ChannelBuffer src, ChannelBuffer dest) {
        if (src.readableBytes() < BUFFER_SIZE) {
            byte[] fill = new byte[BUFFER_SIZE];
            for (int i = 0; i < fill.length; i++) {
                fill[i] = (byte) (i % 256);
            }
            src.clear();
            src.writeBytes(fill);
        }

        for (int i = 0; i < TEST_ITERATIONS / 10; i++) {
            dest.clear();
            dest.setBytes(0, src, 0, BUFFER_SIZE);
        }

        long startTime = System.nanoTime();
        for (int i = 0; i < TEST_ITERATIONS; i++) {
            dest.clear();
            dest.setBytes(0, src, 0, BUFFER_SIZE);
        }
        long endTime = System.nanoTime();
        long totalTime = endTime - startTime;
        
        dest.release();

        return TEST_ITERATIONS / (totalTime / 1_000_000_000.0);
    }
}

Test output:

getBytes: Netty->Netty 36047.87 MB/s, Netty->Other 6926.83 MB/s, ratio 5.20
setBytes: Netty->Netty 49730.58 MB/s, Netty->Other 13129.59 MB/s, ratio 3.79
-----------------------------------
getBytes: Other->Netty 53048.21 MB/s, Other->Other 46807.54 MB/s, ratio 1.13
setBytes: Other->Netty 11765.61 MB/s, Other->Other 11816.55 MB/s, ratio 1.00

@codecov-commenter
Copy link

codecov-commenter commented Jan 20, 2026

Codecov Report

❌ Patch coverage is 10.71429% with 25 lines in your changes missing coverage. Please review.
✅ Project coverage is 60.75%. Comparing base (be40746) to head (0a7a9ef).

Files with missing lines Patch % Lines
...ing/transport/netty4/NettyBackedChannelBuffer.java 10.71% 24 Missing and 1 partial ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##                3.3   #16027      +/-   ##
============================================
+ Coverage     60.74%   60.75%   +0.01%     
+ Complexity    11754    11750       -4     
============================================
  Files          1949     1949              
  Lines         88898    88914      +16     
  Branches      13407    13409       +2     
============================================
+ Hits          53998    54018      +20     
- Misses        29337    29340       +3     
+ Partials       5563     5556       -7     
Flag Coverage Δ
integration-tests-java21 32.22% <0.00%> (+<0.01%) ⬆️
integration-tests-java8 32.30% <0.00%> (-0.09%) ⬇️
samples-tests-java21 32.12% <0.00%> (-0.01%) ⬇️
samples-tests-java8 29.73% <0.00%> (+0.03%) ⬆️
unit-tests-java11 59.01% <10.71%> (+0.01%) ⬆️
unit-tests-java17 58.50% <10.71%> (-0.02%) ⬇️
unit-tests-java21 58.50% <10.71%> (+<0.01%) ⬆️
unit-tests-java25 58.45% <10.71%> (-0.01%) ⬇️
unit-tests-java8 59.00% <10.71%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Contributor

Copilot AI left a comment

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 optimizes buffer transfer operations in NettyBackedChannelBuffer by eliminating intermediate byte array copies when both source and destination are NettyBackedChannelBuffer instances. The changes leverage Netty's native ByteBuf transfer capabilities for direct buffer-to-buffer transfers while maintaining backward compatibility through a fallback path for other ChannelBuffer implementations.

Changes:

  • Optimize getBytes, setBytes, readBytes, and writeBytes methods to use direct Netty ByteBuf transfers when possible
  • Add type checking to detect NettyBackedChannelBuffer instances and use optimized path
  • Maintain fallback to intermediate byte array copy for other ChannelBuffer implementations

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Contributor

Copilot AI left a comment

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 2 out of 2 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Member

@EarthChen EarthChen left a comment

Choose a reason for hiding this comment

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

LGTM

jamespud and others added 4 commits January 23, 2026 23:37
- Optimize getBytes/setBytes/readBytes/writeBytes methods
- Avoid intermediate byte array copy when source/destination is NettyBackedChannelBuffer
- Directly use Netty's ByteBuf transfer capabilities
- Add fallback to intermediate array copy for other ChannelBuffer implementations
- Improve performance by 4-5x for Netty-to-Netty buffer transfers
…dubbo/remoting/transport/netty4/NettyBackedChannelBufferTest.java

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@jamespud jamespud force-pushed the feature/optimize-netty-buffer-copy branch from 2da618a to 0a7a9ef Compare January 23, 2026 15:37
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.

4 participants