Skip to content

Commit 755944a

Browse files
committed
xdr: optimize encoding of ByteBuffer
Motivation: When a ByteBuffer is encoded into XdrStream then a implicit copy of underlaying data is performed. By wrapping ByteBuffer with and building a composite buffer the additional copy can be avoid. Modification: Add Xdr#xdrEncodeShallowByteBuffer that creates a composite buffer to avoid extra copy. Result: Encoding of a ByteBuffer is an O(1) operation, if possible (original buffer is not modified). Buffer size XdrBenchmark.encodeByteBufferShallow 1024 thrpt 5 9402670.599 ± 26808.413 ops/s XdrBenchmark.encodeByteBufferShallow 8192 thrpt 5 9102908.516 ± 64365.443 ops/s XdrBenchmark.encodeByteBufferShallow 262144 thrpt 5 9182060.856 ± 83327.650 ops/s XdrBenchmark.encodeByteBufferShallow 1048576 thrpt 5 9052320.542 ± 23289.002 ops/s XdrBenchmark.encodeByteBuffer 1024 thrpt 5 28773875.725 ± 250222.745 ops/s XdrBenchmark.encodeByteBuffer 8192 thrpt 5 6439241.956 ± 24534.514 ops/s XdrBenchmark.encodeByteBuffer 262144 thrpt 5 88237.372 ± 704.050 ops/s XdrBenchmark.encodeByteBuffer 1048576 thrpt 5 21276.243 ± 116.393 ops/s Acked-by: Albert Rossi Target: master
1 parent f7cf70a commit 755944a

File tree

2 files changed

+85
-0
lines changed
  • oncrpc4j-benchmark/src/main/java/org/dcache/oncrpc4j/benchmarks
  • oncrpc4j-core/src/main/java/org/dcache/oncrpc4j/xdr

2 files changed

+85
-0
lines changed
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package org.dcache.oncrpc4j.benchmarks;
2+
3+
import java.nio.ByteBuffer;
4+
import java.util.concurrent.ThreadLocalRandom;
5+
import org.dcache.oncrpc4j.xdr.Xdr;
6+
import org.openjdk.jmh.annotations.Benchmark;
7+
import org.openjdk.jmh.annotations.BenchmarkMode;
8+
import org.openjdk.jmh.annotations.Mode;
9+
import org.openjdk.jmh.annotations.Param;
10+
import org.openjdk.jmh.annotations.Scope;
11+
import org.openjdk.jmh.annotations.Setup;
12+
import org.openjdk.jmh.annotations.State;
13+
import org.openjdk.jmh.infra.Blackhole;
14+
15+
@State(Scope.Benchmark)
16+
@BenchmarkMode(Mode.Throughput)
17+
public class XdrBenchmark {
18+
19+
20+
@Param({"1024", "8192", "262144", "1048576"})
21+
private String size;
22+
23+
private Xdr xdr;
24+
25+
private ByteBuffer bb;
26+
27+
@Setup
28+
public void setUp() {
29+
30+
byte[] buf = new byte[Integer.parseInt(size)];
31+
ThreadLocalRandom.current().nextBytes(buf);
32+
33+
bb = ByteBuffer.wrap(buf);
34+
xdr = new Xdr(256);
35+
}
36+
37+
38+
@Benchmark
39+
public void encodeByteBuffer(Blackhole blackhole) {
40+
41+
xdr.beginEncoding();
42+
bb.clear().limit(bb.capacity());
43+
xdr.xdrEncodeByteBuffer(bb);
44+
xdr.endEncoding();
45+
46+
blackhole.consume(xdr);
47+
}
48+
49+
@Benchmark
50+
public void encodeByteBufferShallow(Blackhole blackhole) {
51+
52+
xdr.beginEncoding();
53+
bb.clear().limit(bb.capacity());
54+
xdr.xdrEncodeShallowByteBuffer(bb);
55+
xdr.endEncoding();
56+
57+
blackhole.consume(xdr);
58+
}
59+
60+
}

oncrpc4j-core/src/main/java/org/dcache/oncrpc4j/xdr/Xdr.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
import java.nio.charset.StandardCharsets;
2525
import org.dcache.oncrpc4j.grizzly.GrizzlyMemoryManager;
2626
import org.glassfish.grizzly.Buffer;
27+
import org.glassfish.grizzly.memory.BuffersBuffer;
28+
import org.glassfish.grizzly.memory.ByteBufferWrapper;
2729
import org.glassfish.grizzly.memory.MemoryManager;
2830

2931
import static com.google.common.base.Preconditions.checkState;
@@ -759,6 +761,29 @@ public void xdrEncodeByteBuffer(ByteBuffer buf) {
759761
_buffer.position(_buffer.position() + padding);
760762
}
761763

764+
/**
765+
* A version of {@link #xdrEncodeByteBuffer(ByteBuffer)} which avoids internal copy.
766+
* Note: any change to the {@code buf} will cause unpredicted behavior.
767+
*
768+
* @param buf The buffer from which bytes are to be retrieved.
769+
*/
770+
public void xdrEncodeShallowByteBuffer(ByteBuffer buf) {
771+
int len = buf.remaining();
772+
int padding = (4 - (len & 3)) & 3;
773+
xdrEncodeInt(len);
774+
int ep = _buffer.position() + buf.remaining();
775+
var b = new ByteBufferWrapper(buf);
776+
b.allowBufferDispose(true);
777+
var composite = BuffersBuffer.create(_memoryManager);
778+
composite.append(_buffer.slice(0, _buffer.position())).append(b);
779+
composite.position(ep);
780+
composite.limit(ep);
781+
782+
_buffer = composite;
783+
ensureCapacity(padding);
784+
_buffer.position(_buffer.position() + padding);
785+
}
786+
762787
/**
763788
* Encodes (aka "serializes") a vector of bytes, which is nothing more than
764789
* a series of octets (or 8 bits wide bytes), each packed into its very own

0 commit comments

Comments
 (0)