From 8c643739e27551f0b4244b449ca53f8333281905 Mon Sep 17 00:00:00 2001 From: Tyler Gregg Date: Mon, 27 Jan 2025 14:36:48 -0800 Subject: [PATCH] Do not copy when adding annotations to Expression instances; use an expression pool during evaluation in addition to parsing. --- .../com/amazon/ion/impl/macro/Expression.kt | 70 +++++++++++++++---- .../amazon/ion/impl/macro/MacroEvaluator.kt | 29 +++++--- .../impl/macro/PooledExpressionFactory.java | 36 +++++----- 3 files changed, 92 insertions(+), 43 deletions(-) diff --git a/src/main/java/com/amazon/ion/impl/macro/Expression.kt b/src/main/java/com/amazon/ion/impl/macro/Expression.kt index ff95cd260..b49302297 100644 --- a/src/main/java/com/amazon/ion/impl/macro/Expression.kt +++ b/src/main/java/com/amazon/ion/impl/macro/Expression.kt @@ -101,12 +101,18 @@ sealed interface Expression { // Scalars data class NullValue(override var annotations: List = emptyList(), override var type: IonType) : DataModelValue { - override fun withAnnotations(annotations: List) = copy(annotations = annotations) + override fun withAnnotations(annotations: List): NullValue { + this.annotations = annotations + return this + } } data class BoolValue(override var annotations: List = emptyList(), var value: Boolean) : DataModelValue { override val type: IonType get() = IonType.BOOL - override fun withAnnotations(annotations: List) = copy(annotations = annotations) + override fun withAnnotations(annotations: List): BoolValue { + this.annotations = annotations + return this + } } sealed interface IntValue : DataModelValue { @@ -116,31 +122,46 @@ sealed interface Expression { data class LongIntValue(override var annotations: List = emptyList(), var value: Long) : IntValue { override val type: IonType get() = IonType.INT - override fun withAnnotations(annotations: List) = copy(annotations = annotations) + override fun withAnnotations(annotations: List): LongIntValue { + this.annotations = annotations + return this + } override val bigIntegerValue: BigInteger get() = BigInteger.valueOf(value) override val longValue: Long get() = value } data class BigIntValue(override var annotations: List = emptyList(), var value: BigInteger) : IntValue { override val type: IonType get() = IonType.INT - override fun withAnnotations(annotations: List) = copy(annotations = annotations) + override fun withAnnotations(annotations: List): BigIntValue { + this.annotations = annotations + return this + } override val bigIntegerValue: BigInteger get() = value override val longValue: Long get() = value.longValueExact() } data class FloatValue(override var annotations: List = emptyList(), var value: Double) : DataModelValue { override val type: IonType get() = IonType.FLOAT - override fun withAnnotations(annotations: List) = copy(annotations = annotations) + override fun withAnnotations(annotations: List): FloatValue { + this.annotations = annotations + return this + } } data class DecimalValue(override var annotations: List = emptyList(), var value: BigDecimal) : DataModelValue { override val type: IonType get() = IonType.DECIMAL - override fun withAnnotations(annotations: List) = copy(annotations = annotations) + override fun withAnnotations(annotations: List): DecimalValue { + this.annotations = annotations + return this + } } data class TimestampValue(override var annotations: List = emptyList(), var value: Timestamp) : DataModelValue { override val type: IonType get() = IonType.TIMESTAMP - override fun withAnnotations(annotations: List) = copy(annotations = annotations) + override fun withAnnotations(annotations: List): TimestampValue { + this.annotations = annotations + return this + } } sealed interface TextValue : DataModelValue { @@ -150,13 +171,19 @@ sealed interface Expression { data class StringValue(override var annotations: List = emptyList(), var value: String) : TextValue { override val type: IonType get() = IonType.STRING override val stringValue: String get() = value - override fun withAnnotations(annotations: List) = copy(annotations = annotations) + override fun withAnnotations(annotations: List): StringValue { + this.annotations = annotations + return this + } } data class SymbolValue(override var annotations: List = emptyList(), var value: SymbolToken) : TextValue { override val type: IonType get() = IonType.SYMBOL override val stringValue: String get() = value.assumeText() - override fun withAnnotations(annotations: List) = copy(annotations = annotations) + override fun withAnnotations(annotations: List): SymbolValue { + this.annotations = annotations + return this + } } sealed interface LobValue : DataModelValue { @@ -168,7 +195,10 @@ sealed interface Expression { // We must override hashcode and equals in the lob types because `value` is a `byte[]` data class BlobValue(override var annotations: List = emptyList(), override var value: ByteArray) : LobValue { override val type: IonType get() = IonType.BLOB - override fun withAnnotations(annotations: List) = copy(annotations = annotations) + override fun withAnnotations(annotations: List): BlobValue { + this.annotations = annotations + return this + } override fun hashCode(): Int = annotations.hashCode() * 31 + value.contentHashCode() override fun equals(other: Any?): Boolean { if (this === other) return true @@ -180,7 +210,10 @@ sealed interface Expression { data class ClobValue(override var annotations: List = emptyList(), override var value: ByteArray) : LobValue { override val type: IonType get() = IonType.CLOB - override fun withAnnotations(annotations: List) = copy(annotations = annotations) + override fun withAnnotations(annotations: List): ClobValue { + this.annotations = annotations + return this + } override fun hashCode(): Int = annotations.hashCode() * 31 + value.contentHashCode() override fun equals(other: Any?): Boolean { if (this === other) return true @@ -202,7 +235,10 @@ sealed interface Expression { override var endExclusive: Int ) : DataModelContainer { override val type: IonType get() = IonType.LIST - override fun withAnnotations(annotations: List) = copy(annotations = annotations) + override fun withAnnotations(annotations: List): ListValue { + this.annotations = annotations + return this + } } /** @@ -214,7 +250,10 @@ sealed interface Expression { override var endExclusive: Int ) : DataModelContainer { override val type: IonType get() = IonType.SEXP - override fun withAnnotations(annotations: List) = copy(annotations = annotations) + override fun withAnnotations(annotations: List): SExpValue { + this.annotations = annotations + return this + } } /** @@ -227,7 +266,10 @@ sealed interface Expression { val templateStructIndex: Map> ) : DataModelContainer { override val type: IonType get() = IonType.STRUCT - override fun withAnnotations(annotations: List) = copy(annotations = annotations) + override fun withAnnotations(annotations: List): StructValue { + this.annotations = annotations + return this + } } data class FieldName(var value: SymbolToken) : DataModelExpression diff --git a/src/main/java/com/amazon/ion/impl/macro/MacroEvaluator.kt b/src/main/java/com/amazon/ion/impl/macro/MacroEvaluator.kt index 2f6249a1a..8dbdab125 100644 --- a/src/main/java/com/amazon/ion/impl/macro/MacroEvaluator.kt +++ b/src/main/java/com/amazon/ion/impl/macro/MacroEvaluator.kt @@ -10,6 +10,8 @@ import com.amazon.ion.util.unreachable import java.io.ByteArrayOutputStream import java.math.BigDecimal import java.math.BigInteger +import java.util.* +import kotlin.collections.ArrayList /** * Evaluates an EExpression from a List of [EExpressionBodyExpression] and the [TemplateBodyExpression]s @@ -52,6 +54,8 @@ class MacroEvaluator { private var numExpandedExpressions = 0 /** Pool of [ExpansionInfo] to minimize allocation and garbage collection. */ private val expanderPool: ArrayList = ArrayList(32) + /** Pool of [Expression] to minimize allocation and garbage collection. */ + val expressionPool: PooledExpressionFactory = PooledExpressionFactory() /** Gets an [ExpansionInfo] from the pool (or allocates a new one if necessary), initializing it with the provided values. */ fun getExpander(expansionKind: ExpansionKind, expressions: List, startInclusive: Int, endExclusive: Int, environment: Environment): ExpansionInfo { @@ -87,6 +91,7 @@ class MacroEvaluator { fun reset() { numExpandedExpressions = 0 + expressionPool.clear() } } @@ -279,7 +284,7 @@ class MacroEvaluator { } } thisExpansion.expansionKind = Empty - return StringValue(value = sb.toString()) + return thisExpansion.session.expressionPool.createStringValue(Collections.emptyList(), sb.toString()) } }, MakeSymbol { @@ -298,7 +303,7 @@ class MacroEvaluator { is FieldName -> unreachable() } } - return SymbolValue(value = newSymbolToken(sb.toString())) + return thisExpansion.session.expressionPool.createSymbolValue(Collections.emptyList(), newSymbolToken(sb.toString())) } }, MakeBlob { @@ -314,7 +319,7 @@ class MacroEvaluator { } } thisExpansion.expansionKind = Empty - return BlobValue(value = baos.toByteArray()) + return thisExpansion.session.expressionPool.createBlobValue(Collections.emptyList(), baos.toByteArray()) } }, MakeDecimal { @@ -325,7 +330,7 @@ class MacroEvaluator { val coefficient = thisExpansion.readExactlyOneArgument(COEFFICIENT_ARG).bigIntegerValue val exponent = thisExpansion.readExactlyOneArgument(EXPONENT_ARG).bigIntegerValue thisExpansion.expansionKind = Empty - return DecimalValue(value = BigDecimal(coefficient, -1 * exponent.intValueExact())) + return thisExpansion.session.expressionPool.createDecimalValue(Collections.emptyList(), BigDecimal(coefficient, -1 * exponent.intValueExact())) } }, MakeTimestamp { @@ -379,7 +384,7 @@ class MacroEvaluator { } } thisExpansion.expansionKind = Empty - return TimestampValue(value = ts) + return thisExpansion.session.expressionPool.createTimestampValue(Collections.emptyList(), ts) } catch (e: IllegalArgumentException) { throw IonException(e.message) } @@ -391,10 +396,12 @@ class MacroEvaluator { override fun produceNext(thisExpansion: ExpansionInfo): ExpansionOutputExpressionOrContinue { val fieldName = thisExpansion.readExactlyOneArgument(FIELD_NAME) - val fieldNameExpression = when (fieldName) { - is SymbolValue -> FieldName(fieldName.value) - is StringValue -> FieldName(newSymbolToken(fieldName.value)) - } + val fieldNameExpression = thisExpansion.session.expressionPool.createFieldName( + when (fieldName) { + is SymbolValue -> fieldName.value + is StringValue -> newSymbolToken(fieldName.value) + } + ) thisExpansion.readExactlyOneArgument(FIELD_VALUE) @@ -490,7 +497,7 @@ class MacroEvaluator { val a = thisExpansion.readExactlyOneArgument(ARG_A).bigIntegerValue val b = thisExpansion.readExactlyOneArgument(ARG_B).bigIntegerValue thisExpansion.expansionKind = Empty - return BigIntValue(value = a + b) + return thisExpansion.session.expressionPool.createBigIntValue(Collections.emptyList(), a + b) } }, Delta { @@ -510,7 +517,7 @@ class MacroEvaluator { val nextDelta = nextExpandedArg.bigIntegerValue val nextOutput = runningTotal + nextDelta thisExpansion.additionalState = nextOutput - return BigIntValue(value = nextOutput) + return thisExpansion.session.expressionPool.createBigIntValue(Collections.emptyList(), nextOutput) } EndOfExpansion -> return EndOfExpansion else -> throw IonException("delta arguments must be integers") diff --git a/src/main/java/com/amazon/ion/impl/macro/PooledExpressionFactory.java b/src/main/java/com/amazon/ion/impl/macro/PooledExpressionFactory.java index e48bd13e9..02093e040 100644 --- a/src/main/java/com/amazon/ion/impl/macro/PooledExpressionFactory.java +++ b/src/main/java/com/amazon/ion/impl/macro/PooledExpressionFactory.java @@ -19,25 +19,25 @@ */ public class PooledExpressionFactory { - private static final int POOL_SIZE = 32; + private static final int INITIAL_POOL_SIZE = 32; - private Expression.NullValue[] nullValues = new Expression.NullValue[POOL_SIZE]; - private Expression.BoolValue[] boolValues = new Expression.BoolValue[POOL_SIZE]; - private Expression.LongIntValue[] longIntValues = new Expression.LongIntValue[POOL_SIZE]; - private Expression.BigIntValue[] bigIntValues = new Expression.BigIntValue[POOL_SIZE]; - private Expression.FloatValue[] floatValues = new Expression.FloatValue[POOL_SIZE]; - private Expression.DecimalValue[] decimalValues = new Expression.DecimalValue[POOL_SIZE]; - private Expression.TimestampValue[] timestampValues = new Expression.TimestampValue[POOL_SIZE]; - private Expression.SymbolValue[] symbolValues = new Expression.SymbolValue[POOL_SIZE]; - private Expression.StringValue[] stringValues = new Expression.StringValue[POOL_SIZE]; - private Expression.ClobValue[] clobValues = new Expression.ClobValue[POOL_SIZE]; - private Expression.BlobValue[] blobValues = new Expression.BlobValue[POOL_SIZE]; - private Expression.FieldName[] fieldNames = new Expression.FieldName[POOL_SIZE]; - private Expression.EExpression[] eExpressions = new Expression.EExpression[POOL_SIZE]; - private Expression.ExpressionGroup[] expressionGroups = new Expression.ExpressionGroup[POOL_SIZE]; - private Expression.ListValue[] listValues = new Expression.ListValue[POOL_SIZE]; - private Expression.StructValue[] structValues = new Expression.StructValue[POOL_SIZE]; - private Expression.SExpValue[] sexpValues = new Expression.SExpValue[POOL_SIZE]; + private Expression.NullValue[] nullValues = new Expression.NullValue[INITIAL_POOL_SIZE]; + private Expression.BoolValue[] boolValues = new Expression.BoolValue[INITIAL_POOL_SIZE]; + private Expression.LongIntValue[] longIntValues = new Expression.LongIntValue[INITIAL_POOL_SIZE]; + private Expression.BigIntValue[] bigIntValues = new Expression.BigIntValue[INITIAL_POOL_SIZE]; + private Expression.FloatValue[] floatValues = new Expression.FloatValue[INITIAL_POOL_SIZE]; + private Expression.DecimalValue[] decimalValues = new Expression.DecimalValue[INITIAL_POOL_SIZE]; + private Expression.TimestampValue[] timestampValues = new Expression.TimestampValue[INITIAL_POOL_SIZE]; + private Expression.SymbolValue[] symbolValues = new Expression.SymbolValue[INITIAL_POOL_SIZE]; + private Expression.StringValue[] stringValues = new Expression.StringValue[INITIAL_POOL_SIZE]; + private Expression.ClobValue[] clobValues = new Expression.ClobValue[INITIAL_POOL_SIZE]; + private Expression.BlobValue[] blobValues = new Expression.BlobValue[INITIAL_POOL_SIZE]; + private Expression.FieldName[] fieldNames = new Expression.FieldName[INITIAL_POOL_SIZE]; + private Expression.EExpression[] eExpressions = new Expression.EExpression[INITIAL_POOL_SIZE]; + private Expression.ExpressionGroup[] expressionGroups = new Expression.ExpressionGroup[INITIAL_POOL_SIZE]; + private Expression.ListValue[] listValues = new Expression.ListValue[INITIAL_POOL_SIZE]; + private Expression.StructValue[] structValues = new Expression.StructValue[INITIAL_POOL_SIZE]; + private Expression.SExpValue[] sexpValues = new Expression.SExpValue[INITIAL_POOL_SIZE]; private int nullValuesIndex = 0; private int boolValuesIndex = 0;