diff --git a/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/binary/JmhMapSerdesBenchmark.java b/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/binary/JmhMapSerdesBenchmark.java new file mode 100644 index 0000000000000..4744f7e125b59 --- /dev/null +++ b/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/binary/JmhMapSerdesBenchmark.java @@ -0,0 +1,112 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.internal.benchmarks.jmh.binary; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import org.apache.ignite.Ignition; +import org.apache.ignite.configuration.IgniteConfiguration; +import org.apache.ignite.internal.IgniteEx; +import org.apache.ignite.internal.binary.BinaryContext; +import org.apache.ignite.internal.binary.BinaryUtils; +import org.apache.ignite.internal.binary.BinaryWriterEx; +import org.apache.ignite.internal.binary.streams.BinaryOutputStream; +import org.apache.ignite.internal.binary.streams.BinaryStreams; +import org.apache.ignite.internal.util.typedef.internal.U; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; + +import static java.util.concurrent.TimeUnit.NANOSECONDS; +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.openjdk.jmh.annotations.Mode.AverageTime; +import static org.openjdk.jmh.annotations.Scope.Thread; + +/** */ +@State(Thread) +@OutputTimeUnit(NANOSECONDS) +@BenchmarkMode(AverageTime) +@Warmup(iterations = 3, time = 30, timeUnit = SECONDS) +@Measurement(iterations = 3, time = 30, timeUnit = SECONDS) +public class JmhMapSerdesBenchmark { + /** */ + @Param({"5", "100000", "1000000"}) + private String size; + + /** */ + @Param({"HashMap", "ConcurrentHashMap", "LinkedHashMap"}) + private String mapType; + + /** */ + private BinaryContext bctx; + + /** */ + private BinaryOutputStream out; + + /** */ + private Map data; + + /** */ + public static void main(String[] args) throws Exception { + org.openjdk.jmh.Main.main(new String[]{JmhMapSerdesBenchmark.class.getName()}); + } + + /** */ + @Setup + public void setup() throws Exception { + IgniteEx node = (IgniteEx)Ignition.start(new IgniteConfiguration()); + + bctx = node.context().cacheObjects().binaryContext(); + out = BinaryStreams.outputStream((int)(100 * U.MB)); + + Ignition.stopAll(false); + + if ("HashMap".equals(mapType)) + data = new HashMap<>(); + else if ("ConcurrentHashMap".equals(mapType)) + data = new ConcurrentHashMap<>(); + else if ("LinkedHashMap".equals(mapType)) + data = new LinkedHashMap<>(); + else + throw new IllegalArgumentException("Unknown map type: " + mapType); + + int sz = Integer.parseInt(size); + for (int i = 0; i < sz; i++) + data.put(i, i); + } + + /** */ + @Benchmark + public void mapSerialization(Blackhole bh) { + BinaryWriterEx writer = BinaryUtils.writer(bctx, out); + + writer.writeMap(data); + + out.position(0); + + bh.consume(writer); + } +} diff --git a/modules/binary/api/src/main/java/org/apache/ignite/internal/binary/BinaryWriterExImpl.java b/modules/binary/api/src/main/java/org/apache/ignite/internal/binary/BinaryWriterExImpl.java index 6ac562955775a..2bf503b043551 100644 --- a/modules/binary/api/src/main/java/org/apache/ignite/internal/binary/BinaryWriterExImpl.java +++ b/modules/binary/api/src/main/java/org/apache/ignite/internal/binary/BinaryWriterExImpl.java @@ -65,7 +65,7 @@ class BinaryWriterExImpl implements BinaryWriterEx { private int typeId; /** */ - private final int start; + private int start; /** Raw offset position. */ private int rawOffPos; @@ -838,16 +838,27 @@ void writeBooleanField(@Nullable Boolean val) { if (obj == null) out.writeByte(GridBinaryMarshaller.NULL); else { - BinaryWriterExImpl writer = new BinaryWriterExImpl( - ctx, - out, - schema, - handles(), - failIfUnregistered, - GridBinaryMarshaller.UNREGISTERED_TYPE_ID - ); - - writer.marshal(obj); + // Store state. + int typeId0 = this.typeId; + int start0 = this.start; + int rawOffPos0 = this.rawOffPos; + int schemaId0 = this.schemaId; + int fieldCnt0 = this.fieldCnt; + BinaryInternalMapper mapper0 = this.mapper; + + // Handles not cleared, because, in this mode they shared down to hierarchy. + clearState(false); + + // Maybe recursive `writeObject` invocation which will change state. + marshal(obj); + + // Restore state. + this.typeId = typeId0; + this.start = start0; + this.rawOffPos = rawOffPos0; + this.schemaId = schemaId0; + this.fieldCnt = fieldCnt0; + this.mapper = mapper0; } } @@ -856,16 +867,29 @@ void writeBooleanField(@Nullable Boolean val) { if (obj == null) out.writeByte(GridBinaryMarshaller.NULL); else { - BinaryWriterExImpl writer = new BinaryWriterExImpl( - ctx, - out, - schema, - null, - failIfUnregistered, - GridBinaryMarshaller.UNREGISTERED_TYPE_ID - ); - - writer.marshal(obj); + // Store state. + int typeId0 = this.typeId; + int start0 = this.start; + int rawOffPos0 = this.rawOffPos; + int schemaId0 = this.schemaId; + int fieldCnt0 = this.fieldCnt; + BinaryInternalMapper mapper0 = this.mapper; + BinaryWriterHandles handles0 = this.handles; + + // Handles cleared, because, in this mode they are NOT shared down to hierarchy. + clearState(true); + + // Maybe recursive `writeObject` invocation which will change state. + marshal(obj); + + // Restore state. + this.typeId = typeId0; + this.start = start0; + this.rawOffPos = rawOffPos0; + this.schemaId = schemaId0; + this.fieldCnt = fieldCnt0; + this.mapper = mapper0; + this.handles = handles0; } } @@ -1549,4 +1573,17 @@ boolean tryWriteAsHandle(Object obj) { @Override public BinaryContext context() { return ctx; } + + /** Clears writer state. */ + private void clearState(boolean clearHandler) { + this.typeId = GridBinaryMarshaller.UNREGISTERED_TYPE_ID; + this.start = out.position(); + this.rawOffPos = 0; + this.schemaId = BinaryUtils.schemaInitialId(); + this.fieldCnt = 0; + this.mapper = null; + + if (clearHandler) + this.handles = null; + } }