From f024cd12ce0d4276fae7109b954fc35cc98f6c2a Mon Sep 17 00:00:00 2001 From: Peter Vendamere Date: Tue, 1 Aug 2023 21:56:53 -0500 Subject: [PATCH] Adds benchmarks, largely copied from Avro4s --- .../generated/AttributeValue/Empty.java | 284 ++++++++++++ .../generated/AttributeValue/Invalid.java | 430 ++++++++++++++++++ .../generated/AttributeValue/ValidInt.java | 356 +++++++++++++++ .../RecordWithUnionAndTypeField.java | 274 +++++++++++ .../scala/benchmarks/BenchmarkHelpers.scala | 15 + .../scala/benchmarks/CommonParameters.scala | 43 ++ .../src/main/scala/benchmarks/Decoding.scala | 54 +++ .../src/main/scala/benchmarks/Encoding.scala | 42 ++ .../scala/benchmarks/record/Attributes.scala | 31 ++ .../scala/benchmarks/record/Records.scala | 9 + build.sbt | 23 + project/plugins.sbt | 1 + run_benchmarks.sh | 3 + 13 files changed, 1565 insertions(+) create mode 100644 benchmarks/src/main/java/benchmarks/record/generated/AttributeValue/Empty.java create mode 100644 benchmarks/src/main/java/benchmarks/record/generated/AttributeValue/Invalid.java create mode 100644 benchmarks/src/main/java/benchmarks/record/generated/AttributeValue/ValidInt.java create mode 100644 benchmarks/src/main/java/benchmarks/record/generated/RecordWithUnionAndTypeField.java create mode 100644 benchmarks/src/main/scala/benchmarks/BenchmarkHelpers.scala create mode 100644 benchmarks/src/main/scala/benchmarks/CommonParameters.scala create mode 100644 benchmarks/src/main/scala/benchmarks/Decoding.scala create mode 100644 benchmarks/src/main/scala/benchmarks/Encoding.scala create mode 100644 benchmarks/src/main/scala/benchmarks/record/Attributes.scala create mode 100644 benchmarks/src/main/scala/benchmarks/record/Records.scala create mode 100755 run_benchmarks.sh diff --git a/benchmarks/src/main/java/benchmarks/record/generated/AttributeValue/Empty.java b/benchmarks/src/main/java/benchmarks/record/generated/AttributeValue/Empty.java new file mode 100644 index 00000000..d7fc73a0 --- /dev/null +++ b/benchmarks/src/main/java/benchmarks/record/generated/AttributeValue/Empty.java @@ -0,0 +1,284 @@ +/** + * Autogenerated by Avro + * + * DO NOT EDIT DIRECTLY + */ +package benchmarks.record.generated.AttributeValue; + +import org.apache.avro.generic.GenericArray; +import org.apache.avro.specific.SpecificData; +import org.apache.avro.util.Utf8; +import org.apache.avro.message.BinaryMessageEncoder; +import org.apache.avro.message.BinaryMessageDecoder; +import org.apache.avro.message.SchemaStore; + +@org.apache.avro.specific.AvroGenerated +public class Empty extends org.apache.avro.specific.SpecificRecordBase implements org.apache.avro.specific.SpecificRecord { + private static final long serialVersionUID = 2553753959124188411L; + public static final org.apache.avro.Schema SCHEMA$ = new org.apache.avro.Schema.Parser().parse("{\"type\":\"record\",\"name\":\"Empty\",\"namespace\":\"benchmarks.record.generated.AttributeValue\",\"fields\":[{\"name\":\"timestamp\",\"type\":{\"type\":\"long\",\"logicalType\":\"timestamp-millis\"}}]}"); + public static org.apache.avro.Schema getClassSchema() { return SCHEMA$; } + + private static SpecificData MODEL$ = new SpecificData(); +static { + MODEL$.addLogicalTypeConversion(new org.apache.avro.data.TimeConversions.TimestampMillisConversion()); + } + + private static final BinaryMessageEncoder ENCODER = + new BinaryMessageEncoder(MODEL$, SCHEMA$); + + private static final BinaryMessageDecoder DECODER = + new BinaryMessageDecoder(MODEL$, SCHEMA$); + + /** + * Return the BinaryMessageEncoder instance used by this class. + * @return the message encoder used by this class + */ + public static BinaryMessageEncoder getEncoder() { + return ENCODER; + } + + /** + * Return the BinaryMessageDecoder instance used by this class. + * @return the message decoder used by this class + */ + public static BinaryMessageDecoder getDecoder() { + return DECODER; + } + + /** + * Create a new BinaryMessageDecoder instance for this class that uses the specified {@link SchemaStore}. + * @param resolver a {@link SchemaStore} used to find schemas by fingerprint + * @return a BinaryMessageDecoder instance for this class backed by the given SchemaStore + */ + public static BinaryMessageDecoder createDecoder(SchemaStore resolver) { + return new BinaryMessageDecoder(MODEL$, SCHEMA$, resolver); + } + + /** + * Serializes this Empty to a ByteBuffer. + * @return a buffer holding the serialized data for this instance + * @throws java.io.IOException if this instance could not be serialized + */ + public java.nio.ByteBuffer toByteBuffer() throws java.io.IOException { + return ENCODER.encode(this); + } + + /** + * Deserializes a Empty from a ByteBuffer. + * @param b a byte buffer holding serialized data for an instance of this class + * @return a Empty instance decoded from the given buffer + * @throws java.io.IOException if the given bytes could not be deserialized into an instance of this class + */ + public static Empty fromByteBuffer( + java.nio.ByteBuffer b) throws java.io.IOException { + return DECODER.decode(b); + } + + private java.time.Instant timestamp; + + /** + * Default constructor. Note that this does not initialize fields + * to their default values from the schema. If that is desired then + * one should use newBuilder(). + */ + public Empty() {} + + /** + * All-args constructor. + * @param timestamp The new value for timestamp + */ + public Empty(java.time.Instant timestamp) { + this.timestamp = timestamp.truncatedTo(java.time.temporal.ChronoUnit.MILLIS); + } + + public org.apache.avro.specific.SpecificData getSpecificData() { return MODEL$; } + public org.apache.avro.Schema getSchema() { return SCHEMA$; } + // Used by DatumWriter. Applications should not call. + public java.lang.Object get(int field$) { + switch (field$) { + case 0: return timestamp; + default: throw new org.apache.avro.AvroRuntimeException("Bad index"); + } + } + + private static final org.apache.avro.Conversion[] conversions = + new org.apache.avro.Conversion[] { + new org.apache.avro.data.TimeConversions.TimestampMillisConversion(), + null + }; + + @Override + public org.apache.avro.Conversion getConversion(int field) { + return conversions[field]; + } + + // Used by DatumReader. Applications should not call. + @SuppressWarnings(value="unchecked") + public void put(int field$, java.lang.Object value$) { + switch (field$) { + case 0: timestamp = (java.time.Instant)value$; break; + default: throw new org.apache.avro.AvroRuntimeException("Bad index"); + } + } + + /** + * Gets the value of the 'timestamp' field. + * @return The value of the 'timestamp' field. + */ + public java.time.Instant getTimestamp() { + return timestamp; + } + + + /** + * Sets the value of the 'timestamp' field. + * @param value the value to set. + */ + public void setTimestamp(java.time.Instant value) { + this.timestamp = value.truncatedTo(java.time.temporal.ChronoUnit.MILLIS); + } + + /** + * Creates a new Empty RecordBuilder. + * @return A new Empty RecordBuilder + */ + public static benchmarks.record.generated.AttributeValue.Empty.Builder newBuilder() { + return new benchmarks.record.generated.AttributeValue.Empty.Builder(); + } + + /** + * Creates a new Empty RecordBuilder by copying an existing Builder. + * @param other The existing builder to copy. + * @return A new Empty RecordBuilder + */ + public static benchmarks.record.generated.AttributeValue.Empty.Builder newBuilder(benchmarks.record.generated.AttributeValue.Empty.Builder other) { + if (other == null) { + return new benchmarks.record.generated.AttributeValue.Empty.Builder(); + } else { + return new benchmarks.record.generated.AttributeValue.Empty.Builder(other); + } + } + + /** + * Creates a new Empty RecordBuilder by copying an existing Empty instance. + * @param other The existing instance to copy. + * @return A new Empty RecordBuilder + */ + public static benchmarks.record.generated.AttributeValue.Empty.Builder newBuilder(benchmarks.record.generated.AttributeValue.Empty other) { + if (other == null) { + return new benchmarks.record.generated.AttributeValue.Empty.Builder(); + } else { + return new benchmarks.record.generated.AttributeValue.Empty.Builder(other); + } + } + + /** + * RecordBuilder for Empty instances. + */ + public static class Builder extends org.apache.avro.specific.SpecificRecordBuilderBase + implements org.apache.avro.data.RecordBuilder { + + private java.time.Instant timestamp; + + /** Creates a new Builder */ + private Builder() { + super(SCHEMA$); + } + + /** + * Creates a Builder by copying an existing Builder. + * @param other The existing Builder to copy. + */ + private Builder(benchmarks.record.generated.AttributeValue.Empty.Builder other) { + super(other); + if (isValidValue(fields()[0], other.timestamp)) { + this.timestamp = data().deepCopy(fields()[0].schema(), other.timestamp); + fieldSetFlags()[0] = other.fieldSetFlags()[0]; + } + } + + /** + * Creates a Builder by copying an existing Empty instance + * @param other The existing instance to copy. + */ + private Builder(benchmarks.record.generated.AttributeValue.Empty other) { + super(SCHEMA$); + if (isValidValue(fields()[0], other.timestamp)) { + this.timestamp = data().deepCopy(fields()[0].schema(), other.timestamp); + fieldSetFlags()[0] = true; + } + } + + /** + * Gets the value of the 'timestamp' field. + * @return The value. + */ + public java.time.Instant getTimestamp() { + return timestamp; + } + + + /** + * Sets the value of the 'timestamp' field. + * @param value The value of 'timestamp'. + * @return This builder. + */ + public benchmarks.record.generated.AttributeValue.Empty.Builder setTimestamp(java.time.Instant value) { + validate(fields()[0], value); + this.timestamp = value.truncatedTo(java.time.temporal.ChronoUnit.MILLIS); + fieldSetFlags()[0] = true; + return this; + } + + /** + * Checks whether the 'timestamp' field has been set. + * @return True if the 'timestamp' field has been set, false otherwise. + */ + public boolean hasTimestamp() { + return fieldSetFlags()[0]; + } + + + /** + * Clears the value of the 'timestamp' field. + * @return This builder. + */ + public benchmarks.record.generated.AttributeValue.Empty.Builder clearTimestamp() { + fieldSetFlags()[0] = false; + return this; + } + + @Override + @SuppressWarnings("unchecked") + public Empty build() { + try { + Empty record = new Empty(); + record.timestamp = fieldSetFlags()[0] ? this.timestamp : (java.time.Instant) defaultValue(fields()[0]); + return record; + } catch (org.apache.avro.AvroMissingFieldException e) { + throw e; + } catch (java.lang.Exception e) { + throw new org.apache.avro.AvroRuntimeException(e); + } + } + } + + @SuppressWarnings("unchecked") + private static final org.apache.avro.io.DatumWriter + WRITER$ = (org.apache.avro.io.DatumWriter)MODEL$.createDatumWriter(SCHEMA$); + + @Override public void writeExternal(java.io.ObjectOutput out) + throws java.io.IOException { + WRITER$.write(this, SpecificData.getEncoder(out)); + } + + @SuppressWarnings("unchecked") + private static final org.apache.avro.io.DatumReader + READER$ = (org.apache.avro.io.DatumReader)MODEL$.createDatumReader(SCHEMA$); + + @Override public void readExternal(java.io.ObjectInput in) + throws java.io.IOException { + READER$.read(this, SpecificData.getDecoder(in)); + } + +} diff --git a/benchmarks/src/main/java/benchmarks/record/generated/AttributeValue/Invalid.java b/benchmarks/src/main/java/benchmarks/record/generated/AttributeValue/Invalid.java new file mode 100644 index 00000000..5d06b21a --- /dev/null +++ b/benchmarks/src/main/java/benchmarks/record/generated/AttributeValue/Invalid.java @@ -0,0 +1,430 @@ +/** + * Autogenerated by Avro + * + * DO NOT EDIT DIRECTLY + */ +package benchmarks.record.generated.AttributeValue; + +import org.apache.avro.generic.GenericArray; +import org.apache.avro.specific.SpecificData; +import org.apache.avro.util.Utf8; +import org.apache.avro.message.BinaryMessageEncoder; +import org.apache.avro.message.BinaryMessageDecoder; +import org.apache.avro.message.SchemaStore; + +@org.apache.avro.specific.AvroGenerated +public class Invalid extends org.apache.avro.specific.SpecificRecordBase implements org.apache.avro.specific.SpecificRecord { + private static final long serialVersionUID = -3669368427118149100L; + public static final org.apache.avro.Schema SCHEMA$ = new org.apache.avro.Schema.Parser().parse("{\"type\":\"record\",\"name\":\"Invalid\",\"namespace\":\"benchmarks.record.generated.AttributeValue\",\"fields\":[{\"name\":\"value\",\"type\":[\"null\",\"string\"]},{\"name\":\"timestamp\",\"type\":{\"type\":\"long\",\"logicalType\":\"timestamp-millis\"}},{\"name\":\"errors\",\"type\":{\"type\":\"array\",\"items\":\"string\"}}]}"); + public static org.apache.avro.Schema getClassSchema() { return SCHEMA$; } + + private static SpecificData MODEL$ = new SpecificData(); +static { + MODEL$.addLogicalTypeConversion(new org.apache.avro.data.TimeConversions.TimestampMillisConversion()); + } + + private static final BinaryMessageEncoder ENCODER = + new BinaryMessageEncoder(MODEL$, SCHEMA$); + + private static final BinaryMessageDecoder DECODER = + new BinaryMessageDecoder(MODEL$, SCHEMA$); + + /** + * Return the BinaryMessageEncoder instance used by this class. + * @return the message encoder used by this class + */ + public static BinaryMessageEncoder getEncoder() { + return ENCODER; + } + + /** + * Return the BinaryMessageDecoder instance used by this class. + * @return the message decoder used by this class + */ + public static BinaryMessageDecoder getDecoder() { + return DECODER; + } + + /** + * Create a new BinaryMessageDecoder instance for this class that uses the specified {@link SchemaStore}. + * @param resolver a {@link SchemaStore} used to find schemas by fingerprint + * @return a BinaryMessageDecoder instance for this class backed by the given SchemaStore + */ + public static BinaryMessageDecoder createDecoder(SchemaStore resolver) { + return new BinaryMessageDecoder(MODEL$, SCHEMA$, resolver); + } + + /** + * Serializes this Invalid to a ByteBuffer. + * @return a buffer holding the serialized data for this instance + * @throws java.io.IOException if this instance could not be serialized + */ + public java.nio.ByteBuffer toByteBuffer() throws java.io.IOException { + return ENCODER.encode(this); + } + + /** + * Deserializes a Invalid from a ByteBuffer. + * @param b a byte buffer holding serialized data for an instance of this class + * @return a Invalid instance decoded from the given buffer + * @throws java.io.IOException if the given bytes could not be deserialized into an instance of this class + */ + public static Invalid fromByteBuffer( + java.nio.ByteBuffer b) throws java.io.IOException { + return DECODER.decode(b); + } + + private java.lang.CharSequence value; + private java.time.Instant timestamp; + private java.util.List errors; + + /** + * Default constructor. Note that this does not initialize fields + * to their default values from the schema. If that is desired then + * one should use newBuilder(). + */ + public Invalid() {} + + /** + * All-args constructor. + * @param value The new value for value + * @param timestamp The new value for timestamp + * @param errors The new value for errors + */ + public Invalid(java.lang.CharSequence value, java.time.Instant timestamp, java.util.List errors) { + this.value = value; + this.timestamp = timestamp.truncatedTo(java.time.temporal.ChronoUnit.MILLIS); + this.errors = errors; + } + + public org.apache.avro.specific.SpecificData getSpecificData() { return MODEL$; } + public org.apache.avro.Schema getSchema() { return SCHEMA$; } + // Used by DatumWriter. Applications should not call. + public java.lang.Object get(int field$) { + switch (field$) { + case 0: return value; + case 1: return timestamp; + case 2: return errors; + default: throw new org.apache.avro.AvroRuntimeException("Bad index"); + } + } + + private static final org.apache.avro.Conversion[] conversions = + new org.apache.avro.Conversion[] { + null, + new org.apache.avro.data.TimeConversions.TimestampMillisConversion(), + null, + null + }; + + @Override + public org.apache.avro.Conversion getConversion(int field) { + return conversions[field]; + } + + // Used by DatumReader. Applications should not call. + @SuppressWarnings(value="unchecked") + public void put(int field$, java.lang.Object value$) { + switch (field$) { + case 0: value = (java.lang.CharSequence)value$; break; + case 1: timestamp = (java.time.Instant)value$; break; + case 2: errors = (java.util.List)value$; break; + default: throw new org.apache.avro.AvroRuntimeException("Bad index"); + } + } + + /** + * Gets the value of the 'value' field. + * @return The value of the 'value' field. + */ + public java.lang.CharSequence getValue() { + return value; + } + + + /** + * Sets the value of the 'value' field. + * @param value the value to set. + */ + public void setValue(java.lang.CharSequence value) { + this.value = value; + } + + /** + * Gets the value of the 'timestamp' field. + * @return The value of the 'timestamp' field. + */ + public java.time.Instant getTimestamp() { + return timestamp; + } + + + /** + * Sets the value of the 'timestamp' field. + * @param value the value to set. + */ + public void setTimestamp(java.time.Instant value) { + this.timestamp = value.truncatedTo(java.time.temporal.ChronoUnit.MILLIS); + } + + /** + * Gets the value of the 'errors' field. + * @return The value of the 'errors' field. + */ + public java.util.List getErrors() { + return errors; + } + + + /** + * Sets the value of the 'errors' field. + * @param value the value to set. + */ + public void setErrors(java.util.List value) { + this.errors = value; + } + + /** + * Creates a new Invalid RecordBuilder. + * @return A new Invalid RecordBuilder + */ + public static benchmarks.record.generated.AttributeValue.Invalid.Builder newBuilder() { + return new benchmarks.record.generated.AttributeValue.Invalid.Builder(); + } + + /** + * Creates a new Invalid RecordBuilder by copying an existing Builder. + * @param other The existing builder to copy. + * @return A new Invalid RecordBuilder + */ + public static benchmarks.record.generated.AttributeValue.Invalid.Builder newBuilder(benchmarks.record.generated.AttributeValue.Invalid.Builder other) { + if (other == null) { + return new benchmarks.record.generated.AttributeValue.Invalid.Builder(); + } else { + return new benchmarks.record.generated.AttributeValue.Invalid.Builder(other); + } + } + + /** + * Creates a new Invalid RecordBuilder by copying an existing Invalid instance. + * @param other The existing instance to copy. + * @return A new Invalid RecordBuilder + */ + public static benchmarks.record.generated.AttributeValue.Invalid.Builder newBuilder(benchmarks.record.generated.AttributeValue.Invalid other) { + if (other == null) { + return new benchmarks.record.generated.AttributeValue.Invalid.Builder(); + } else { + return new benchmarks.record.generated.AttributeValue.Invalid.Builder(other); + } + } + + /** + * RecordBuilder for Invalid instances. + */ + public static class Builder extends org.apache.avro.specific.SpecificRecordBuilderBase + implements org.apache.avro.data.RecordBuilder { + + private java.lang.CharSequence value; + private java.time.Instant timestamp; + private java.util.List errors; + + /** Creates a new Builder */ + private Builder() { + super(SCHEMA$); + } + + /** + * Creates a Builder by copying an existing Builder. + * @param other The existing Builder to copy. + */ + private Builder(benchmarks.record.generated.AttributeValue.Invalid.Builder other) { + super(other); + if (isValidValue(fields()[0], other.value)) { + this.value = data().deepCopy(fields()[0].schema(), other.value); + fieldSetFlags()[0] = other.fieldSetFlags()[0]; + } + if (isValidValue(fields()[1], other.timestamp)) { + this.timestamp = data().deepCopy(fields()[1].schema(), other.timestamp); + fieldSetFlags()[1] = other.fieldSetFlags()[1]; + } + if (isValidValue(fields()[2], other.errors)) { + this.errors = data().deepCopy(fields()[2].schema(), other.errors); + fieldSetFlags()[2] = other.fieldSetFlags()[2]; + } + } + + /** + * Creates a Builder by copying an existing Invalid instance + * @param other The existing instance to copy. + */ + private Builder(benchmarks.record.generated.AttributeValue.Invalid other) { + super(SCHEMA$); + if (isValidValue(fields()[0], other.value)) { + this.value = data().deepCopy(fields()[0].schema(), other.value); + fieldSetFlags()[0] = true; + } + if (isValidValue(fields()[1], other.timestamp)) { + this.timestamp = data().deepCopy(fields()[1].schema(), other.timestamp); + fieldSetFlags()[1] = true; + } + if (isValidValue(fields()[2], other.errors)) { + this.errors = data().deepCopy(fields()[2].schema(), other.errors); + fieldSetFlags()[2] = true; + } + } + + /** + * Gets the value of the 'value' field. + * @return The value. + */ + public java.lang.CharSequence getValue() { + return value; + } + + + /** + * Sets the value of the 'value' field. + * @param value The value of 'value'. + * @return This builder. + */ + public benchmarks.record.generated.AttributeValue.Invalid.Builder setValue(java.lang.CharSequence value) { + validate(fields()[0], value); + this.value = value; + fieldSetFlags()[0] = true; + return this; + } + + /** + * Checks whether the 'value' field has been set. + * @return True if the 'value' field has been set, false otherwise. + */ + public boolean hasValue() { + return fieldSetFlags()[0]; + } + + + /** + * Clears the value of the 'value' field. + * @return This builder. + */ + public benchmarks.record.generated.AttributeValue.Invalid.Builder clearValue() { + value = null; + fieldSetFlags()[0] = false; + return this; + } + + /** + * Gets the value of the 'timestamp' field. + * @return The value. + */ + public java.time.Instant getTimestamp() { + return timestamp; + } + + + /** + * Sets the value of the 'timestamp' field. + * @param value The value of 'timestamp'. + * @return This builder. + */ + public benchmarks.record.generated.AttributeValue.Invalid.Builder setTimestamp(java.time.Instant value) { + validate(fields()[1], value); + this.timestamp = value.truncatedTo(java.time.temporal.ChronoUnit.MILLIS); + fieldSetFlags()[1] = true; + return this; + } + + /** + * Checks whether the 'timestamp' field has been set. + * @return True if the 'timestamp' field has been set, false otherwise. + */ + public boolean hasTimestamp() { + return fieldSetFlags()[1]; + } + + + /** + * Clears the value of the 'timestamp' field. + * @return This builder. + */ + public benchmarks.record.generated.AttributeValue.Invalid.Builder clearTimestamp() { + fieldSetFlags()[1] = false; + return this; + } + + /** + * Gets the value of the 'errors' field. + * @return The value. + */ + public java.util.List getErrors() { + return errors; + } + + + /** + * Sets the value of the 'errors' field. + * @param value The value of 'errors'. + * @return This builder. + */ + public benchmarks.record.generated.AttributeValue.Invalid.Builder setErrors(java.util.List value) { + validate(fields()[2], value); + this.errors = value; + fieldSetFlags()[2] = true; + return this; + } + + /** + * Checks whether the 'errors' field has been set. + * @return True if the 'errors' field has been set, false otherwise. + */ + public boolean hasErrors() { + return fieldSetFlags()[2]; + } + + + /** + * Clears the value of the 'errors' field. + * @return This builder. + */ + public benchmarks.record.generated.AttributeValue.Invalid.Builder clearErrors() { + errors = null; + fieldSetFlags()[2] = false; + return this; + } + + @Override + @SuppressWarnings("unchecked") + public Invalid build() { + try { + Invalid record = new Invalid(); + record.value = fieldSetFlags()[0] ? this.value : (java.lang.CharSequence) defaultValue(fields()[0]); + record.timestamp = fieldSetFlags()[1] ? this.timestamp : (java.time.Instant) defaultValue(fields()[1]); + record.errors = fieldSetFlags()[2] ? this.errors : (java.util.List) defaultValue(fields()[2]); + return record; + } catch (org.apache.avro.AvroMissingFieldException e) { + throw e; + } catch (java.lang.Exception e) { + throw new org.apache.avro.AvroRuntimeException(e); + } + } + } + + @SuppressWarnings("unchecked") + private static final org.apache.avro.io.DatumWriter + WRITER$ = (org.apache.avro.io.DatumWriter)MODEL$.createDatumWriter(SCHEMA$); + + @Override public void writeExternal(java.io.ObjectOutput out) + throws java.io.IOException { + WRITER$.write(this, SpecificData.getEncoder(out)); + } + + @SuppressWarnings("unchecked") + private static final org.apache.avro.io.DatumReader + READER$ = (org.apache.avro.io.DatumReader)MODEL$.createDatumReader(SCHEMA$); + + @Override public void readExternal(java.io.ObjectInput in) + throws java.io.IOException { + READER$.read(this, SpecificData.getDecoder(in)); + } + +} diff --git a/benchmarks/src/main/java/benchmarks/record/generated/AttributeValue/ValidInt.java b/benchmarks/src/main/java/benchmarks/record/generated/AttributeValue/ValidInt.java new file mode 100644 index 00000000..b0885d55 --- /dev/null +++ b/benchmarks/src/main/java/benchmarks/record/generated/AttributeValue/ValidInt.java @@ -0,0 +1,356 @@ +/** + * Autogenerated by Avro + * + * DO NOT EDIT DIRECTLY + */ +package benchmarks.record.generated.AttributeValue; + +import org.apache.avro.generic.GenericArray; +import org.apache.avro.specific.SpecificData; +import org.apache.avro.util.Utf8; +import org.apache.avro.message.BinaryMessageEncoder; +import org.apache.avro.message.BinaryMessageDecoder; +import org.apache.avro.message.SchemaStore; + +@org.apache.avro.specific.AvroGenerated +public class ValidInt extends org.apache.avro.specific.SpecificRecordBase implements org.apache.avro.specific.SpecificRecord { + private static final long serialVersionUID = -7538044359318399188L; + public static final org.apache.avro.Schema SCHEMA$ = new org.apache.avro.Schema.Parser().parse("{\"type\":\"record\",\"name\":\"ValidInt\",\"namespace\":\"benchmarks.record.generated.AttributeValue\",\"fields\":[{\"name\":\"value\",\"type\":\"int\"},{\"name\":\"timestamp\",\"type\":{\"type\":\"long\",\"logicalType\":\"timestamp-millis\"}}]}"); + public static org.apache.avro.Schema getClassSchema() { return SCHEMA$; } + + private static SpecificData MODEL$ = new SpecificData(); +static { + MODEL$.addLogicalTypeConversion(new org.apache.avro.data.TimeConversions.TimestampMillisConversion()); + } + + private static final BinaryMessageEncoder ENCODER = + new BinaryMessageEncoder(MODEL$, SCHEMA$); + + private static final BinaryMessageDecoder DECODER = + new BinaryMessageDecoder(MODEL$, SCHEMA$); + + /** + * Return the BinaryMessageEncoder instance used by this class. + * @return the message encoder used by this class + */ + public static BinaryMessageEncoder getEncoder() { + return ENCODER; + } + + /** + * Return the BinaryMessageDecoder instance used by this class. + * @return the message decoder used by this class + */ + public static BinaryMessageDecoder getDecoder() { + return DECODER; + } + + /** + * Create a new BinaryMessageDecoder instance for this class that uses the specified {@link SchemaStore}. + * @param resolver a {@link SchemaStore} used to find schemas by fingerprint + * @return a BinaryMessageDecoder instance for this class backed by the given SchemaStore + */ + public static BinaryMessageDecoder createDecoder(SchemaStore resolver) { + return new BinaryMessageDecoder(MODEL$, SCHEMA$, resolver); + } + + /** + * Serializes this ValidInt to a ByteBuffer. + * @return a buffer holding the serialized data for this instance + * @throws java.io.IOException if this instance could not be serialized + */ + public java.nio.ByteBuffer toByteBuffer() throws java.io.IOException { + return ENCODER.encode(this); + } + + /** + * Deserializes a ValidInt from a ByteBuffer. + * @param b a byte buffer holding serialized data for an instance of this class + * @return a ValidInt instance decoded from the given buffer + * @throws java.io.IOException if the given bytes could not be deserialized into an instance of this class + */ + public static ValidInt fromByteBuffer( + java.nio.ByteBuffer b) throws java.io.IOException { + return DECODER.decode(b); + } + + private int value; + private java.time.Instant timestamp; + + /** + * Default constructor. Note that this does not initialize fields + * to their default values from the schema. If that is desired then + * one should use newBuilder(). + */ + public ValidInt() {} + + /** + * All-args constructor. + * @param value The new value for value + * @param timestamp The new value for timestamp + */ + public ValidInt(java.lang.Integer value, java.time.Instant timestamp) { + this.value = value; + this.timestamp = timestamp.truncatedTo(java.time.temporal.ChronoUnit.MILLIS); + } + + public org.apache.avro.specific.SpecificData getSpecificData() { return MODEL$; } + public org.apache.avro.Schema getSchema() { return SCHEMA$; } + // Used by DatumWriter. Applications should not call. + public java.lang.Object get(int field$) { + switch (field$) { + case 0: return value; + case 1: return timestamp; + default: throw new org.apache.avro.AvroRuntimeException("Bad index"); + } + } + + private static final org.apache.avro.Conversion[] conversions = + new org.apache.avro.Conversion[] { + null, + new org.apache.avro.data.TimeConversions.TimestampMillisConversion(), + null + }; + + @Override + public org.apache.avro.Conversion getConversion(int field) { + return conversions[field]; + } + + // Used by DatumReader. Applications should not call. + @SuppressWarnings(value="unchecked") + public void put(int field$, java.lang.Object value$) { + switch (field$) { + case 0: value = (java.lang.Integer)value$; break; + case 1: timestamp = (java.time.Instant)value$; break; + default: throw new org.apache.avro.AvroRuntimeException("Bad index"); + } + } + + /** + * Gets the value of the 'value' field. + * @return The value of the 'value' field. + */ + public int getValue() { + return value; + } + + + /** + * Sets the value of the 'value' field. + * @param value the value to set. + */ + public void setValue(int value) { + this.value = value; + } + + /** + * Gets the value of the 'timestamp' field. + * @return The value of the 'timestamp' field. + */ + public java.time.Instant getTimestamp() { + return timestamp; + } + + + /** + * Sets the value of the 'timestamp' field. + * @param value the value to set. + */ + public void setTimestamp(java.time.Instant value) { + this.timestamp = value.truncatedTo(java.time.temporal.ChronoUnit.MILLIS); + } + + /** + * Creates a new ValidInt RecordBuilder. + * @return A new ValidInt RecordBuilder + */ + public static benchmarks.record.generated.AttributeValue.ValidInt.Builder newBuilder() { + return new benchmarks.record.generated.AttributeValue.ValidInt.Builder(); + } + + /** + * Creates a new ValidInt RecordBuilder by copying an existing Builder. + * @param other The existing builder to copy. + * @return A new ValidInt RecordBuilder + */ + public static benchmarks.record.generated.AttributeValue.ValidInt.Builder newBuilder(benchmarks.record.generated.AttributeValue.ValidInt.Builder other) { + if (other == null) { + return new benchmarks.record.generated.AttributeValue.ValidInt.Builder(); + } else { + return new benchmarks.record.generated.AttributeValue.ValidInt.Builder(other); + } + } + + /** + * Creates a new ValidInt RecordBuilder by copying an existing ValidInt instance. + * @param other The existing instance to copy. + * @return A new ValidInt RecordBuilder + */ + public static benchmarks.record.generated.AttributeValue.ValidInt.Builder newBuilder(benchmarks.record.generated.AttributeValue.ValidInt other) { + if (other == null) { + return new benchmarks.record.generated.AttributeValue.ValidInt.Builder(); + } else { + return new benchmarks.record.generated.AttributeValue.ValidInt.Builder(other); + } + } + + /** + * RecordBuilder for ValidInt instances. + */ + public static class Builder extends org.apache.avro.specific.SpecificRecordBuilderBase + implements org.apache.avro.data.RecordBuilder { + + private int value; + private java.time.Instant timestamp; + + /** Creates a new Builder */ + private Builder() { + super(SCHEMA$); + } + + /** + * Creates a Builder by copying an existing Builder. + * @param other The existing Builder to copy. + */ + private Builder(benchmarks.record.generated.AttributeValue.ValidInt.Builder other) { + super(other); + if (isValidValue(fields()[0], other.value)) { + this.value = data().deepCopy(fields()[0].schema(), other.value); + fieldSetFlags()[0] = other.fieldSetFlags()[0]; + } + if (isValidValue(fields()[1], other.timestamp)) { + this.timestamp = data().deepCopy(fields()[1].schema(), other.timestamp); + fieldSetFlags()[1] = other.fieldSetFlags()[1]; + } + } + + /** + * Creates a Builder by copying an existing ValidInt instance + * @param other The existing instance to copy. + */ + private Builder(benchmarks.record.generated.AttributeValue.ValidInt other) { + super(SCHEMA$); + if (isValidValue(fields()[0], other.value)) { + this.value = data().deepCopy(fields()[0].schema(), other.value); + fieldSetFlags()[0] = true; + } + if (isValidValue(fields()[1], other.timestamp)) { + this.timestamp = data().deepCopy(fields()[1].schema(), other.timestamp); + fieldSetFlags()[1] = true; + } + } + + /** + * Gets the value of the 'value' field. + * @return The value. + */ + public int getValue() { + return value; + } + + + /** + * Sets the value of the 'value' field. + * @param value The value of 'value'. + * @return This builder. + */ + public benchmarks.record.generated.AttributeValue.ValidInt.Builder setValue(int value) { + validate(fields()[0], value); + this.value = value; + fieldSetFlags()[0] = true; + return this; + } + + /** + * Checks whether the 'value' field has been set. + * @return True if the 'value' field has been set, false otherwise. + */ + public boolean hasValue() { + return fieldSetFlags()[0]; + } + + + /** + * Clears the value of the 'value' field. + * @return This builder. + */ + public benchmarks.record.generated.AttributeValue.ValidInt.Builder clearValue() { + fieldSetFlags()[0] = false; + return this; + } + + /** + * Gets the value of the 'timestamp' field. + * @return The value. + */ + public java.time.Instant getTimestamp() { + return timestamp; + } + + + /** + * Sets the value of the 'timestamp' field. + * @param value The value of 'timestamp'. + * @return This builder. + */ + public benchmarks.record.generated.AttributeValue.ValidInt.Builder setTimestamp(java.time.Instant value) { + validate(fields()[1], value); + this.timestamp = value.truncatedTo(java.time.temporal.ChronoUnit.MILLIS); + fieldSetFlags()[1] = true; + return this; + } + + /** + * Checks whether the 'timestamp' field has been set. + * @return True if the 'timestamp' field has been set, false otherwise. + */ + public boolean hasTimestamp() { + return fieldSetFlags()[1]; + } + + + /** + * Clears the value of the 'timestamp' field. + * @return This builder. + */ + public benchmarks.record.generated.AttributeValue.ValidInt.Builder clearTimestamp() { + fieldSetFlags()[1] = false; + return this; + } + + @Override + @SuppressWarnings("unchecked") + public ValidInt build() { + try { + ValidInt record = new ValidInt(); + record.value = fieldSetFlags()[0] ? this.value : (java.lang.Integer) defaultValue(fields()[0]); + record.timestamp = fieldSetFlags()[1] ? this.timestamp : (java.time.Instant) defaultValue(fields()[1]); + return record; + } catch (org.apache.avro.AvroMissingFieldException e) { + throw e; + } catch (java.lang.Exception e) { + throw new org.apache.avro.AvroRuntimeException(e); + } + } + } + + @SuppressWarnings("unchecked") + private static final org.apache.avro.io.DatumWriter + WRITER$ = (org.apache.avro.io.DatumWriter)MODEL$.createDatumWriter(SCHEMA$); + + @Override public void writeExternal(java.io.ObjectOutput out) + throws java.io.IOException { + WRITER$.write(this, SpecificData.getEncoder(out)); + } + + @SuppressWarnings("unchecked") + private static final org.apache.avro.io.DatumReader + READER$ = (org.apache.avro.io.DatumReader)MODEL$.createDatumReader(SCHEMA$); + + @Override public void readExternal(java.io.ObjectInput in) + throws java.io.IOException { + READER$.read(this, SpecificData.getDecoder(in)); + } + +} diff --git a/benchmarks/src/main/java/benchmarks/record/generated/RecordWithUnionAndTypeField.java b/benchmarks/src/main/java/benchmarks/record/generated/RecordWithUnionAndTypeField.java new file mode 100644 index 00000000..8f793524 --- /dev/null +++ b/benchmarks/src/main/java/benchmarks/record/generated/RecordWithUnionAndTypeField.java @@ -0,0 +1,274 @@ +/** + * Autogenerated by Avro + * + * DO NOT EDIT DIRECTLY + */ +package benchmarks.record.generated; + +import org.apache.avro.generic.GenericArray; +import org.apache.avro.specific.SpecificData; +import org.apache.avro.util.Utf8; +import org.apache.avro.message.BinaryMessageEncoder; +import org.apache.avro.message.BinaryMessageDecoder; +import org.apache.avro.message.SchemaStore; + +@org.apache.avro.specific.AvroGenerated +public class RecordWithUnionAndTypeField extends org.apache.avro.specific.SpecificRecordBase implements org.apache.avro.specific.SpecificRecord { + private static final long serialVersionUID = -7529083795954770251L; + public static final org.apache.avro.Schema SCHEMA$ = new org.apache.avro.Schema.Parser().parse("{\"type\":\"record\",\"name\":\"RecordWithUnionAndTypeField\",\"namespace\":\"benchmarks.record.generated\",\"fields\":[{\"name\":\"attribute\",\"type\":[{\"type\":\"record\",\"name\":\"Empty\",\"namespace\":\"benchmarks.record.generated.AttributeValue\",\"fields\":[{\"name\":\"timestamp\",\"type\":{\"type\":\"long\",\"logicalType\":\"timestamp-millis\"}}]},{\"type\":\"record\",\"name\":\"Invalid\",\"namespace\":\"benchmarks.record.generated.AttributeValue\",\"fields\":[{\"name\":\"value\",\"type\":[\"null\",\"string\"]},{\"name\":\"timestamp\",\"type\":{\"type\":\"long\",\"logicalType\":\"timestamp-millis\"}},{\"name\":\"errors\",\"type\":{\"type\":\"array\",\"items\":\"string\"}}]},{\"type\":\"record\",\"name\":\"ValidInt\",\"namespace\":\"benchmarks.record.generated.AttributeValue\",\"fields\":[{\"name\":\"value\",\"type\":\"int\"},{\"name\":\"timestamp\",\"type\":{\"type\":\"long\",\"logicalType\":\"timestamp-millis\"}}]}]}]}"); + public static org.apache.avro.Schema getClassSchema() { return SCHEMA$; } + + private static SpecificData MODEL$ = new SpecificData(); +static { + MODEL$.addLogicalTypeConversion(new org.apache.avro.data.TimeConversions.TimestampMillisConversion()); + } + + private static final BinaryMessageEncoder ENCODER = + new BinaryMessageEncoder(MODEL$, SCHEMA$); + + private static final BinaryMessageDecoder DECODER = + new BinaryMessageDecoder(MODEL$, SCHEMA$); + + /** + * Return the BinaryMessageEncoder instance used by this class. + * @return the message encoder used by this class + */ + public static BinaryMessageEncoder getEncoder() { + return ENCODER; + } + + /** + * Return the BinaryMessageDecoder instance used by this class. + * @return the message decoder used by this class + */ + public static BinaryMessageDecoder getDecoder() { + return DECODER; + } + + /** + * Create a new BinaryMessageDecoder instance for this class that uses the specified {@link SchemaStore}. + * @param resolver a {@link SchemaStore} used to find schemas by fingerprint + * @return a BinaryMessageDecoder instance for this class backed by the given SchemaStore + */ + public static BinaryMessageDecoder createDecoder(SchemaStore resolver) { + return new BinaryMessageDecoder(MODEL$, SCHEMA$, resolver); + } + + /** + * Serializes this RecordWithUnionAndTypeField to a ByteBuffer. + * @return a buffer holding the serialized data for this instance + * @throws java.io.IOException if this instance could not be serialized + */ + public java.nio.ByteBuffer toByteBuffer() throws java.io.IOException { + return ENCODER.encode(this); + } + + /** + * Deserializes a RecordWithUnionAndTypeField from a ByteBuffer. + * @param b a byte buffer holding serialized data for an instance of this class + * @return a RecordWithUnionAndTypeField instance decoded from the given buffer + * @throws java.io.IOException if the given bytes could not be deserialized into an instance of this class + */ + public static RecordWithUnionAndTypeField fromByteBuffer( + java.nio.ByteBuffer b) throws java.io.IOException { + return DECODER.decode(b); + } + + private java.lang.Object attribute; + + /** + * Default constructor. Note that this does not initialize fields + * to their default values from the schema. If that is desired then + * one should use newBuilder(). + */ + public RecordWithUnionAndTypeField() {} + + /** + * All-args constructor. + * @param attribute The new value for attribute + */ + public RecordWithUnionAndTypeField(java.lang.Object attribute) { + this.attribute = attribute; + } + + public org.apache.avro.specific.SpecificData getSpecificData() { return MODEL$; } + public org.apache.avro.Schema getSchema() { return SCHEMA$; } + // Used by DatumWriter. Applications should not call. + public java.lang.Object get(int field$) { + switch (field$) { + case 0: return attribute; + default: throw new org.apache.avro.AvroRuntimeException("Bad index"); + } + } + + // Used by DatumReader. Applications should not call. + @SuppressWarnings(value="unchecked") + public void put(int field$, java.lang.Object value$) { + switch (field$) { + case 0: attribute = value$; break; + default: throw new org.apache.avro.AvroRuntimeException("Bad index"); + } + } + + /** + * Gets the value of the 'attribute' field. + * @return The value of the 'attribute' field. + */ + public java.lang.Object getAttribute() { + return attribute; + } + + + /** + * Sets the value of the 'attribute' field. + * @param value the value to set. + */ + public void setAttribute(java.lang.Object value) { + this.attribute = value; + } + + /** + * Creates a new RecordWithUnionAndTypeField RecordBuilder. + * @return A new RecordWithUnionAndTypeField RecordBuilder + */ + public static benchmarks.record.generated.RecordWithUnionAndTypeField.Builder newBuilder() { + return new benchmarks.record.generated.RecordWithUnionAndTypeField.Builder(); + } + + /** + * Creates a new RecordWithUnionAndTypeField RecordBuilder by copying an existing Builder. + * @param other The existing builder to copy. + * @return A new RecordWithUnionAndTypeField RecordBuilder + */ + public static benchmarks.record.generated.RecordWithUnionAndTypeField.Builder newBuilder(benchmarks.record.generated.RecordWithUnionAndTypeField.Builder other) { + if (other == null) { + return new benchmarks.record.generated.RecordWithUnionAndTypeField.Builder(); + } else { + return new benchmarks.record.generated.RecordWithUnionAndTypeField.Builder(other); + } + } + + /** + * Creates a new RecordWithUnionAndTypeField RecordBuilder by copying an existing RecordWithUnionAndTypeField instance. + * @param other The existing instance to copy. + * @return A new RecordWithUnionAndTypeField RecordBuilder + */ + public static benchmarks.record.generated.RecordWithUnionAndTypeField.Builder newBuilder(benchmarks.record.generated.RecordWithUnionAndTypeField other) { + if (other == null) { + return new benchmarks.record.generated.RecordWithUnionAndTypeField.Builder(); + } else { + return new benchmarks.record.generated.RecordWithUnionAndTypeField.Builder(other); + } + } + + /** + * RecordBuilder for RecordWithUnionAndTypeField instances. + */ + public static class Builder extends org.apache.avro.specific.SpecificRecordBuilderBase + implements org.apache.avro.data.RecordBuilder { + + private java.lang.Object attribute; + + /** Creates a new Builder */ + private Builder() { + super(SCHEMA$); + } + + /** + * Creates a Builder by copying an existing Builder. + * @param other The existing Builder to copy. + */ + private Builder(benchmarks.record.generated.RecordWithUnionAndTypeField.Builder other) { + super(other); + if (isValidValue(fields()[0], other.attribute)) { + this.attribute = data().deepCopy(fields()[0].schema(), other.attribute); + fieldSetFlags()[0] = other.fieldSetFlags()[0]; + } + } + + /** + * Creates a Builder by copying an existing RecordWithUnionAndTypeField instance + * @param other The existing instance to copy. + */ + private Builder(benchmarks.record.generated.RecordWithUnionAndTypeField other) { + super(SCHEMA$); + if (isValidValue(fields()[0], other.attribute)) { + this.attribute = data().deepCopy(fields()[0].schema(), other.attribute); + fieldSetFlags()[0] = true; + } + } + + /** + * Gets the value of the 'attribute' field. + * @return The value. + */ + public java.lang.Object getAttribute() { + return attribute; + } + + + /** + * Sets the value of the 'attribute' field. + * @param value The value of 'attribute'. + * @return This builder. + */ + public benchmarks.record.generated.RecordWithUnionAndTypeField.Builder setAttribute(java.lang.Object value) { + validate(fields()[0], value); + this.attribute = value; + fieldSetFlags()[0] = true; + return this; + } + + /** + * Checks whether the 'attribute' field has been set. + * @return True if the 'attribute' field has been set, false otherwise. + */ + public boolean hasAttribute() { + return fieldSetFlags()[0]; + } + + + /** + * Clears the value of the 'attribute' field. + * @return This builder. + */ + public benchmarks.record.generated.RecordWithUnionAndTypeField.Builder clearAttribute() { + attribute = null; + fieldSetFlags()[0] = false; + return this; + } + + @Override + @SuppressWarnings("unchecked") + public RecordWithUnionAndTypeField build() { + try { + RecordWithUnionAndTypeField record = new RecordWithUnionAndTypeField(); + record.attribute = fieldSetFlags()[0] ? this.attribute : defaultValue(fields()[0]); + return record; + } catch (org.apache.avro.AvroMissingFieldException e) { + throw e; + } catch (java.lang.Exception e) { + throw new org.apache.avro.AvroRuntimeException(e); + } + } + } + + @SuppressWarnings("unchecked") + private static final org.apache.avro.io.DatumWriter + WRITER$ = (org.apache.avro.io.DatumWriter)MODEL$.createDatumWriter(SCHEMA$); + + @Override public void writeExternal(java.io.ObjectOutput out) + throws java.io.IOException { + WRITER$.write(this, SpecificData.getEncoder(out)); + } + + @SuppressWarnings("unchecked") + private static final org.apache.avro.io.DatumReader + READER$ = (org.apache.avro.io.DatumReader)MODEL$.createDatumReader(SCHEMA$); + + @Override public void readExternal(java.io.ObjectInput in) + throws java.io.IOException { + READER$.read(this, SpecificData.getDecoder(in)); + } + +} diff --git a/benchmarks/src/main/scala/benchmarks/BenchmarkHelpers.scala b/benchmarks/src/main/scala/benchmarks/BenchmarkHelpers.scala new file mode 100644 index 00000000..0bb061c5 --- /dev/null +++ b/benchmarks/src/main/scala/benchmarks/BenchmarkHelpers.scala @@ -0,0 +1,15 @@ +package benchmarks + +import java.time.Instant + +trait BenchmarkHelpers { + + val now: Instant = Instant.now + var counter = 0L + + def t: Instant = { + counter += 1 + now.minusSeconds(counter) + } + +} diff --git a/benchmarks/src/main/scala/benchmarks/CommonParameters.scala b/benchmarks/src/main/scala/benchmarks/CommonParameters.scala new file mode 100644 index 00000000..df341918 --- /dev/null +++ b/benchmarks/src/main/scala/benchmarks/CommonParameters.scala @@ -0,0 +1,43 @@ +package benchmarks + +import java.io.InputStream +import java.util +import java.util.concurrent.TimeUnit + +import org.openjdk.jmh.annotations._ + +@State(Scope.Thread) +@Warmup(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS) +@Fork(value = 1, jvmArgs = Array( + "-server", + "-Xms2g", + "-Xmx2g", + "-XX:NewSize=1g", + "-XX:MaxNewSize=1g", + "-XX:InitialCodeCacheSize=512m", + "-XX:ReservedCodeCacheSize=512m", + "-XX:+UseParallelGC", + "-XX:-UseAdaptiveSizePolicy", + "-XX:MaxInlineLevel=18", + // "-XX:-UseBiasedLocking", // Java < 15 + "-XX:+AlwaysPreTouch", + "-XX:+UseNUMA", + "-XX:-UseAdaptiveNUMAChunkSizing" +)) +@BenchmarkMode(Array(Mode.Throughput)) +@OutputTimeUnit(TimeUnit.SECONDS) +abstract class CommonParams { + def bytes(in: InputStream): Array[Byte] = try { + val step = 8192 + var buf = new Array[Byte](step) + var pos, n = 0 + while ({ + if (pos + step > buf.length) buf = util.Arrays.copyOf(buf, buf.length << 1) + n = in.read(buf, pos, step) + n != -1 + }) pos += n + if (pos != buf.length) buf = util.Arrays.copyOf(buf, pos) + buf + } finally in.close() +} diff --git a/benchmarks/src/main/scala/benchmarks/Decoding.scala b/benchmarks/src/main/scala/benchmarks/Decoding.scala new file mode 100644 index 00000000..214d3c33 --- /dev/null +++ b/benchmarks/src/main/scala/benchmarks/Decoding.scala @@ -0,0 +1,54 @@ +package benchmarks + +import cats.syntax.all._ +import benchmarks.record._ +import org.openjdk.jmh.annotations._ +import org.openjdk.jmh.infra.Blackhole +import vulcan.Codec +import vulcan.generic._ +import scala.annotation.nowarn + +object Decoding extends BenchmarkHelpers { + @State(Scope.Thread) + class Setup { + val avroBytes = { + import benchmarks.record.generated.AttributeValue._ + import benchmarks.record.generated._ + new RecordWithUnionAndTypeField(new ValidInt(255, t)).toByteBuffer + } + + @nowarn + implicit private def attributeValueCodec: Codec[AttributeValue[Int]] = + Codec.derive[AttributeValue[Int]] + + implicit val derivedCodec: Codec[RecordWithUnionAndTypeField] = + Codec.derive[RecordWithUnionAndTypeField] + + val schema = derivedCodec.schema.leftMap(_.throwable).toTry.get + + val vulcanBytes = Codec + .toBinary(RecordWithUnionAndTypeField(AttributeValue.Valid[Int](255, t))) + .leftMap(_.throwable) + .toTry + .get + } +} + +class Decoding extends CommonParams with BenchmarkHelpers { + + import Decoding._ + + @Benchmark + def avroSpecificRecord(setup: Setup, blackhole: Blackhole) = { + import benchmarks.record.generated._ + blackhole.consume(RecordWithUnionAndTypeField.fromByteBuffer(setup.avroBytes.duplicate)) + } + + @Benchmark + def vulcanGenerated(setup: Setup, blackhole: Blackhole) = + blackhole.consume( + Codec.fromBinary[RecordWithUnionAndTypeField](setup.vulcanBytes, setup.schema)( + setup.derivedCodec + ) + ) +} diff --git a/benchmarks/src/main/scala/benchmarks/Encoding.scala b/benchmarks/src/main/scala/benchmarks/Encoding.scala new file mode 100644 index 00000000..fbc22d41 --- /dev/null +++ b/benchmarks/src/main/scala/benchmarks/Encoding.scala @@ -0,0 +1,42 @@ +package benchmarks + +import cats.implicits._ +import benchmarks.record._ +import org.openjdk.jmh.annotations._ +import org.openjdk.jmh.infra.Blackhole +import vulcan.Codec +import vulcan.generic._ + +object Encoding extends BenchmarkHelpers { + + @State(Scope.Thread) + class Setup { + val record = RecordWithUnionAndTypeField(AttributeValue.Valid[Int](255, t)) + + val specificRecord = { + import benchmarks.record.generated.AttributeValue._ + import benchmarks.record.generated._ + new RecordWithUnionAndTypeField(new ValidInt(255, t)) + } + + // @nowarn + implicit def attributeValueCodec: Codec[AttributeValue[Int]] = Codec.derive[AttributeValue[Int]] + implicit val derivedCodec: Codec[RecordWithUnionAndTypeField] = + Codec.derive[RecordWithUnionAndTypeField] + + val schema = derivedCodec.schema.leftMap(_.throwable).toTry.get + } +} + +class Encoding extends CommonParams with BenchmarkHelpers { + + import Encoding._ + + @Benchmark + def avroSpecificRecord(setup: Setup, blackhole: Blackhole) = + blackhole.consume(setup.specificRecord.toByteBuffer) + + @Benchmark + def vulcanGenerated(setup: Setup, blackhole: Blackhole) = + blackhole.consume(Codec.toBinary(setup.record)(setup.derivedCodec)) +} diff --git a/benchmarks/src/main/scala/benchmarks/record/Attributes.scala b/benchmarks/src/main/scala/benchmarks/record/Attributes.scala new file mode 100644 index 00000000..c75e3166 --- /dev/null +++ b/benchmarks/src/main/scala/benchmarks/record/Attributes.scala @@ -0,0 +1,31 @@ +package benchmarks.record + +import java.time.Instant + +sealed trait IntAttributeValue { + def timestamp: Instant +} + +object IntAttributeValue { + final case class Empty(timestamp: Instant) extends IntAttributeValue + + final case class Invalid(value: Option[String], timestamp: Instant, errors: Set[String]) + extends IntAttributeValue + + final case class Valid(value: Int, timestamp: Instant) extends IntAttributeValue +} + +sealed trait AttributeValue[+A] { + def timestamp: Instant +} + +object AttributeValue { + + final case class Empty(timestamp: Instant) extends AttributeValue[Nothing] + + final case class Invalid(value: Option[String], timestamp: Instant, errors: Set[String]) + extends AttributeValue[Nothing] + + final case class Valid[A](value: A, timestamp: Instant) extends AttributeValue[A] + +} diff --git a/benchmarks/src/main/scala/benchmarks/record/Records.scala b/benchmarks/src/main/scala/benchmarks/record/Records.scala new file mode 100644 index 00000000..8b99ad84 --- /dev/null +++ b/benchmarks/src/main/scala/benchmarks/record/Records.scala @@ -0,0 +1,9 @@ +package benchmarks.record + +case class RecordWithSimpleField(attribute: IntAttributeValue.Valid) + +case class RecordWithUnionField(attribute: IntAttributeValue) + +case class RecordWithTypeParamField(attribute: AttributeValue.Valid[Int]) + +case class RecordWithUnionAndTypeField(attribute: AttributeValue[Int]) diff --git a/build.sbt b/build.sbt index 2a945646..e0d0b44b 100644 --- a/build.sbt +++ b/build.sbt @@ -11,6 +11,8 @@ val scalaCollectionCompatVersion = "2.9.0" val shapeless3Version = "3.3.0" val shapelessVersion = "2.3.10" val slf4jNopVersion = "2.0.7" +val sbtJmhVersion = "0.3.7" +val jmhVersion = "1.32" val scala212 = "2.12.17" val scala213 = "2.13.10" @@ -135,6 +137,27 @@ lazy val docs = project .dependsOn(core, enumeratum, generic, refined) .enablePlugins(BuildInfoPlugin, DocusaurusPlugin, MdocPlugin, ScalaUnidocPlugin) +lazy val benchmarks = project + .in(file("benchmarks")) + .dependsOn(core, generic) + .enablePlugins(JmhPlugin) + .settings( + Test / fork := true, + publishArtifact := false, + scalaSettings ++ Seq( + crossScalaVersions += scala3 + ), + dependencySettings ++ Seq( + libraryDependencies ++= Seq( + "pl.project13.scala" % "sbt-jmh-extras" % sbtJmhVersion, + "org.openjdk.jmh" % "jmh-core" % jmhVersion, + "org.openjdk.jmh" % "jmh-generator-asm" % jmhVersion, + "org.openjdk.jmh" % "jmh-generator-bytecode" % jmhVersion, + "org.openjdk.jmh" % "jmh-generator-reflection" % jmhVersion + ) + ) + ) + lazy val dependencySettings = Seq( libraryDependencies ++= { if (scalaVersion.value.startsWith("3")) Nil diff --git a/project/plugins.sbt b/project/plugins.sbt index 95c45894..ffc4b8ce 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -5,3 +5,4 @@ addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "1.1.2") addSbtPlugin("de.heikoseeberger" % "sbt-header" % "5.9.0") addSbtPlugin("org.scalameta" % "sbt-mdoc" % "2.3.7") addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.0") +addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.5") diff --git a/run_benchmarks.sh b/run_benchmarks.sh new file mode 100755 index 00000000..00a965df --- /dev/null +++ b/run_benchmarks.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +sbt benchmarks/jmh:run