/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.flink.benchmark; import org.apache.flink.api.common.ExecutionConfig; import org.apache.flink.api.common.typeinfo.TypeInformation; import org.apache.flink.api.common.typeinfo.Types; import org.apache.flink.api.java.tuple.Tuple2; import org.apache.flink.api.java.tuple.Tuple8; import org.apache.flink.api.java.typeutils.ResultTypeQueryable; import org.apache.flink.benchmark.full.StringSerializationBenchmark; import org.apache.flink.benchmark.functions.BaseSourceWithKeyRange; import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment; import org.apache.flink.streaming.api.functions.sink.DiscardingSink; import org.apache.flink.types.Row; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.OperationsPerInvocation; import org.openjdk.jmh.runner.Runner; import org.openjdk.jmh.runner.RunnerException; import org.openjdk.jmh.runner.options.Options; import org.openjdk.jmh.runner.options.OptionsBuilder; import org.openjdk.jmh.runner.options.VerboseMode; import java.util.Arrays; import java.util.Random; import java.util.concurrent.ThreadLocalRandom; /** * Benchmark for serializing POJOs and Tuples with different serialization frameworks. */ public class SerializationFrameworkMiniBenchmarks extends BenchmarkBase { protected static final int RECORDS_PER_INVOCATION = 300_000; public static void main(String[] args) throws RunnerException { Options options = new OptionsBuilder() .verbosity(VerboseMode.NORMAL) .include(".*" + SerializationFrameworkMiniBenchmarks.class.getCanonicalName() + ".*") .build(); new Runner(options).run(); } @Benchmark @OperationsPerInvocation(value = SerializationFrameworkMiniBenchmarks.RECORDS_PER_INVOCATION) public void serializerPojo(FlinkEnvironmentContext context) throws Exception { StreamExecutionEnvironment env = context.env; env.setParallelism(4); ExecutionConfig executionConfig = env.getConfig(); executionConfig.registerPojoType(MyPojo.class); executionConfig.registerPojoType(MyOperation.class); env.addSource(new PojoSource(RECORDS_PER_INVOCATION, 10)) .rebalance() .addSink(new DiscardingSink<>()); env.execute(); } @Benchmark @OperationsPerInvocation(value = SerializationFrameworkMiniBenchmarks.RECORDS_PER_INVOCATION) public void serializerHeavyString(FlinkEnvironmentContext context) throws Exception { StreamExecutionEnvironment env = context.env; env.setParallelism(1); ExecutionConfig executionConfig = env.getConfig(); executionConfig.registerPojoType(MyPojo.class); executionConfig.registerPojoType(MyOperation.class); env.addSource(new LongStringSource(RECORDS_PER_INVOCATION, 12)) .rebalance() .addSink(new DiscardingSink<>()); env.execute(); } @Benchmark @OperationsPerInvocation(value = SerializationFrameworkMiniBenchmarks.RECORDS_PER_INVOCATION) public void serializerTuple(FlinkEnvironmentContext context) throws Exception { StreamExecutionEnvironment env = context.env; env.setParallelism(4); env.addSource(new TupleSource(RECORDS_PER_INVOCATION, 10)) .rebalance() .addSink(new DiscardingSink<>()); env.execute(); } @Benchmark @OperationsPerInvocation(value = SerializationFrameworkMiniBenchmarks.RECORDS_PER_INVOCATION) public void serializerKryo(FlinkEnvironmentContext context) throws Exception { StreamExecutionEnvironment env = context.env; env.setParallelism(4); ExecutionConfig executionConfig = env.getConfig(); executionConfig.enableForceKryo(); executionConfig.registerKryoType(MyPojo.class); executionConfig.registerKryoType(MyOperation.class); env.addSource(new PojoSource(RECORDS_PER_INVOCATION, 10)) .rebalance() .addSink(new DiscardingSink<>()); env.execute(); } @Benchmark @OperationsPerInvocation(value = SerializationFrameworkMiniBenchmarks.RECORDS_PER_INVOCATION) public void serializerAvro(FlinkEnvironmentContext context) throws Exception { StreamExecutionEnvironment env = context.env; env.setParallelism(4); env.addSource(new AvroPojoSource(RECORDS_PER_INVOCATION, 10)) .rebalance() .addSink(new DiscardingSink<>()); env.execute(); } @Benchmark @OperationsPerInvocation(value = SerializationFrameworkMiniBenchmarks.RECORDS_PER_INVOCATION) public void serializerRow(FlinkEnvironmentContext context) throws Exception { StreamExecutionEnvironment env = context.env; env.setParallelism(4); env.addSource(new RowSource(RECORDS_PER_INVOCATION, 10)) .rebalance() .addSink(new DiscardingSink<>()); env.execute(); } /** * Source emitting a long String. */ public static class LongStringSource extends BaseSourceWithKeyRange<String> { private static final long serialVersionUID = 3746240885982877398L; private String[] templates; public LongStringSource(int numEvents, int numKeys) { super(numEvents, numKeys); } @Override protected void init() { super.init(); templates = new String[] { makeString(StringSerializationBenchmark.asciiChars, 1024), makeString(StringSerializationBenchmark.russianChars, 1024), makeString(StringSerializationBenchmark.chineseChars, 1024) }; } private String makeString(char[] symbols, int length) { char[] buffer = new char[length]; Random random = ThreadLocalRandom.current(); Arrays.fill(buffer, symbols[random.nextInt(symbols.length)]); return new String(buffer); } @Override protected String getElement(int keyId) { return templates[keyId % templates.length]; } } /** * Source emitting a simple {@link MyPojo POJO}. */ public static class PojoSource extends BaseSourceWithKeyRange<MyPojo> { private static final long serialVersionUID = 2941333602938145526L; private transient MyPojo template; public PojoSource(int numEvents, int numKeys) { super(numEvents, numKeys); } @Override protected void init() { super.init(); template = new MyPojo( 0, "myName", new String[] {"op1", "op2", "op3", "op4"}, new MyOperation[] { new MyOperation(1, "op1"), new MyOperation(2, "op2"), new MyOperation(3, "op3")}, 1, 2, 3, "null"); } @Override protected MyPojo getElement(int keyId) { template.setId(keyId); return template; } } /** * Source emitting a {@link org.apache.flink.benchmark.avro.MyPojo POJO} generated by an Avro schema. */ public static class AvroPojoSource extends BaseSourceWithKeyRange<org.apache.flink.benchmark.avro.MyPojo> { private static final long serialVersionUID = 2941333602938145526L; private transient org.apache.flink.benchmark.avro.MyPojo template; public AvroPojoSource(int numEvents, int numKeys) { super(numEvents, numKeys); } @Override protected void init() { super.init(); template = new org.apache.flink.benchmark.avro.MyPojo( 0, "myName", Arrays.asList("op1", "op2", "op3", "op4"), Arrays.asList( new org.apache.flink.benchmark.avro.MyOperation(1, "op1"), new org.apache.flink.benchmark.avro.MyOperation(2, "op2"), new org.apache.flink.benchmark.avro.MyOperation(3, "op3")), 1, 2, 3, "null"); } @Override protected org.apache.flink.benchmark.avro.MyPojo getElement(int keyId) { template.setId(keyId); return template; } } /** * Source emitting a <tt>Tuple</tt> based on {@link MyPojo}. */ public static class TupleSource extends BaseSourceWithKeyRange<Tuple8<Integer, String, String[], Tuple2<Integer, String>[], Integer, Integer, Integer, Object>> { private static final long serialVersionUID = 2941333602938145526L; private transient Tuple8 template; public TupleSource(int numEvents, int numKeys) { super(numEvents, numKeys); } @SuppressWarnings("unchecked") @Override protected void init() { super.init(); template = MyPojo.createTuple( 0, "myName", new String[] {"op1", "op2", "op3", "op4"}, new Tuple2[] { MyOperation.createTuple(1, "op1"), MyOperation.createTuple(2, "op2"), MyOperation.createTuple(3, "op3")}, 1, 2, 3, "null"); } @Override protected Tuple8<Integer, String, String[], Tuple2<Integer, String>[], Integer, Integer, Integer, Object> getElement(int keyId) { template.setField(keyId, 0); return template; } } /** * Source emitting a {@link Row} based on {@link MyPojo}. */ public static class RowSource extends BaseSourceWithKeyRange<Row> implements ResultTypeQueryable<Row> { private static final long serialVersionUID = 2941333602938145526L; private transient Row template; public RowSource(int numEvents, int numKeys) { super(numEvents, numKeys); } @SuppressWarnings("unchecked") @Override protected void init() { super.init(); template = MyPojo.createRow( 0, "myName", new String[] {"op1", "op2", "op3", "op4"}, new Row[] { MyOperation.createRow(1, "op1"), MyOperation.createRow(2, "op2"), MyOperation.createRow(3, "op3")}, 1, 2, 3, "null"); } @Override protected Row getElement(int keyId) { template.setField(0, keyId); return template; } @Override public TypeInformation<Row> getProducedType() { return MyPojo.getProducedRowType(); } } /** * Not so simple POJO. */ @SuppressWarnings({"WeakerAccess", "unused"}) public static class MyPojo { public int id; private String name; private String[] operationNames; private MyOperation[] operations; private int otherId1; private int otherId2; private int otherId3; private Object someObject; public MyPojo() { } public MyPojo( int id, String name, String[] operationNames, MyOperation[] operations, int otherId1, int otherId2, int otherId3, Object someObject) { this.id = id; this.name = name; this.operationNames = operationNames; this.operations = operations; this.otherId1 = otherId1; this.otherId2 = otherId2; this.otherId3 = otherId3; this.someObject = someObject; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String[] getOperationNames() { return operationNames; } public void setOperationNames(String[] operationNames) { this.operationNames = operationNames; } public MyOperation[] getOperations() { return operations; } public void setOperations( MyOperation[] operations) { this.operations = operations; } public int getOtherId1() { return otherId1; } public void setOtherId1(int otherId1) { this.otherId1 = otherId1; } public int getOtherId2() { return otherId2; } public void setOtherId2(int otherId2) { this.otherId2 = otherId2; } public int getOtherId3() { return otherId3; } public void setOtherId3(int otherId3) { this.otherId3 = otherId3; } public Object getSomeObject() { return someObject; } public void setSomeObject(Object someObject) { this.someObject = someObject; } public static Tuple8<Integer, String, String[], Tuple2<Integer, String>[], Integer, Integer, Integer, Object> createTuple( int id, String name, String[] operationNames, Tuple2<Integer, String>[] operations, int otherId1, int otherId2, int otherId3, Object someObject) { return Tuple8.of(id, name, operationNames, operations, otherId1, otherId2, otherId3, someObject); } public static Row createRow( int id, String name, String[] operationNames, Row[] operations, int otherId1, int otherId2, int otherId3, Object someObject) { return Row.of(id, name, operationNames, operations, otherId1, otherId2, otherId3, someObject); } public static TypeInformation<Row> getProducedRowType() { return Types.ROW( Types.INT, Types.STRING, Types.OBJECT_ARRAY(Types.STRING), Types.OBJECT_ARRAY(Types.ROW(Types.INT, Types.STRING)), Types.INT, Types.INT, Types.INT, Types.GENERIC(Object.class) ); } } /** * Another POJO. */ @SuppressWarnings({"WeakerAccess", "unused"}) public static class MyOperation { int id; protected String name; public MyOperation() { } public MyOperation(int id, String name) { this.id = id; this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public static Tuple2<Integer, String> createTuple(int id, String name) { return Tuple2.of(id, name); } public static Row createRow(int id, String name) { return Row.of(id, name); } } }