From a128bfa890ceaf9e49f60edcf520e2cac1750971 Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Tue, 14 Jan 2025 23:37:50 -0600 Subject: [PATCH] Reimpl byte[] stream without synchronization The byte[] output stream used here extended ByteArrayOutputStream from the JDK, which sychronizes all mutation operations (like writes). Since this is only going to be used once within a given call stack, it needs no synchronization. This change more than triples the performance of a benchmark of dumping an array of empty arrays and should increase performance of all dump forms. --- .../json/ext/ByteListDirectOutputStream.java | 48 +++++++++++++++++-- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/java/src/json/ext/ByteListDirectOutputStream.java b/java/src/json/ext/ByteListDirectOutputStream.java index 178cf11c..006a5ead 100644 --- a/java/src/json/ext/ByteListDirectOutputStream.java +++ b/java/src/json/ext/ByteListDirectOutputStream.java @@ -3,14 +3,54 @@ import org.jcodings.Encoding; import org.jruby.util.ByteList; -import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Arrays; + +public class ByteListDirectOutputStream extends OutputStream { + private byte[] buffer; + private int length; -public class ByteListDirectOutputStream extends ByteArrayOutputStream { ByteListDirectOutputStream(int size) { - super(size); + buffer = new byte[size]; } public ByteList toByteListDirect(Encoding encoding) { - return new ByteList(buf, 0, count, encoding, false); + return new ByteList(buffer, 0, length, encoding, false); + } + + @Override + public void write(int b) throws IOException { + int myLength = this.length; + grow(buffer, myLength, 1); + buffer[length++] = (byte) b; + } + + @Override + public void write(byte[] bytes, int start, int length) throws IOException { + int myLength = this.length; + grow(buffer, myLength, length); + System.arraycopy(bytes, start, buffer, myLength, length); + this.length = myLength + length; + } + + @Override + public void write(byte[] bytes) throws IOException { + int myLength = this.length; + int moreLength = bytes.length; + grow(buffer, myLength, moreLength); + System.arraycopy(bytes, 0, buffer, myLength, moreLength); + this.length = myLength + moreLength; + } + + private static void grow(ByteListDirectOutputStream self, byte[] buffer, int myLength, int more) { + int newLength = myLength + more; + int myCapacity = buffer.length; + int diff = newLength - myCapacity; + if (diff > 0) { + // grow to double current length or capacity + diff, whichever is greater + int growBy = Math.max(myLength, diff); + self.buffer = Arrays.copyOf(self.buffer, myCapacity + growBy); + } } }