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

[Feature] Add Serialization Optimization for Primitive Collection Types #2386

Open
wants to merge 1 commit into
base: bmarcaurele/small-conjure-collections-refactor
Choose a base branch
from

Conversation

bmarcaur
Copy link
Member

@bmarcaur bmarcaur commented Oct 18, 2024

Before this PR

When Jackson attempted to serialize a primitive collection type, e.g. ConjureDoubleList, it utilized Jackon's default serializers, specifically IndexedListSerializer. This makes sense when you remember that ConjureDoubleList does not have an implemented serializer, so at best its going to be treated like List at time of serialization. This causes unnecessary boxing no matter how optimally you this collection stores the primitive types. You can see evidence of these points in the debugging screenshot below:

image

After this PR

Adds serialization paths that avoid boxing and directly leverage Jackson primitive serialization routines.

I purposely didn't implement this feature for ConjureBooleanList because it does not have a simple serialized format that benefits from primitive optimization. i.e. it expands a single bit out to true or false over the wire. Frankly, this type feels like it was added for completeness and not for usefulness. A dense boolean[] is abstractly a bit mask and if that is the intent should have its own type. I think the existence of this class is just maintenance overhead at this point. This has been removed #2390

Possible downsides?

Creates an alternative path for serialization that needs to match the default serialization paths.

@palantir palantir deleted a comment from changelog-app bot Oct 18, 2024
@bmarcaur bmarcaur changed the title [FR] Add Serialization Optimization for Primitive Collection Types [Feature] Add Serialization Optimization for Primitive Collection Types Oct 22, 2024
@bmarcaur bmarcaur marked this pull request as ready for review October 23, 2024 15:17
@bmarcaur bmarcaur force-pushed the bmarcaurele/conjure-double-list-serialization branch from 1fec024 to b6196ea Compare November 8, 2024 23:44
@bmarcaur bmarcaur force-pushed the bmarcaurele/conjure-double-list-serialization branch 4 times, most recently from a76bb0d to 505cc4c Compare November 14, 2024 16:47
import org.eclipse.collections.impl.utility.Iterate;

/**
* ConjureDoubleList is a boxed list wrapper for the eclipse-collections DoubleArrayList. In eclipse-collections 12,
* a BoxedMutableDoubleList will be released. Once available, ConjureDoubleList should be replaced with that.
*/
final class ConjureDoubleList extends AbstractList<Double> implements RandomAccess {
private final DoubleArrayList delegate;
private final MutableDoubleList delegate;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing this type so that I can wrap both DoubleArrayList and UnmodifiableDoubleList which share this interface as the lowest common denominator.

import org.eclipse.collections.impl.utility.Iterate;

/**
* ConjureIntegerList is a boxed list wrapper for the eclipse-collections IntArrayList. In eclipse-collections 12,
* a BoxedMutableIntList will be released. Once available, ConjureIntegerList should be replaced with that.
*/
final class ConjureIntegerList extends AbstractList<Integer> implements RandomAccess {
private final IntArrayList delegate;
private final MutableIntList delegate;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

import org.eclipse.collections.impl.utility.Iterate;

/**
* ConjureSafeLongList is a boxed list wrapper for the eclipse-collections LongArrayList. This handles boxing/unboxing
* with SafeLongs.
*/
final class ConjureSafeLongList extends AbstractList<SafeLong> implements RandomAccess {
private final LongArrayList delegate;
private final MutableLongList delegate;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// This is a serialization optimization that avoids boxing, but does copy
@JsonValue
double[] jacksonSerialize() {
return delegate.toArray();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is how we avoid boxing out the door (I checked the Jackson code paths manually, it does infact operate on the array), while also respecting the various Jackson flags we use like excludeEmptyCollections. It does make a copy, but implementing a version that avoided this copy was very complex as it required leaking details of various Jackson settings.

// This is a serialization optimization that avoids boxing, but does copy
@JsonValue
int[] jacksonSerialize() {
return delegate.toArray();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// This is a serialization optimization that avoids boxing, but does copy
@JsonValue
long[] jacksonSerialize() {
return delegate.toArray();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bmarcaur bmarcaur force-pushed the bmarcaurele/conjure-double-list-serialization branch from 505cc4c to 8afd7d8 Compare November 14, 2024 18:07
@@ -259,7 +260,7 @@ private static MethodSpec createConstructor(Collection<EnrichedField> fields, Co
// is private and necessarily called from the builder, which does its own defensive copying.
if (field.conjureDef().getType().accept(TypeVisitor.IS_LIST)) {
// TODO(melliot): contribute a fix to JavaPoet that parses $T correctly for a JavaPoet FieldSpec
body.addStatement("this.$1N = $2T.unmodifiableList($1N)", spec, Collections.class);
body.addStatement("this.$1N = $2T.unmodifiableList($1N)", spec, ConjureCollections.class);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pulled this out in to a separate PR so this one can continue to focus on the optimizations: #2409

@bmarcaur bmarcaur force-pushed the bmarcaurele/conjure-double-list-serialization branch from 8afd7d8 to 8376624 Compare November 14, 2024 18:20
@bmarcaur bmarcaur changed the base branch from develop to bmarcaurele/small-conjure-collections-refactor November 14, 2024 18:20
@bmarcaur bmarcaur requested a review from carterkozak November 18, 2024 18:04
@bmarcaur bmarcaur force-pushed the bmarcaurele/conjure-double-list-serialization branch from 8376624 to 42e78f4 Compare November 25, 2024 18:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants