From a167fbceed413af760d3ffc4cb5ce88ed1798948 Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Wed, 2 Apr 2025 17:09:39 -0700 Subject: [PATCH 1/3] Cache String encoded bytes to reduce GC allocation rate and CPU time. JAVA-5836 --- bson/src/main/org/bson/BsonBinaryWriter.java | 31 ++++++++++++++++--- .../unit/org/bson/BsonBinaryWriterTest.java | 2 +- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/bson/src/main/org/bson/BsonBinaryWriter.java b/bson/src/main/org/bson/BsonBinaryWriter.java index dd0a3e1ffa..896812233c 100644 --- a/bson/src/main/org/bson/BsonBinaryWriter.java +++ b/bson/src/main/org/bson/BsonBinaryWriter.java @@ -37,13 +37,34 @@ public class BsonBinaryWriter extends AbstractBsonWriter { private final BsonOutput bsonOutput; private final Stack maxDocumentSizeStack = new Stack<>(); - private static final int ARRAY_INDEXES_CACHE_SIZE = 256; - private static final String[] ARRAY_INDEXES_CACHE = new String[ARRAY_INDEXES_CACHE_SIZE]; + private static final int ARRAY_INDEXES_CACHE_SIZE = 1000; + private static final byte[] ARRAY_INDEXES_BUFFER; + private static final int[] ARRAY_INDEXES_OFFSETS; + private static final int[] ARRAY_INDEXES_LENGTHS; private Mark mark; static { + int totalSize = 0; + int[] lengths = new int[ARRAY_INDEXES_CACHE_SIZE]; for (int i = 0; i < ARRAY_INDEXES_CACHE_SIZE; i++) { - ARRAY_INDEXES_CACHE[i] = Integer.toString(i); + String string = Integer.toString(i); + lengths[i] = string.length() + 1; // +1 for null terminator + totalSize += lengths[i]; + } + + ARRAY_INDEXES_BUFFER = new byte[totalSize]; + ARRAY_INDEXES_OFFSETS = new int[ARRAY_INDEXES_CACHE_SIZE]; + ARRAY_INDEXES_LENGTHS = lengths; + + // Fill buffer + int offset = 0; + for (int i = 0; i < ARRAY_INDEXES_CACHE_SIZE; i++) { + ARRAY_INDEXES_OFFSETS[i] = offset; + String string = Integer.toString(i); + for (int j = 0; j < string.length(); j++) { + ARRAY_INDEXES_BUFFER[offset++] = (byte) string.charAt(j); + } + ARRAY_INDEXES_BUFFER[offset++] = 0; } } @@ -409,7 +430,9 @@ private void writeCurrentName() { if (index >= ARRAY_INDEXES_CACHE_SIZE) { bsonOutput.writeCString(Integer.toString(index)); } else { - bsonOutput.writeCString(ARRAY_INDEXES_CACHE[index]); + bsonOutput.writeBytes(ARRAY_INDEXES_BUFFER, + ARRAY_INDEXES_OFFSETS[index], + ARRAY_INDEXES_LENGTHS[index]); } } else { bsonOutput.writeCString(getName()); diff --git a/bson/src/test/unit/org/bson/BsonBinaryWriterTest.java b/bson/src/test/unit/org/bson/BsonBinaryWriterTest.java index 84f2aeca65..f807deeed6 100644 --- a/bson/src/test/unit/org/bson/BsonBinaryWriterTest.java +++ b/bson/src/test/unit/org/bson/BsonBinaryWriterTest.java @@ -66,7 +66,7 @@ public void shouldThrowWhenMaxDocumentSizeIsExceeded() { writer.writeEndDocument(); fail(); } catch (BsonMaximumSizeExceededException e) { - assertEquals("Document size of 1037 is larger than maximum of 1024.", e.getMessage()); + assertEquals("Document size of 12917 is larger than maximum of 12904.", e.getMessage()); } } From 069470cd24ec632d14ad719588bca6c9b57e9d02 Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Wed, 2 Apr 2025 17:17:20 -0700 Subject: [PATCH 2/3] Update tests. JAVA-5836 --- bson/src/test/unit/org/bson/BsonBinaryWriterTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bson/src/test/unit/org/bson/BsonBinaryWriterTest.java b/bson/src/test/unit/org/bson/BsonBinaryWriterTest.java index f807deeed6..0b067fc816 100644 --- a/bson/src/test/unit/org/bson/BsonBinaryWriterTest.java +++ b/bson/src/test/unit/org/bson/BsonBinaryWriterTest.java @@ -201,16 +201,16 @@ public void testWriteArray() { public void testWriteArrayElements() throws IOException { ByteArrayOutputStream expectedOutput = new ByteArrayOutputStream(); expectedOutput.write(new byte[]{ - 88, 11, 0, 0, //document length + -52, 25, 0, 0, //document length 4, // array type 97, 49, 0, // "a1" name + null terminator - 79, 11, 0, 0}); // array length + -61, 25, 0, 0}); // array length writer.writeStartDocument(); writer.writeStartArray("a1"); int arrayIndex = 0; - while (arrayIndex < 500) { + while (arrayIndex < 1100) { writer.writeBoolean(true); expectedOutput.write(BsonType.BOOLEAN.getValue()); From 5980b3571f7edad4e2828ccdef062ab7d51ccc2a Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Wed, 2 Apr 2025 17:53:52 -0700 Subject: [PATCH 3/3] Change calculation logic. JAVA-5836 --- bson/src/main/org/bson/BsonBinaryWriter.java | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/bson/src/main/org/bson/BsonBinaryWriter.java b/bson/src/main/org/bson/BsonBinaryWriter.java index 896812233c..7764cd718a 100644 --- a/bson/src/main/org/bson/BsonBinaryWriter.java +++ b/bson/src/main/org/bson/BsonBinaryWriter.java @@ -24,6 +24,7 @@ import java.util.List; import java.util.Stack; +import static java.lang.Math.max; import static java.lang.String.format; import static org.bson.assertions.Assertions.notNull; @@ -44,27 +45,27 @@ public class BsonBinaryWriter extends AbstractBsonWriter { private Mark mark; static { + ARRAY_INDEXES_LENGTHS = new int[ARRAY_INDEXES_CACHE_SIZE]; + ARRAY_INDEXES_OFFSETS = new int[ARRAY_INDEXES_CACHE_SIZE]; int totalSize = 0; - int[] lengths = new int[ARRAY_INDEXES_CACHE_SIZE]; for (int i = 0; i < ARRAY_INDEXES_CACHE_SIZE; i++) { - String string = Integer.toString(i); - lengths[i] = string.length() + 1; // +1 for null terminator - totalSize += lengths[i]; + totalSize += (int) (Math.log10(max(i, 1)) + + 1 // number of digits + + 1); // +1 for null terminator } - ARRAY_INDEXES_BUFFER = new byte[totalSize]; - ARRAY_INDEXES_OFFSETS = new int[ARRAY_INDEXES_CACHE_SIZE]; - ARRAY_INDEXES_LENGTHS = lengths; // Fill buffer int offset = 0; for (int i = 0; i < ARRAY_INDEXES_CACHE_SIZE; i++) { - ARRAY_INDEXES_OFFSETS[i] = offset; String string = Integer.toString(i); - for (int j = 0; j < string.length(); j++) { + int length = string.length(); + for (int j = 0; j < length; j++) { ARRAY_INDEXES_BUFFER[offset++] = (byte) string.charAt(j); } ARRAY_INDEXES_BUFFER[offset++] = 0; + ARRAY_INDEXES_OFFSETS[i] = offset - (length + 1); + ARRAY_INDEXES_LENGTHS[i] = length + 1; // +1 for null terminator } }