From 5baaab27b1c883d58eb357304dc1a513099ab8c5 Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Tue, 18 Feb 2025 11:29:17 -0800 Subject: [PATCH 01/15] Optimize BSON Array decoding performance - Use `BsonTypeCodecMap` for decoding in `BsonArray` to enable faster access. - Optimize `BsonTypeClassMap` by replacing `Map` with a plain array to eliminate redundant hash computations. JAVA-5339 --- .../main/org/bson/codecs/BsonArrayCodec.java | 16 +++- .../org/bson/codecs/BsonTypeClassMap.java | 60 +++++++------- .../main/org/bson/codecs/DocumentCodec.java | 2 +- driver-benchmarks/build.gradle | 10 +++ .../jmh/codec/BsonDocumentBenchmark.java | 80 +++++++++++++++++++ 5 files changed, 132 insertions(+), 36 deletions(-) create mode 100644 driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonDocumentBenchmark.java diff --git a/bson/src/main/org/bson/codecs/BsonArrayCodec.java b/bson/src/main/org/bson/codecs/BsonArrayCodec.java index 9b4bef5e4c5..ac62edfd9f0 100644 --- a/bson/src/main/org/bson/codecs/BsonArrayCodec.java +++ b/bson/src/main/org/bson/codecs/BsonArrayCodec.java @@ -24,6 +24,7 @@ import org.bson.codecs.configuration.CodecRegistry; import static org.bson.assertions.Assertions.notNull; +import static org.bson.codecs.BsonValueCodecProvider.getBsonTypeClassMap; import static org.bson.codecs.configuration.CodecRegistries.fromProviders; /** @@ -34,8 +35,10 @@ public class BsonArrayCodec implements Codec { private static final CodecRegistry DEFAULT_REGISTRY = fromProviders(new BsonValueCodecProvider()); + private static final BsonTypeCodecMap DEFAULT_BSON_TYPE_CODEC_MAP = new BsonTypeCodecMap(getBsonTypeClassMap(), DEFAULT_REGISTRY); private final CodecRegistry codecRegistry; + private final BsonTypeCodecMap bsonTypeCodecMap; /** * Creates a new instance with a default codec registry that uses the {@link BsonValueCodecProvider}. @@ -43,7 +46,7 @@ public class BsonArrayCodec implements Codec { * @since 3.4 */ public BsonArrayCodec() { - this(DEFAULT_REGISTRY); + this(DEFAULT_REGISTRY, DEFAULT_BSON_TYPE_CODEC_MAP); } /** @@ -52,7 +55,12 @@ public BsonArrayCodec() { * @param codecRegistry the codec registry */ public BsonArrayCodec(final CodecRegistry codecRegistry) { - this.codecRegistry = notNull("codecRegistry", codecRegistry); + this(codecRegistry, new BsonTypeCodecMap(getBsonTypeClassMap(), codecRegistry)); + } + + private BsonArrayCodec(final CodecRegistry codecRegistry, final BsonTypeCodecMap bsonTypeCodecMap) { + this.codecRegistry = notNull("Codec registry", codecRegistry); + this.bsonTypeCodecMap = notNull("bsonTypeCodecMap", bsonTypeCodecMap); } @Override @@ -93,7 +101,7 @@ public Class getEncoderClass() { * @return the non-null value read from the reader */ protected BsonValue readValue(final BsonReader reader, final DecoderContext decoderContext) { - return codecRegistry.get(BsonValueCodecProvider.getClassForBsonType(reader.getCurrentBsonType())).decode(reader, decoderContext); + BsonType currentBsonType = reader.getCurrentBsonType(); + return (BsonValue) bsonTypeCodecMap.get(currentBsonType).decode(reader, decoderContext); } - } diff --git a/bson/src/main/org/bson/codecs/BsonTypeClassMap.java b/bson/src/main/org/bson/codecs/BsonTypeClassMap.java index 82144e9b4aa..6e009228e19 100644 --- a/bson/src/main/org/bson/codecs/BsonTypeClassMap.java +++ b/bson/src/main/org/bson/codecs/BsonTypeClassMap.java @@ -31,9 +31,10 @@ import org.bson.types.ObjectId; import org.bson.types.Symbol; +import java.util.Arrays; import java.util.Collections; import java.util.Date; -import java.util.HashMap; +import java.util.EnumSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -71,7 +72,7 @@ */ public class BsonTypeClassMap { static final BsonTypeClassMap DEFAULT_BSON_TYPE_CLASS_MAP = new BsonTypeClassMap(); - private final Map> map = new HashMap<>(); + private final Class[] index = new Class[BsonType.values().length]; /** * Construct an instance with the default mapping, but replacing the default mapping with any values contained in the given map. @@ -81,7 +82,7 @@ public class BsonTypeClassMap { */ public BsonTypeClassMap(final Map> replacementsForDefaults) { addDefaults(); - map.putAll(replacementsForDefaults); + replacementsForDefaults.forEach((key, value) -> index[key.ordinal()] = value); } /** @@ -92,7 +93,8 @@ public BsonTypeClassMap() { } Set keys() { - return map.keySet(); + //TODO Only return keys that have been set + return EnumSet.allOf(BsonType.class); } /** @@ -102,30 +104,30 @@ Set keys() { * @return the Class that is mapped to the BSON type */ public Class get(final BsonType bsonType) { - return map.get(bsonType); + return index[bsonType.ordinal()]; } private void addDefaults() { - map.put(BsonType.ARRAY, List.class); - map.put(BsonType.BINARY, Binary.class); - map.put(BsonType.BOOLEAN, Boolean.class); - map.put(BsonType.DATE_TIME, Date.class); - map.put(BsonType.DB_POINTER, BsonDbPointer.class); - map.put(BsonType.DOCUMENT, Document.class); - map.put(BsonType.DOUBLE, Double.class); - map.put(BsonType.INT32, Integer.class); - map.put(BsonType.INT64, Long.class); - map.put(BsonType.DECIMAL128, Decimal128.class); - map.put(BsonType.MAX_KEY, MaxKey.class); - map.put(BsonType.MIN_KEY, MinKey.class); - map.put(BsonType.JAVASCRIPT, Code.class); - map.put(BsonType.JAVASCRIPT_WITH_SCOPE, CodeWithScope.class); - map.put(BsonType.OBJECT_ID, ObjectId.class); - map.put(BsonType.REGULAR_EXPRESSION, BsonRegularExpression.class); - map.put(BsonType.STRING, String.class); - map.put(BsonType.SYMBOL, Symbol.class); - map.put(BsonType.TIMESTAMP, BsonTimestamp.class); - map.put(BsonType.UNDEFINED, BsonUndefined.class); + index[BsonType.ARRAY.ordinal()] = List.class; + index[BsonType.BINARY.ordinal()] = Binary.class; + index[BsonType.BOOLEAN.ordinal()] = Boolean.class; + index[BsonType.DATE_TIME.ordinal()] = Date.class; + index[BsonType.DB_POINTER.ordinal()] = BsonDbPointer.class; + index[BsonType.DOCUMENT.ordinal()] = Document.class; + index[BsonType.DOUBLE.ordinal()] = Double.class; + index[BsonType.INT32.ordinal()] = Integer.class; + index[BsonType.INT64.ordinal()] = Long.class; + index[BsonType.DECIMAL128.ordinal()] = Decimal128.class; + index[BsonType.MAX_KEY.ordinal()] = MaxKey.class; + index[BsonType.MIN_KEY.ordinal()] = MinKey.class; + index[BsonType.JAVASCRIPT.ordinal()] = Code.class; + index[BsonType.JAVASCRIPT_WITH_SCOPE.ordinal()] = CodeWithScope.class; + index[BsonType.OBJECT_ID.ordinal()] = ObjectId.class; + index[BsonType.REGULAR_EXPRESSION.ordinal()] = BsonRegularExpression.class; + index[BsonType.STRING.ordinal()] = String.class; + index[BsonType.SYMBOL.ordinal()] = Symbol.class; + index[BsonType.TIMESTAMP.ordinal()] = BsonTimestamp.class; + index[BsonType.UNDEFINED.ordinal()] = BsonUndefined.class; } @Override @@ -139,15 +141,11 @@ public boolean equals(final Object o) { BsonTypeClassMap that = (BsonTypeClassMap) o; - if (!map.equals(that.map)) { - return false; - } - - return true; + return Arrays.equals(index, that.index); } @Override public int hashCode() { - return map.hashCode(); + return Arrays.hashCode(index); } } diff --git a/bson/src/main/org/bson/codecs/DocumentCodec.java b/bson/src/main/org/bson/codecs/DocumentCodec.java index 3559e93fcae..0c4161f53fd 100644 --- a/bson/src/main/org/bson/codecs/DocumentCodec.java +++ b/bson/src/main/org/bson/codecs/DocumentCodec.java @@ -156,7 +156,7 @@ public void encode(final BsonWriter writer, final Document document, final Encod beforeFields(writer, encoderContext, document); - for (final Map.Entry entry : ((Map) document).entrySet()) { + for (final Map.Entry entry : document.entrySet()) { if (skipField(encoderContext, entry.getKey())) { continue; } diff --git a/driver-benchmarks/build.gradle b/driver-benchmarks/build.gradle index 91d979cff68..84e32c42e52 100644 --- a/driver-benchmarks/build.gradle +++ b/driver-benchmarks/build.gradle @@ -33,6 +33,16 @@ dependencies { api project(':driver-sync') api project(':mongodb-crypt') implementation "ch.qos.logback:logback-classic:$logbackVersion" + + implementation 'org.openjdk.jmh:jmh-core:1.37' + annotationProcessor 'org.openjdk.jmh:jmh-generator-annprocess:1.37' +} + +tasks.register("jmh", JavaExec) { + group = 'benchmark' + description = 'Run JMH benchmarks.' + mainClass = 'org.openjdk.jmh.Main' + classpath = sourceSets.main.runtimeClasspath } javadoc { diff --git a/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonDocumentBenchmark.java b/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonDocumentBenchmark.java new file mode 100644 index 00000000000..f0601423180 --- /dev/null +++ b/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonDocumentBenchmark.java @@ -0,0 +1,80 @@ +package com.mongodb.benchmark.jmh.codec; + +import com.mongodb.internal.connection.ByteBufferBsonOutput; +import com.mongodb.internal.connection.PowerOfTwoBufferPool; +import org.bson.BsonArray; +import org.bson.BsonBinaryReader; +import org.bson.BsonBinaryWriter; +import org.bson.BsonDocument; +import org.bson.BsonDouble; +import org.bson.codecs.Codec; +import org.bson.codecs.DecoderContext; +import org.bson.codecs.EncoderContext; +import org.bson.io.BasicOutputBuffer; +import org.jetbrains.annotations.NotNull; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +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 java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.concurrent.TimeUnit; + +import static com.mongodb.MongoClientSettings.getDefaultCodecRegistry; + +@BenchmarkMode(Mode.Throughput) +@Warmup(iterations = 5, time = 2, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 5, time = 2, timeUnit = TimeUnit.SECONDS) +@OutputTimeUnit(TimeUnit.SECONDS) +@Fork(0) +public class BsonDocumentBenchmark { + + @State(Scope.Benchmark) + public static class Input { + protected final PowerOfTwoBufferPool bufferPool = PowerOfTwoBufferPool.DEFAULT; + protected final Codec codec = getDefaultCodecRegistry().get(BsonDocument.class); + protected BsonDocument document; + protected byte[] documentBytes; + + @Setup + public void setup() throws IOException { + + BsonArray bsonValues = new BsonArray(); + for (int i = 0; i < 1000; i++) { + bsonValues.add(new BsonDouble(i)); + } + + document = new BsonDocument("array", bsonValues); + documentBytes = getDocumentAsBuffer(document); + } + + private byte[] getDocumentAsBuffer(final BsonDocument document) throws IOException { + BasicOutputBuffer buffer = new BasicOutputBuffer(); + codec.encode(new BsonBinaryWriter(buffer), document, EncoderContext.builder().build()); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(buffer.getSize()); + buffer.pipe(baos); + return baos.toByteArray(); + } + } + + @Benchmark + public void decode(@NotNull Input input, @NotNull Blackhole blackhole) { + blackhole.consume(input.codec.decode(new BsonBinaryReader(ByteBuffer.wrap(input.documentBytes)), DecoderContext.builder().build())); + } + + @Benchmark + public void encode(@NotNull Input input, @NotNull Blackhole blackhole) { + input.codec.encode(new BsonBinaryWriter(new ByteBufferBsonOutput(input.bufferPool)), input.document, EncoderContext.builder().build()); + blackhole.consume(input); + } +} From 51d413dc3cb5efc26025acbc208feca529dd3f25 Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Tue, 18 Feb 2025 21:21:58 -0800 Subject: [PATCH 02/15] Use BsonTypeClass map for encoding. JAVA-5339 --- bson/src/main/org/bson/codecs/BsonArrayCodec.java | 11 ++++------- bson/src/main/org/bson/codecs/BsonDocumentCodec.java | 2 +- bson/src/main/org/bson/codecs/BsonTypeClassMap.java | 7 ------- bson/src/main/org/bson/codecs/BsonTypeCodecMap.java | 2 +- .../benchmark/jmh/codec/BsonDocumentBenchmark.java | 6 +++--- 5 files changed, 9 insertions(+), 19 deletions(-) diff --git a/bson/src/main/org/bson/codecs/BsonArrayCodec.java b/bson/src/main/org/bson/codecs/BsonArrayCodec.java index ac62edfd9f0..6d16bb7d1b0 100644 --- a/bson/src/main/org/bson/codecs/BsonArrayCodec.java +++ b/bson/src/main/org/bson/codecs/BsonArrayCodec.java @@ -36,8 +36,6 @@ public class BsonArrayCodec implements Codec { private static final CodecRegistry DEFAULT_REGISTRY = fromProviders(new BsonValueCodecProvider()); private static final BsonTypeCodecMap DEFAULT_BSON_TYPE_CODEC_MAP = new BsonTypeCodecMap(getBsonTypeClassMap(), DEFAULT_REGISTRY); - - private final CodecRegistry codecRegistry; private final BsonTypeCodecMap bsonTypeCodecMap; /** @@ -46,7 +44,7 @@ public class BsonArrayCodec implements Codec { * @since 3.4 */ public BsonArrayCodec() { - this(DEFAULT_REGISTRY, DEFAULT_BSON_TYPE_CODEC_MAP); + this(DEFAULT_BSON_TYPE_CODEC_MAP); } /** @@ -55,11 +53,10 @@ public BsonArrayCodec() { * @param codecRegistry the codec registry */ public BsonArrayCodec(final CodecRegistry codecRegistry) { - this(codecRegistry, new BsonTypeCodecMap(getBsonTypeClassMap(), codecRegistry)); + this(new BsonTypeCodecMap(getBsonTypeClassMap(), codecRegistry)); } - private BsonArrayCodec(final CodecRegistry codecRegistry, final BsonTypeCodecMap bsonTypeCodecMap) { - this.codecRegistry = notNull("Codec registry", codecRegistry); + private BsonArrayCodec(final BsonTypeCodecMap bsonTypeCodecMap) { this.bsonTypeCodecMap = notNull("bsonTypeCodecMap", bsonTypeCodecMap); } @@ -80,7 +77,7 @@ public void encode(final BsonWriter writer, final BsonArray array, final Encoder writer.writeStartArray(); for (BsonValue value : array) { - Codec codec = codecRegistry.get(value.getClass()); + Codec codec = bsonTypeCodecMap.get(value.getBsonType()); encoderContext.encodeWithChildContext(codec, writer, value); } diff --git a/bson/src/main/org/bson/codecs/BsonDocumentCodec.java b/bson/src/main/org/bson/codecs/BsonDocumentCodec.java index 405fd78e117..235b0ddf429 100644 --- a/bson/src/main/org/bson/codecs/BsonDocumentCodec.java +++ b/bson/src/main/org/bson/codecs/BsonDocumentCodec.java @@ -135,7 +135,7 @@ private boolean skipField(final EncoderContext encoderContext, final String key) @SuppressWarnings({"unchecked", "rawtypes"}) private void writeValue(final BsonWriter writer, final EncoderContext encoderContext, final BsonValue value) { - Codec codec = codecRegistry.get(value.getClass()); + Codec codec = bsonTypeCodecMap.get(value.getBsonType()); encoderContext.encodeWithChildContext(codec, writer, value); } diff --git a/bson/src/main/org/bson/codecs/BsonTypeClassMap.java b/bson/src/main/org/bson/codecs/BsonTypeClassMap.java index 6e009228e19..ee1eee2e932 100644 --- a/bson/src/main/org/bson/codecs/BsonTypeClassMap.java +++ b/bson/src/main/org/bson/codecs/BsonTypeClassMap.java @@ -34,10 +34,8 @@ import java.util.Arrays; import java.util.Collections; import java.util.Date; -import java.util.EnumSet; import java.util.List; import java.util.Map; -import java.util.Set; /** *

A map from a BSON types to the Class to which it should be decoded. This class is useful if, for example, @@ -92,11 +90,6 @@ public BsonTypeClassMap() { this(Collections.emptyMap()); } - Set keys() { - //TODO Only return keys that have been set - return EnumSet.allOf(BsonType.class); - } - /** * Gets the Class that is mapped to the given BSON type. * diff --git a/bson/src/main/org/bson/codecs/BsonTypeCodecMap.java b/bson/src/main/org/bson/codecs/BsonTypeCodecMap.java index 510a6041a0b..3a3def7ca7f 100644 --- a/bson/src/main/org/bson/codecs/BsonTypeCodecMap.java +++ b/bson/src/main/org/bson/codecs/BsonTypeCodecMap.java @@ -40,7 +40,7 @@ public class BsonTypeCodecMap { public BsonTypeCodecMap(final BsonTypeClassMap bsonTypeClassMap, final CodecRegistry codecRegistry) { this.bsonTypeClassMap = notNull("bsonTypeClassMap", bsonTypeClassMap); notNull("codecRegistry", codecRegistry); - for (BsonType cur : bsonTypeClassMap.keys()) { + for (BsonType cur : BsonType.values()) { Class clazz = bsonTypeClassMap.get(cur); if (clazz != null) { try { diff --git a/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonDocumentBenchmark.java b/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonDocumentBenchmark.java index f0601423180..65bf87de057 100644 --- a/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonDocumentBenchmark.java +++ b/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonDocumentBenchmark.java @@ -32,10 +32,10 @@ import static com.mongodb.MongoClientSettings.getDefaultCodecRegistry; @BenchmarkMode(Mode.Throughput) -@Warmup(iterations = 5, time = 2, timeUnit = TimeUnit.SECONDS) -@Measurement(iterations = 5, time = 2, timeUnit = TimeUnit.SECONDS) +@Warmup(iterations = 20, time = 2, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 20, time = 2, timeUnit = TimeUnit.SECONDS) @OutputTimeUnit(TimeUnit.SECONDS) -@Fork(0) +@Fork(3) public class BsonDocumentBenchmark { @State(Scope.Benchmark) From 069f7e8a644db5f66aed9b84568112d46adb6232 Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Wed, 19 Feb 2025 15:51:02 -0800 Subject: [PATCH 03/15] Add benchmarks. JAVA-5339 --- .../org/bson/codecs/BsonDocumentCodec.java | 8 +- .../jmh/codec/BsonArrayCodecBenchmark.java | 101 ++++++++++++++++++ .../jmh/codec/BsonDocumentBenchmark.java | 43 +++++--- .../benchmark/jmh/codec/BsonUtils.java | 42 ++++++++ .../benchmark/jmh/codec/package-info.java | 27 +++++ 5 files changed, 199 insertions(+), 22 deletions(-) create mode 100644 driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonArrayCodecBenchmark.java create mode 100644 driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonUtils.java create mode 100644 driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/package-info.java diff --git a/bson/src/main/org/bson/codecs/BsonDocumentCodec.java b/bson/src/main/org/bson/codecs/BsonDocumentCodec.java index 235b0ddf429..4729b3ac233 100644 --- a/bson/src/main/org/bson/codecs/BsonDocumentCodec.java +++ b/bson/src/main/org/bson/codecs/BsonDocumentCodec.java @@ -79,17 +79,15 @@ public CodecRegistry getCodecRegistry() { @Override public BsonDocument decode(final BsonReader reader, final DecoderContext decoderContext) { - List keyValuePairs = new ArrayList<>(); - + BsonDocument bsonDocument = new BsonDocument(); reader.readStartDocument(); while (reader.readBsonType() != BsonType.END_OF_DOCUMENT) { String fieldName = reader.readName(); - keyValuePairs.add(new BsonElement(fieldName, readValue(reader, decoderContext))); + bsonDocument.append(fieldName, readValue(reader, decoderContext)); } reader.readEndDocument(); - - return new BsonDocument(keyValuePairs); + return bsonDocument; } /** diff --git a/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonArrayCodecBenchmark.java b/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonArrayCodecBenchmark.java new file mode 100644 index 00000000000..060b78e15b7 --- /dev/null +++ b/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonArrayCodecBenchmark.java @@ -0,0 +1,101 @@ +/* + * Copyright 2016-present MongoDB, Inc. + * + * Licensed 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 com.mongodb.benchmark.jmh.codec; + +import com.mongodb.internal.connection.ByteBufferBsonOutput; +import com.mongodb.internal.connection.PowerOfTwoBufferPool; +import org.bson.BsonArray; +import org.bson.BsonBinaryReader; +import org.bson.BsonBinaryWriter; +import org.bson.BsonDocument; +import org.bson.BsonDouble; +import org.bson.codecs.Codec; +import org.bson.codecs.DecoderContext; +import org.bson.codecs.EncoderContext; +import org.jetbrains.annotations.NotNull; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +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 java.io.IOException; +import java.nio.ByteBuffer; +import java.util.concurrent.TimeUnit; + +import static com.mongodb.MongoClientSettings.getDefaultCodecRegistry; +import static com.mongodb.benchmark.jmh.codec.BsonUtils.getDocumentAsBuffer; + +@BenchmarkMode(Mode.Throughput) +@Warmup(iterations = 20, time = 2, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 20, time = 2, timeUnit = TimeUnit.SECONDS) +@OutputTimeUnit(TimeUnit.SECONDS) +@Fork(3) +public class BsonArrayCodecBenchmark { + + @State(Scope.Benchmark) + public static class Input { + protected final PowerOfTwoBufferPool bufferPool = PowerOfTwoBufferPool.DEFAULT; + protected final Codec bsonArrayCodec = getDefaultCodecRegistry().get(BsonArray.class); + protected BsonDocument document; + protected byte[] documentBytes; + private BsonBinaryReader reader; + private BsonBinaryWriter writer; + private BsonArray bsonValues; + + @Setup + public void setup() throws IOException { + bsonValues = new BsonArray(); + document = new BsonDocument("array", bsonValues); + + for (int i = 0; i < 1000; i++) { + bsonValues.add(new BsonDouble(i)); + } + + documentBytes = getDocumentAsBuffer(document); + } + + @Setup(Level.Invocation) + public void beforeIteration() { + reader = new BsonBinaryReader(ByteBuffer.wrap(documentBytes)); + writer = new BsonBinaryWriter(new ByteBufferBsonOutput(bufferPool)); + + reader.readStartDocument(); + writer.writeStartDocument(); + writer.writeName("array"); + } + } + + @Benchmark + public void decode(@NotNull Input input, @NotNull Blackhole blackhole) { + blackhole.consume(input.bsonArrayCodec.decode(input.reader, DecoderContext.builder().build())); + } + + @Benchmark + public void encode(@NotNull Input input, @NotNull Blackhole blackhole) { + input.bsonArrayCodec.encode(input.writer, input.bsonValues, EncoderContext.builder().build()); + blackhole.consume(input); + } +} diff --git a/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonDocumentBenchmark.java b/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonDocumentBenchmark.java index 65bf87de057..c191b08ee83 100644 --- a/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonDocumentBenchmark.java +++ b/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonDocumentBenchmark.java @@ -1,20 +1,36 @@ +/* + * Copyright 2016-present MongoDB, Inc. + * + * Licensed 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 com.mongodb.benchmark.jmh.codec; import com.mongodb.internal.connection.ByteBufferBsonOutput; import com.mongodb.internal.connection.PowerOfTwoBufferPool; -import org.bson.BsonArray; import org.bson.BsonBinaryReader; import org.bson.BsonBinaryWriter; import org.bson.BsonDocument; -import org.bson.BsonDouble; +import org.bson.BsonInt32; import org.bson.codecs.Codec; import org.bson.codecs.DecoderContext; import org.bson.codecs.EncoderContext; -import org.bson.io.BasicOutputBuffer; import org.jetbrains.annotations.NotNull; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Level; import org.openjdk.jmh.annotations.Measurement; import org.openjdk.jmh.annotations.Mode; import org.openjdk.jmh.annotations.OutputTimeUnit; @@ -24,13 +40,16 @@ import org.openjdk.jmh.annotations.Warmup; import org.openjdk.jmh.infra.Blackhole; -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.util.concurrent.TimeUnit; import static com.mongodb.MongoClientSettings.getDefaultCodecRegistry; +import static com.mongodb.benchmark.jmh.codec.BsonUtils.getDocumentAsBuffer; +/** + * Benchmark with minimal dependency on other codecs to evaluate BsonDocumentCodec's internal performance. + */ @BenchmarkMode(Mode.Throughput) @Warmup(iterations = 20, time = 2, timeUnit = TimeUnit.SECONDS) @Measurement(iterations = 20, time = 2, timeUnit = TimeUnit.SECONDS) @@ -47,24 +66,14 @@ public static class Input { @Setup public void setup() throws IOException { + document = new BsonDocument(); - BsonArray bsonValues = new BsonArray(); - for (int i = 0; i < 1000; i++) { - bsonValues.add(new BsonDouble(i)); + for (int i = 0; i < 500; i++) { + document.append(Integer.toString(i), new BsonInt32(i)); } - document = new BsonDocument("array", bsonValues); documentBytes = getDocumentAsBuffer(document); } - - private byte[] getDocumentAsBuffer(final BsonDocument document) throws IOException { - BasicOutputBuffer buffer = new BasicOutputBuffer(); - codec.encode(new BsonBinaryWriter(buffer), document, EncoderContext.builder().build()); - - ByteArrayOutputStream baos = new ByteArrayOutputStream(buffer.getSize()); - buffer.pipe(baos); - return baos.toByteArray(); - } } @Benchmark diff --git a/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonUtils.java b/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonUtils.java new file mode 100644 index 00000000000..6530499e1fe --- /dev/null +++ b/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonUtils.java @@ -0,0 +1,42 @@ +/* + * Copyright 2016-present MongoDB, Inc. + * + * Licensed 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 com.mongodb.benchmark.jmh.codec; + +import org.bson.BsonBinaryWriter; +import org.bson.BsonDocument; +import org.bson.codecs.Codec; +import org.bson.codecs.EncoderContext; +import org.bson.io.BasicOutputBuffer; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import static com.mongodb.MongoClientSettings.getDefaultCodecRegistry; + +public class BsonUtils { + static final Codec BSON_DOCUMENT_CODEC = getDefaultCodecRegistry().get(BsonDocument.class); + + public static byte[] getDocumentAsBuffer(final BsonDocument document) throws IOException { + BasicOutputBuffer buffer = new BasicOutputBuffer(); + BSON_DOCUMENT_CODEC.encode(new BsonBinaryWriter(buffer), document, EncoderContext.builder().build()); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(buffer.getSize()); + buffer.pipe(baos); + return baos.toByteArray(); + } +} diff --git a/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/package-info.java b/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/package-info.java new file mode 100644 index 00000000000..1f66f559b38 --- /dev/null +++ b/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/package-info.java @@ -0,0 +1,27 @@ +/* + * Copyright 2016-present MongoDB, Inc. + * + * Licensed 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. + * + */ + +/** + * Contains JMH benchmarks for targeted components and code paths. + * + *

When changes are made, the existing benchmarks can be quickly executed to assess + * any performance impact. These benchmarks are intended for targeted evaluation in a local environment + * and are not currently executed on the Evergreen. If a benchmark for a particular code path or component does not yet + * exist, this package provides a convenient location to set up a new one + * for performance testing.

+ */ +package com.mongodb.benchmark.jmh.codec; \ No newline at end of file From 84597f219fbb42eec5cf52a067965b3d039ec159 Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Wed, 19 Feb 2025 16:40:55 -0800 Subject: [PATCH 04/15] Rename field. JAVA-5339 --- .../org/bson/codecs/BsonTypeClassMap.java | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/bson/src/main/org/bson/codecs/BsonTypeClassMap.java b/bson/src/main/org/bson/codecs/BsonTypeClassMap.java index ee1eee2e932..ba810b7b375 100644 --- a/bson/src/main/org/bson/codecs/BsonTypeClassMap.java +++ b/bson/src/main/org/bson/codecs/BsonTypeClassMap.java @@ -70,7 +70,7 @@ */ public class BsonTypeClassMap { static final BsonTypeClassMap DEFAULT_BSON_TYPE_CLASS_MAP = new BsonTypeClassMap(); - private final Class[] index = new Class[BsonType.values().length]; + private final Class[] bsonTypeOrdinalToClassMap = new Class[BsonType.values().length]; /** * Construct an instance with the default mapping, but replacing the default mapping with any values contained in the given map. @@ -80,7 +80,7 @@ public class BsonTypeClassMap { */ public BsonTypeClassMap(final Map> replacementsForDefaults) { addDefaults(); - replacementsForDefaults.forEach((key, value) -> index[key.ordinal()] = value); + replacementsForDefaults.forEach((key, value) -> bsonTypeOrdinalToClassMap[key.ordinal()] = value); } /** @@ -97,30 +97,30 @@ public BsonTypeClassMap() { * @return the Class that is mapped to the BSON type */ public Class get(final BsonType bsonType) { - return index[bsonType.ordinal()]; + return bsonTypeOrdinalToClassMap[bsonType.ordinal()]; } private void addDefaults() { - index[BsonType.ARRAY.ordinal()] = List.class; - index[BsonType.BINARY.ordinal()] = Binary.class; - index[BsonType.BOOLEAN.ordinal()] = Boolean.class; - index[BsonType.DATE_TIME.ordinal()] = Date.class; - index[BsonType.DB_POINTER.ordinal()] = BsonDbPointer.class; - index[BsonType.DOCUMENT.ordinal()] = Document.class; - index[BsonType.DOUBLE.ordinal()] = Double.class; - index[BsonType.INT32.ordinal()] = Integer.class; - index[BsonType.INT64.ordinal()] = Long.class; - index[BsonType.DECIMAL128.ordinal()] = Decimal128.class; - index[BsonType.MAX_KEY.ordinal()] = MaxKey.class; - index[BsonType.MIN_KEY.ordinal()] = MinKey.class; - index[BsonType.JAVASCRIPT.ordinal()] = Code.class; - index[BsonType.JAVASCRIPT_WITH_SCOPE.ordinal()] = CodeWithScope.class; - index[BsonType.OBJECT_ID.ordinal()] = ObjectId.class; - index[BsonType.REGULAR_EXPRESSION.ordinal()] = BsonRegularExpression.class; - index[BsonType.STRING.ordinal()] = String.class; - index[BsonType.SYMBOL.ordinal()] = Symbol.class; - index[BsonType.TIMESTAMP.ordinal()] = BsonTimestamp.class; - index[BsonType.UNDEFINED.ordinal()] = BsonUndefined.class; + bsonTypeOrdinalToClassMap[BsonType.ARRAY.ordinal()] = List.class; + bsonTypeOrdinalToClassMap[BsonType.BINARY.ordinal()] = Binary.class; + bsonTypeOrdinalToClassMap[BsonType.BOOLEAN.ordinal()] = Boolean.class; + bsonTypeOrdinalToClassMap[BsonType.DATE_TIME.ordinal()] = Date.class; + bsonTypeOrdinalToClassMap[BsonType.DB_POINTER.ordinal()] = BsonDbPointer.class; + bsonTypeOrdinalToClassMap[BsonType.DOCUMENT.ordinal()] = Document.class; + bsonTypeOrdinalToClassMap[BsonType.DOUBLE.ordinal()] = Double.class; + bsonTypeOrdinalToClassMap[BsonType.INT32.ordinal()] = Integer.class; + bsonTypeOrdinalToClassMap[BsonType.INT64.ordinal()] = Long.class; + bsonTypeOrdinalToClassMap[BsonType.DECIMAL128.ordinal()] = Decimal128.class; + bsonTypeOrdinalToClassMap[BsonType.MAX_KEY.ordinal()] = MaxKey.class; + bsonTypeOrdinalToClassMap[BsonType.MIN_KEY.ordinal()] = MinKey.class; + bsonTypeOrdinalToClassMap[BsonType.JAVASCRIPT.ordinal()] = Code.class; + bsonTypeOrdinalToClassMap[BsonType.JAVASCRIPT_WITH_SCOPE.ordinal()] = CodeWithScope.class; + bsonTypeOrdinalToClassMap[BsonType.OBJECT_ID.ordinal()] = ObjectId.class; + bsonTypeOrdinalToClassMap[BsonType.REGULAR_EXPRESSION.ordinal()] = BsonRegularExpression.class; + bsonTypeOrdinalToClassMap[BsonType.STRING.ordinal()] = String.class; + bsonTypeOrdinalToClassMap[BsonType.SYMBOL.ordinal()] = Symbol.class; + bsonTypeOrdinalToClassMap[BsonType.TIMESTAMP.ordinal()] = BsonTimestamp.class; + bsonTypeOrdinalToClassMap[BsonType.UNDEFINED.ordinal()] = BsonUndefined.class; } @Override @@ -134,11 +134,11 @@ public boolean equals(final Object o) { BsonTypeClassMap that = (BsonTypeClassMap) o; - return Arrays.equals(index, that.index); + return Arrays.equals(bsonTypeOrdinalToClassMap, that.bsonTypeOrdinalToClassMap); } @Override public int hashCode() { - return Arrays.hashCode(index); + return Arrays.hashCode(bsonTypeOrdinalToClassMap); } } From 82cbaf2b1597f7809a6fbd4d9bec189aea52b567 Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Wed, 19 Feb 2025 18:01:14 -0800 Subject: [PATCH 05/15] Remove unused imports. --- bson/src/main/org/bson/codecs/BsonDocumentCodec.java | 3 --- .../org/bson/codecs/IterableCodecProviderSpecification.groovy | 3 ++- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/bson/src/main/org/bson/codecs/BsonDocumentCodec.java b/bson/src/main/org/bson/codecs/BsonDocumentCodec.java index 4729b3ac233..75bd3b7a2b0 100644 --- a/bson/src/main/org/bson/codecs/BsonDocumentCodec.java +++ b/bson/src/main/org/bson/codecs/BsonDocumentCodec.java @@ -17,7 +17,6 @@ package org.bson.codecs; import org.bson.BsonDocument; -import org.bson.BsonElement; import org.bson.BsonObjectId; import org.bson.BsonReader; import org.bson.BsonType; @@ -26,8 +25,6 @@ import org.bson.codecs.configuration.CodecRegistry; import org.bson.types.ObjectId; -import java.util.ArrayList; -import java.util.List; import java.util.Map; import static org.bson.assertions.Assertions.notNull; diff --git a/bson/src/test/unit/org/bson/codecs/IterableCodecProviderSpecification.groovy b/bson/src/test/unit/org/bson/codecs/IterableCodecProviderSpecification.groovy index b0eae796fc4..b5217676871 100644 --- a/bson/src/test/unit/org/bson/codecs/IterableCodecProviderSpecification.groovy +++ b/bson/src/test/unit/org/bson/codecs/IterableCodecProviderSpecification.groovy @@ -16,6 +16,7 @@ package org.bson.codecs +import org.bson.BsonType import spock.lang.Specification import static org.bson.codecs.configuration.CodecRegistries.fromProviders @@ -57,7 +58,7 @@ class IterableCodecProviderSpecification extends Specification { def 'unidentical instances should not be equal'() { given: def first = new IterableCodecProvider() - def second = new IterableCodecProvider(new BsonTypeClassMap([BOOLEAN: String])) + def second = new IterableCodecProvider(new BsonTypeClassMap([(BsonType.BOOLEAN): String])) def third = new IterableCodecProvider(new BsonTypeClassMap(), { Object from -> from }) From 413672594c1654c93e501331a5ed75f7e92d2e23 Mon Sep 17 00:00:00 2001 From: Viacheslav Babanin Date: Wed, 19 Feb 2025 19:19:01 -0800 Subject: [PATCH 06/15] Update package-info.java --- .../main/com/mongodb/benchmark/jmh/codec/package-info.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/package-info.java b/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/package-info.java index 1f66f559b38..4c2731a218f 100644 --- a/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/package-info.java +++ b/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/package-info.java @@ -19,9 +19,9 @@ * Contains JMH benchmarks for targeted components and code paths. * *

When changes are made, the existing benchmarks can be quickly executed to assess - * any performance impact. These benchmarks are intended for targeted evaluation in a local environment + * any performance impact. These benchmarks are intended for targeted evaluation in a local environment or spawn host * and are not currently executed on the Evergreen. If a benchmark for a particular code path or component does not yet * exist, this package provides a convenient location to set up a new one * for performance testing.

*/ -package com.mongodb.benchmark.jmh.codec; \ No newline at end of file +package com.mongodb.benchmark.jmh.codec; From 56acb809b539b976b940579184951aa29ec99ee8 Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Wed, 26 Feb 2025 23:16:43 -0800 Subject: [PATCH 07/15] Switch to getValue()-based mapping in BsonTypeClassMap Using ordinal() ties the mapping to enum declaration order, which risks subtle bugs in the future. JAVA-5339 --- .../org/bson/codecs/BsonTypeClassMap.java | 46 +++++++++---------- .../jmh/codec/BsonArrayCodecBenchmark.java | 4 +- .../jmh/codec/BsonDocumentBenchmark.java | 9 ++-- 3 files changed, 29 insertions(+), 30 deletions(-) diff --git a/bson/src/main/org/bson/codecs/BsonTypeClassMap.java b/bson/src/main/org/bson/codecs/BsonTypeClassMap.java index ba810b7b375..32acaeb7f85 100644 --- a/bson/src/main/org/bson/codecs/BsonTypeClassMap.java +++ b/bson/src/main/org/bson/codecs/BsonTypeClassMap.java @@ -70,7 +70,7 @@ */ public class BsonTypeClassMap { static final BsonTypeClassMap DEFAULT_BSON_TYPE_CLASS_MAP = new BsonTypeClassMap(); - private final Class[] bsonTypeOrdinalToClassMap = new Class[BsonType.values().length]; + private final Class[] bsonTypeOrdinalToClassMap = new Class[256]; /** * Construct an instance with the default mapping, but replacing the default mapping with any values contained in the given map. @@ -80,7 +80,7 @@ public class BsonTypeClassMap { */ public BsonTypeClassMap(final Map> replacementsForDefaults) { addDefaults(); - replacementsForDefaults.forEach((key, value) -> bsonTypeOrdinalToClassMap[key.ordinal()] = value); + replacementsForDefaults.forEach((key, value) -> bsonTypeOrdinalToClassMap[key.getValue()] = value); } /** @@ -97,30 +97,30 @@ public BsonTypeClassMap() { * @return the Class that is mapped to the BSON type */ public Class get(final BsonType bsonType) { - return bsonTypeOrdinalToClassMap[bsonType.ordinal()]; + return bsonTypeOrdinalToClassMap[bsonType.getValue()]; } private void addDefaults() { - bsonTypeOrdinalToClassMap[BsonType.ARRAY.ordinal()] = List.class; - bsonTypeOrdinalToClassMap[BsonType.BINARY.ordinal()] = Binary.class; - bsonTypeOrdinalToClassMap[BsonType.BOOLEAN.ordinal()] = Boolean.class; - bsonTypeOrdinalToClassMap[BsonType.DATE_TIME.ordinal()] = Date.class; - bsonTypeOrdinalToClassMap[BsonType.DB_POINTER.ordinal()] = BsonDbPointer.class; - bsonTypeOrdinalToClassMap[BsonType.DOCUMENT.ordinal()] = Document.class; - bsonTypeOrdinalToClassMap[BsonType.DOUBLE.ordinal()] = Double.class; - bsonTypeOrdinalToClassMap[BsonType.INT32.ordinal()] = Integer.class; - bsonTypeOrdinalToClassMap[BsonType.INT64.ordinal()] = Long.class; - bsonTypeOrdinalToClassMap[BsonType.DECIMAL128.ordinal()] = Decimal128.class; - bsonTypeOrdinalToClassMap[BsonType.MAX_KEY.ordinal()] = MaxKey.class; - bsonTypeOrdinalToClassMap[BsonType.MIN_KEY.ordinal()] = MinKey.class; - bsonTypeOrdinalToClassMap[BsonType.JAVASCRIPT.ordinal()] = Code.class; - bsonTypeOrdinalToClassMap[BsonType.JAVASCRIPT_WITH_SCOPE.ordinal()] = CodeWithScope.class; - bsonTypeOrdinalToClassMap[BsonType.OBJECT_ID.ordinal()] = ObjectId.class; - bsonTypeOrdinalToClassMap[BsonType.REGULAR_EXPRESSION.ordinal()] = BsonRegularExpression.class; - bsonTypeOrdinalToClassMap[BsonType.STRING.ordinal()] = String.class; - bsonTypeOrdinalToClassMap[BsonType.SYMBOL.ordinal()] = Symbol.class; - bsonTypeOrdinalToClassMap[BsonType.TIMESTAMP.ordinal()] = BsonTimestamp.class; - bsonTypeOrdinalToClassMap[BsonType.UNDEFINED.ordinal()] = BsonUndefined.class; + bsonTypeOrdinalToClassMap[BsonType.ARRAY.getValue()] = List.class; + bsonTypeOrdinalToClassMap[BsonType.BINARY.getValue()] = Binary.class; + bsonTypeOrdinalToClassMap[BsonType.BOOLEAN.getValue()] = Boolean.class; + bsonTypeOrdinalToClassMap[BsonType.DATE_TIME.getValue()] = Date.class; + bsonTypeOrdinalToClassMap[BsonType.DB_POINTER.getValue()] = BsonDbPointer.class; + bsonTypeOrdinalToClassMap[BsonType.DOCUMENT.getValue()] = Document.class; + bsonTypeOrdinalToClassMap[BsonType.DOUBLE.getValue()] = Double.class; + bsonTypeOrdinalToClassMap[BsonType.INT32.getValue()] = Integer.class; + bsonTypeOrdinalToClassMap[BsonType.INT64.getValue()] = Long.class; + bsonTypeOrdinalToClassMap[BsonType.DECIMAL128.getValue()] = Decimal128.class; + bsonTypeOrdinalToClassMap[BsonType.MAX_KEY.getValue()] = MaxKey.class; + bsonTypeOrdinalToClassMap[BsonType.MIN_KEY.getValue()] = MinKey.class; + bsonTypeOrdinalToClassMap[BsonType.JAVASCRIPT.getValue()] = Code.class; + bsonTypeOrdinalToClassMap[BsonType.JAVASCRIPT_WITH_SCOPE.getValue()] = CodeWithScope.class; + bsonTypeOrdinalToClassMap[BsonType.OBJECT_ID.getValue()] = ObjectId.class; + bsonTypeOrdinalToClassMap[BsonType.REGULAR_EXPRESSION.getValue()] = BsonRegularExpression.class; + bsonTypeOrdinalToClassMap[BsonType.STRING.getValue()] = String.class; + bsonTypeOrdinalToClassMap[BsonType.SYMBOL.getValue()] = Symbol.class; + bsonTypeOrdinalToClassMap[BsonType.TIMESTAMP.getValue()] = BsonTimestamp.class; + bsonTypeOrdinalToClassMap[BsonType.UNDEFINED.getValue()] = BsonUndefined.class; } @Override diff --git a/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonArrayCodecBenchmark.java b/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonArrayCodecBenchmark.java index 060b78e15b7..2908982ca0c 100644 --- a/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonArrayCodecBenchmark.java +++ b/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonArrayCodecBenchmark.java @@ -24,7 +24,7 @@ import org.bson.BsonBinaryWriter; import org.bson.BsonDocument; import org.bson.BsonDouble; -import org.bson.codecs.Codec; +import org.bson.codecs.BsonArrayCodec; import org.bson.codecs.DecoderContext; import org.bson.codecs.EncoderContext; import org.jetbrains.annotations.NotNull; @@ -58,7 +58,7 @@ public class BsonArrayCodecBenchmark { @State(Scope.Benchmark) public static class Input { protected final PowerOfTwoBufferPool bufferPool = PowerOfTwoBufferPool.DEFAULT; - protected final Codec bsonArrayCodec = getDefaultCodecRegistry().get(BsonArray.class); + protected final BsonArrayCodec bsonArrayCodec = new BsonArrayCodec(getDefaultCodecRegistry()); protected BsonDocument document; protected byte[] documentBytes; private BsonBinaryReader reader; diff --git a/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonDocumentBenchmark.java b/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonDocumentBenchmark.java index c191b08ee83..24b4a79e5a4 100644 --- a/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonDocumentBenchmark.java +++ b/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonDocumentBenchmark.java @@ -23,14 +23,13 @@ import org.bson.BsonBinaryWriter; import org.bson.BsonDocument; import org.bson.BsonInt32; -import org.bson.codecs.Codec; +import org.bson.codecs.BsonDocumentCodec; import org.bson.codecs.DecoderContext; import org.bson.codecs.EncoderContext; import org.jetbrains.annotations.NotNull; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; -import org.openjdk.jmh.annotations.Level; import org.openjdk.jmh.annotations.Measurement; import org.openjdk.jmh.annotations.Mode; import org.openjdk.jmh.annotations.OutputTimeUnit; @@ -60,7 +59,7 @@ public class BsonDocumentBenchmark { @State(Scope.Benchmark) public static class Input { protected final PowerOfTwoBufferPool bufferPool = PowerOfTwoBufferPool.DEFAULT; - protected final Codec codec = getDefaultCodecRegistry().get(BsonDocument.class); + protected final BsonDocumentCodec bsonDocumentCodec = new BsonDocumentCodec(getDefaultCodecRegistry()); protected BsonDocument document; protected byte[] documentBytes; @@ -78,12 +77,12 @@ public void setup() throws IOException { @Benchmark public void decode(@NotNull Input input, @NotNull Blackhole blackhole) { - blackhole.consume(input.codec.decode(new BsonBinaryReader(ByteBuffer.wrap(input.documentBytes)), DecoderContext.builder().build())); + blackhole.consume(input.bsonDocumentCodec.decode(new BsonBinaryReader(ByteBuffer.wrap(input.documentBytes)), DecoderContext.builder().build())); } @Benchmark public void encode(@NotNull Input input, @NotNull Blackhole blackhole) { - input.codec.encode(new BsonBinaryWriter(new ByteBufferBsonOutput(input.bufferPool)), input.document, EncoderContext.builder().build()); + input.bsonDocumentCodec.encode(new BsonBinaryWriter(new ByteBufferBsonOutput(input.bufferPool)), input.document, EncoderContext.builder().build()); blackhole.consume(input); } } From dfce11bb1657f79098e96900a6e5350eb7a1b6df Mon Sep 17 00:00:00 2001 From: Viacheslav Babanin Date: Tue, 4 Mar 2025 00:00:39 -0800 Subject: [PATCH 08/15] Update driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonArrayCodecBenchmark.java Co-authored-by: Ross Lawley --- .../mongodb/benchmark/jmh/codec/BsonArrayCodecBenchmark.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonArrayCodecBenchmark.java b/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonArrayCodecBenchmark.java index 2908982ca0c..46cb6f18dca 100644 --- a/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonArrayCodecBenchmark.java +++ b/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonArrayCodecBenchmark.java @@ -58,7 +58,7 @@ public class BsonArrayCodecBenchmark { @State(Scope.Benchmark) public static class Input { protected final PowerOfTwoBufferPool bufferPool = PowerOfTwoBufferPool.DEFAULT; - protected final BsonArrayCodec bsonArrayCodec = new BsonArrayCodec(getDefaultCodecRegistry()); + protected final BsonArrayCodec bsonArrayCodec = new BsonArrayCodec(); protected BsonDocument document; protected byte[] documentBytes; private BsonBinaryReader reader; From a03d11e72dbe4d00b270bf3cdd3124d2673a6ee9 Mon Sep 17 00:00:00 2001 From: Viacheslav Babanin Date: Tue, 4 Mar 2025 00:00:52 -0800 Subject: [PATCH 09/15] Update driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonDocumentBenchmark.java Co-authored-by: Ross Lawley --- .../com/mongodb/benchmark/jmh/codec/BsonDocumentBenchmark.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonDocumentBenchmark.java b/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonDocumentBenchmark.java index 24b4a79e5a4..9e8f3e6e8aa 100644 --- a/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonDocumentBenchmark.java +++ b/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonDocumentBenchmark.java @@ -59,7 +59,7 @@ public class BsonDocumentBenchmark { @State(Scope.Benchmark) public static class Input { protected final PowerOfTwoBufferPool bufferPool = PowerOfTwoBufferPool.DEFAULT; - protected final BsonDocumentCodec bsonDocumentCodec = new BsonDocumentCodec(getDefaultCodecRegistry()); + protected final BsonDocumentCodec bsonDocumentCodec = new BsonDocumentCodec(); protected BsonDocument document; protected byte[] documentBytes; From eb8c355508c15468b9e928254baf2d28ac82a4c7 Mon Sep 17 00:00:00 2001 From: Viacheslav Babanin Date: Tue, 4 Mar 2025 00:00:59 -0800 Subject: [PATCH 10/15] Update driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonDocumentBenchmark.java Co-authored-by: Ross Lawley --- .../com/mongodb/benchmark/jmh/codec/BsonDocumentBenchmark.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonDocumentBenchmark.java b/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonDocumentBenchmark.java index 9e8f3e6e8aa..22739c511b8 100644 --- a/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonDocumentBenchmark.java +++ b/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonDocumentBenchmark.java @@ -42,8 +42,6 @@ import java.io.IOException; import java.nio.ByteBuffer; import java.util.concurrent.TimeUnit; - -import static com.mongodb.MongoClientSettings.getDefaultCodecRegistry; import static com.mongodb.benchmark.jmh.codec.BsonUtils.getDocumentAsBuffer; /** From ae1907944f85454ffb0cbbe92408d22773130068 Mon Sep 17 00:00:00 2001 From: Viacheslav Babanin Date: Tue, 4 Mar 2025 00:01:11 -0800 Subject: [PATCH 11/15] Update driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonArrayCodecBenchmark.java Co-authored-by: Ross Lawley --- .../mongodb/benchmark/jmh/codec/BsonArrayCodecBenchmark.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonArrayCodecBenchmark.java b/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonArrayCodecBenchmark.java index 46cb6f18dca..19df4bd8269 100644 --- a/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonArrayCodecBenchmark.java +++ b/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonArrayCodecBenchmark.java @@ -44,8 +44,6 @@ import java.io.IOException; import java.nio.ByteBuffer; import java.util.concurrent.TimeUnit; - -import static com.mongodb.MongoClientSettings.getDefaultCodecRegistry; import static com.mongodb.benchmark.jmh.codec.BsonUtils.getDocumentAsBuffer; @BenchmarkMode(Mode.Throughput) From b65f6ad76d8141c4da8c54366e8c0fd1fe7c6e06 Mon Sep 17 00:00:00 2001 From: Viacheslav Babanin Date: Tue, 4 Mar 2025 00:01:29 -0800 Subject: [PATCH 12/15] Update driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonUtils.java Co-authored-by: Ross Lawley --- .../src/main/com/mongodb/benchmark/jmh/codec/BsonUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonUtils.java b/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonUtils.java index 6530499e1fe..f4f4705634b 100644 --- a/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonUtils.java +++ b/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonUtils.java @@ -29,7 +29,7 @@ import static com.mongodb.MongoClientSettings.getDefaultCodecRegistry; public class BsonUtils { - static final Codec BSON_DOCUMENT_CODEC = getDefaultCodecRegistry().get(BsonDocument.class); + static final Codec BSON_DOCUMENT_CODEC = BsonDocumentCodec(); public static byte[] getDocumentAsBuffer(final BsonDocument document) throws IOException { BasicOutputBuffer buffer = new BasicOutputBuffer(); From 4ff5da4914972a1c4186b4aef1ecb0021032bace Mon Sep 17 00:00:00 2001 From: Viacheslav Babanin Date: Tue, 4 Mar 2025 00:01:35 -0800 Subject: [PATCH 13/15] Update driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonUtils.java Co-authored-by: Ross Lawley --- .../src/main/com/mongodb/benchmark/jmh/codec/BsonUtils.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonUtils.java b/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonUtils.java index f4f4705634b..97b580e5288 100644 --- a/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonUtils.java +++ b/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonUtils.java @@ -26,8 +26,6 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; -import static com.mongodb.MongoClientSettings.getDefaultCodecRegistry; - public class BsonUtils { static final Codec BSON_DOCUMENT_CODEC = BsonDocumentCodec(); From 2f4a370b727a4f8f841659ffdd066b9cf9fa07bf Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Tue, 4 Mar 2025 00:08:02 -0800 Subject: [PATCH 14/15] Remove redundant imports JAVA-5339 --- .../main/com/mongodb/benchmark/jmh/codec/BsonUtils.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonUtils.java b/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonUtils.java index 97b580e5288..a21198152b2 100644 --- a/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonUtils.java +++ b/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonUtils.java @@ -19,6 +19,7 @@ import org.bson.BsonBinaryWriter; import org.bson.BsonDocument; +import org.bson.codecs.BsonDocumentCodec; import org.bson.codecs.Codec; import org.bson.codecs.EncoderContext; import org.bson.io.BasicOutputBuffer; @@ -27,7 +28,12 @@ import java.io.IOException; public class BsonUtils { - static final Codec BSON_DOCUMENT_CODEC = BsonDocumentCodec(); + + private BsonUtils(){ + //NOP + } + + static final Codec BSON_DOCUMENT_CODEC = new BsonDocumentCodec(); public static byte[] getDocumentAsBuffer(final BsonDocument document) throws IOException { BasicOutputBuffer buffer = new BasicOutputBuffer(); From 626531b433c3e86bdf5d6b30ad541c7e1df9f96c Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Tue, 4 Mar 2025 00:10:25 -0800 Subject: [PATCH 15/15] Move static field up. JAVA-5339 --- .../src/main/com/mongodb/benchmark/jmh/codec/BsonUtils.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonUtils.java b/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonUtils.java index a21198152b2..58ad034788b 100644 --- a/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonUtils.java +++ b/driver-benchmarks/src/main/com/mongodb/benchmark/jmh/codec/BsonUtils.java @@ -29,12 +29,12 @@ public class BsonUtils { + private static final Codec BSON_DOCUMENT_CODEC = new BsonDocumentCodec(); + private BsonUtils(){ //NOP } - static final Codec BSON_DOCUMENT_CODEC = new BsonDocumentCodec(); - public static byte[] getDocumentAsBuffer(final BsonDocument document) throws IOException { BasicOutputBuffer buffer = new BasicOutputBuffer(); BSON_DOCUMENT_CODEC.encode(new BsonBinaryWriter(buffer), document, EncoderContext.builder().build());