diff --git a/oncrpc4j-benchmark/src/main/java/org/dcache/oncrpc4j/benchmarks/XdrBenchmark.java b/oncrpc4j-benchmark/src/main/java/org/dcache/oncrpc4j/benchmarks/XdrBenchmark.java new file mode 100644 index 0000000..39e02c0 --- /dev/null +++ b/oncrpc4j-benchmark/src/main/java/org/dcache/oncrpc4j/benchmarks/XdrBenchmark.java @@ -0,0 +1,60 @@ +package org.dcache.oncrpc4j.benchmarks; + +import java.nio.ByteBuffer; +import java.util.concurrent.ThreadLocalRandom; +import org.dcache.oncrpc4j.xdr.Xdr; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.infra.Blackhole; + +@State(Scope.Benchmark) +@BenchmarkMode(Mode.Throughput) +public class XdrBenchmark { + + + @Param({"1024", "8192", "262144", "1048576"}) + private String size; + + private Xdr xdr; + + private ByteBuffer bb; + + @Setup + public void setUp() { + + byte[] buf = new byte[Integer.parseInt(size)]; + ThreadLocalRandom.current().nextBytes(buf); + + bb = ByteBuffer.wrap(buf); + xdr = new Xdr(256); + } + + + @Benchmark + public void encodeByteBuffer(Blackhole blackhole) { + + xdr.beginEncoding(); + bb.clear().limit(bb.capacity()); + xdr.xdrEncodeByteBuffer(bb); + xdr.endEncoding(); + + blackhole.consume(xdr); + } + + @Benchmark + public void encodeByteBufferShallow(Blackhole blackhole) { + + xdr.beginEncoding(); + bb.clear().limit(bb.capacity()); + xdr.xdrEncodeShallowByteBuffer(bb); + xdr.endEncoding(); + + blackhole.consume(xdr); + } + +} diff --git a/oncrpc4j-core/src/main/java/org/dcache/oncrpc4j/xdr/Xdr.java b/oncrpc4j-core/src/main/java/org/dcache/oncrpc4j/xdr/Xdr.java index f680f5c..a0ff68e 100644 --- a/oncrpc4j-core/src/main/java/org/dcache/oncrpc4j/xdr/Xdr.java +++ b/oncrpc4j-core/src/main/java/org/dcache/oncrpc4j/xdr/Xdr.java @@ -24,6 +24,8 @@ import java.nio.charset.StandardCharsets; import org.dcache.oncrpc4j.grizzly.GrizzlyMemoryManager; import org.glassfish.grizzly.Buffer; +import org.glassfish.grizzly.memory.BuffersBuffer; +import org.glassfish.grizzly.memory.ByteBufferWrapper; import org.glassfish.grizzly.memory.MemoryManager; import static com.google.common.base.Preconditions.checkState; @@ -759,6 +761,29 @@ public void xdrEncodeByteBuffer(ByteBuffer buf) { _buffer.position(_buffer.position() + padding); } + /** + * A version of {@link #xdrEncodeByteBuffer(ByteBuffer)} which avoids internal copy. + * Note: any change to the {@code buf} will cause unpredicted behavior. + * + * @param buf The buffer from which bytes are to be retrieved. + */ + public void xdrEncodeShallowByteBuffer(ByteBuffer buf) { + int len = buf.remaining(); + int padding = (4 - (len & 3)) & 3; + xdrEncodeInt(len); + int ep = _buffer.position() + buf.remaining(); + var b = new ByteBufferWrapper(buf); + b.allowBufferDispose(true); + var composite = BuffersBuffer.create(_memoryManager); + composite.append(_buffer.slice(0, _buffer.position())).append(b); + composite.position(ep); + composite.limit(ep); + + _buffer = composite; + ensureCapacity(padding); + _buffer.position(_buffer.position() + padding); + } + /** * Encodes (aka "serializes") a vector of bytes, which is nothing more than * a series of octets (or 8 bits wide bytes), each packed into its very own