Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize StringBuilder/StringBuffer serialization #908

Merged
merged 15 commits into from
Sep 27, 2023
93 changes: 64 additions & 29 deletions java/fury-core/src/main/java/io/fury/serializer/Serializers.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,16 @@
import com.google.common.base.Preconditions;
import com.google.common.primitives.Primitives;
import io.fury.Fury;
import io.fury.collection.Tuple2;
import io.fury.memory.MemoryBuffer;
import io.fury.resolver.ClassResolver;
import io.fury.type.Type;
import io.fury.util.Platform;
import io.fury.util.Utils;
import io.fury.util.function.Functions;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URI;
Expand All @@ -37,6 +40,7 @@
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.regex.Pattern;

/**
Expand Down Expand Up @@ -410,17 +414,69 @@ public Double read(MemoryBuffer buffer) {
}
}

public static final class StringBuilderSerializer extends Serializer<StringBuilder> {
private final StringSerializer stringSerializer;
static Tuple2<Function, Function> builderCache;

private static synchronized Tuple2<Function, Function> getBuilderFunc() {
if (builderCache == null) {
Function getValue =
(Function) Functions.makeGetterFunction(StringBuilder.class.getSuperclass(), "getValue");
if (Platform.JAVA_VERSION > 8) {
Method getCoderMethod;
try {
getCoderMethod = StringBuilder.class.getSuperclass().getDeclaredMethod("getCoder");
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
// ToIntFunction<CharSequence> o = (ToIntFunction<CharSequence>)
pandalee99 marked this conversation as resolved.
Show resolved Hide resolved
// makeGetterFunction(lookup, lookup.unreflect(getCoder), int.class);
Function getCoder =
(Function)
Functions.makeGetterFunction(StringBuilder.class.getSuperclass(), "getCoder");
builderCache = Tuple2.of(getCoder, getValue);
} else {
builderCache = Tuple2.of(null, getValue);
}
}
return builderCache;
}

public StringBuilderSerializer(Fury fury) {
super(fury, StringBuilder.class);
public abstract static class AbstractStringBuilderSerializer<T extends CharSequence>
extends Serializer<T> {
protected final Function getCoder;
protected final Function getValue;
protected final StringSerializer stringSerializer;

public AbstractStringBuilderSerializer(Fury fury, Class<T> type) {
super(fury, type);
Tuple2<Function, Function> builderFunc = getBuilderFunc();
getCoder = builderFunc.f0;
getValue = builderFunc.f1;
stringSerializer = new StringSerializer(fury);
}

pandalee99 marked this conversation as resolved.
Show resolved Hide resolved
@Override
public void write(MemoryBuffer buffer, StringBuilder value) {
stringSerializer.writeJavaString(buffer, value.toString());
public void write(MemoryBuffer buffer, T value) {
if (Platform.JAVA_VERSION > 8) {
byte coder = (byte) getCoder.apply(value);
byte[] v = (byte[]) getValue.apply(value);
buffer.writeByte(coder);
buffer.writeBytesWithSizeEmbedded(v);
pandalee99 marked this conversation as resolved.
Show resolved Hide resolved
} else {
char[] v = (char[]) getValue.apply(value);
if (StringSerializer.isAscii(v)) {
stringSerializer.writeJDK8Ascii(buffer, v, v.length);
pandalee99 marked this conversation as resolved.
Show resolved Hide resolved
} else {
stringSerializer.writeJDK8UTF16(buffer, v, v.length);
pandalee99 marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
}

public static final class StringBuilderSerializer
extends AbstractStringBuilderSerializer<StringBuilder> {

public StringBuilderSerializer(Fury fury) {
super(fury, StringBuilder.class);
}

@Override
Expand All @@ -429,38 +485,17 @@ public StringBuilder read(MemoryBuffer buffer) {
}
}

pandalee99 marked this conversation as resolved.
Show resolved Hide resolved
public static final class StringBufferSerializer extends Serializer<StringBuffer> {
private final StringSerializer stringSerializer;
public static final class StringBufferSerializer
extends AbstractStringBuilderSerializer<StringBuffer> {

public StringBufferSerializer(Fury fury) {
super(fury, StringBuffer.class);
stringSerializer = new StringSerializer(fury);
}

@Override
public short getXtypeId() {
pandalee99 marked this conversation as resolved.
Show resolved Hide resolved
return (short) -Type.STRING.getId();
}

@Override
public void write(MemoryBuffer buffer, StringBuffer value) {
stringSerializer.writeJavaString(buffer, value.toString());
}

@Override
public void xwrite(MemoryBuffer buffer, StringBuffer value) {
stringSerializer.writeUTF8String(buffer, value.toString());
}

pandalee99 marked this conversation as resolved.
Show resolved Hide resolved
@Override
public StringBuffer read(MemoryBuffer buffer) {
return new StringBuffer(stringSerializer.readJavaString(buffer));
}

@Override
public StringBuffer xread(MemoryBuffer buffer) {
return new StringBuffer(stringSerializer.readUTF8String(buffer));
}
}

public static final class EnumSerializer extends Serializer<Enum> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,9 @@ public Expression writeStringExpr(Expression strSerializer, Expression buffer, E
public void writeJava8StringCompressed(MemoryBuffer buffer, String value) {
final char[] chars = (char[]) Platform.getObject(value, STRING_VALUE_FIELD_OFFSET);
if (isAscii(chars)) {
writeJDK8Ascii(buffer, chars);
writeJDK8Ascii(buffer, chars, chars.length);
} else {
writeJDK8UTF16(buffer, chars);
writeJDK8UTF16(buffer, chars, chars.length);
}
}

Expand Down Expand Up @@ -289,9 +289,9 @@ public void writeJavaString(MemoryBuffer buffer, String value) {
final char[] chars = (char[]) Platform.getObject(value, STRING_VALUE_FIELD_OFFSET);
if (compressString) {
if (isAscii(chars)) {
writeJDK8Ascii(buffer, chars);
writeJDK8Ascii(buffer, chars, chars.length);
} else {
writeJDK8UTF16(buffer, chars);
writeJDK8UTF16(buffer, chars, chars.length);
}
} else {
int numBytes = MathUtils.doubleExact(value.length());
Expand Down Expand Up @@ -382,8 +382,7 @@ public static void writeJDK11String(MemoryBuffer buffer, String value) {
buffer.unsafeWriterIndex(writerIndex);
}

public void writeJDK8Ascii(MemoryBuffer buffer, char[] chars) {
final int strLen = chars.length;
public void writeJDK8Ascii(MemoryBuffer buffer, char[] chars, final int strLen) {
int writerIndex = buffer.writerIndex();
// The `ensure` ensure next operations are safe without bound checks,
// and inner heap buffer doesn't change.
Expand Down Expand Up @@ -413,8 +412,7 @@ public void writeJDK8Ascii(MemoryBuffer buffer, char[] chars) {
}
}

public void writeJDK8UTF16(MemoryBuffer buffer, char[] chars) {
int strLen = chars.length;
public void writeJDK8UTF16(MemoryBuffer buffer, char[] chars, int strLen) {
int numBytes = MathUtils.doubleExact(strLen);
if (Platform.IS_LITTLE_ENDIAN) {
buffer.writeByte(UTF16);
Expand Down