blob: 2a33c76ebaf4113152694798309ef36b91dfe1e3 [file] [log] [blame]
/*
* 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.beam.sdk.jmh.util;
import java.util.List;
import org.apache.beam.sdk.util.ByteStringOutputStream;
import org.apache.beam.vendor.grpc.v1p43p2.com.google.protobuf.ByteString;
import org.apache.beam.vendor.grpc.v1p43p2.com.google.protobuf.UnsafeByteOperations;
import org.apache.beam.vendor.guava.v26_0_jre.com.google.common.base.Splitter;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.TearDown;
import org.openjdk.jmh.infra.Blackhole;
/** Benchmarks for {@link ByteStringOutputStream}. */
public class ByteStringOutputStreamBenchmark {
private static final int MANY_WRITES = 10_000;
private static final int FEW_WRITES = 5;
private static final byte[] LARGE_BUFFER = new byte[1000];
private static final byte[] SMALL_BUFFER = new byte[20];
@State(Scope.Thread)
public static class ProtobufByteStringOutputStream {
final ByteString.Output output = ByteString.newOutput();
@TearDown
public void tearDown() throws Exception {
output.close();
}
}
@State(Scope.Thread)
public static class SdkCoreByteStringOutputStream {
final ByteStringOutputStream output = new ByteStringOutputStream();
@TearDown
public void tearDown() throws Exception {
output.close();
}
}
@Benchmark
public void testSdkCoreByteStringOutputStreamManyMixedWritesWithoutReuse() throws Exception {
ByteStringOutputStream output = new ByteStringOutputStream();
for (int i = 0; i < MANY_WRITES; i++) {
output.write(1);
output.write(SMALL_BUFFER);
output.write(1);
output.write(LARGE_BUFFER);
output.write(1);
output.write(SMALL_BUFFER);
output.write(1);
}
if (output.toByteString().size()
!= (4 + 2 * SMALL_BUFFER.length + LARGE_BUFFER.length) * MANY_WRITES) {
throw new IllegalArgumentException();
}
output.close();
}
@Benchmark
public void testSdkCoreByteStringOutputStreamFewMixedWritesWithoutReuse() throws Exception {
ByteStringOutputStream output = new ByteStringOutputStream();
for (int i = 0; i < FEW_WRITES; i++) {
output.write(1);
output.write(SMALL_BUFFER);
output.write(1);
output.write(LARGE_BUFFER);
output.write(1);
output.write(SMALL_BUFFER);
output.write(1);
}
if (output.toByteString().size()
!= (4 + 2 * SMALL_BUFFER.length + LARGE_BUFFER.length) * FEW_WRITES) {
throw new IllegalArgumentException();
}
output.close();
}
@Benchmark
public void testProtobufByteStringOutputStreamManyMixedWritesWithoutReuse() throws Exception {
ByteString.Output output = ByteString.newOutput();
for (int i = 0; i < MANY_WRITES; i++) {
output.write(1);
output.write(SMALL_BUFFER);
output.write(1);
output.write(LARGE_BUFFER);
output.write(1);
output.write(SMALL_BUFFER);
output.write(1);
}
if (output.toByteString().size()
!= (4 + 2 * SMALL_BUFFER.length + LARGE_BUFFER.length) * MANY_WRITES) {
throw new IllegalArgumentException();
}
output.close();
}
@Benchmark
public void testProtobufByteStringOutputStreamFewMixedWritesWithoutReuse() throws Exception {
ByteString.Output output = ByteString.newOutput();
for (int i = 0; i < FEW_WRITES; i++) {
output.write(1);
output.write(SMALL_BUFFER);
output.write(1);
output.write(LARGE_BUFFER);
output.write(1);
output.write(SMALL_BUFFER);
output.write(1);
}
if (output.toByteString().size()
!= (4 + 2 * SMALL_BUFFER.length + LARGE_BUFFER.length) * FEW_WRITES) {
throw new IllegalArgumentException();
}
output.close();
}
@Benchmark
public void testProtobufByteStringOutputStreamManyTinyWrites() throws Exception {
ByteString.Output output = ByteString.newOutput();
for (int i = 0; i < MANY_WRITES; ++i) {
output.write(1);
}
if (output.toByteString().size() != MANY_WRITES) {
throw new IllegalArgumentException();
}
output.close();
}
@Benchmark
public void testProtobufByteStringOutputStreamManySmallWrites() throws Exception {
ByteString.Output output = ByteString.newOutput();
for (int i = 0; i < MANY_WRITES; ++i) {
output.write(SMALL_BUFFER);
}
if (output.toByteString().size() != MANY_WRITES * SMALL_BUFFER.length) {
throw new IllegalArgumentException();
}
output.close();
}
@Benchmark
public void testProtobufByteStringOutputStreamManyLargeWrites() throws Exception {
ByteString.Output output = ByteString.newOutput();
for (int i = 0; i < MANY_WRITES; ++i) {
output.write(LARGE_BUFFER);
}
if (output.toByteString().size() != MANY_WRITES * LARGE_BUFFER.length) {
throw new IllegalArgumentException();
}
output.close();
}
@Benchmark
public void testProtobufByteStringOutputStreamFewTinyWrites() throws Exception {
ByteString.Output output = ByteString.newOutput();
for (int i = 0; i < FEW_WRITES; ++i) {
output.write(1);
}
if (output.toByteString().size() != FEW_WRITES) {
throw new IllegalArgumentException();
}
output.close();
}
@Benchmark
public void testProtobufByteStringOutputStreamFewSmallWrites() throws Exception {
ByteString.Output output = ByteString.newOutput();
for (int i = 0; i < FEW_WRITES; ++i) {
output.write(SMALL_BUFFER);
}
if (output.toByteString().size() != FEW_WRITES * SMALL_BUFFER.length) {
throw new IllegalArgumentException();
}
output.close();
}
@Benchmark
public void testProtobufByteStringOutputStreamFewLargeWrites() throws Exception {
ByteString.Output output = ByteString.newOutput();
for (int i = 0; i < FEW_WRITES; ++i) {
output.write(LARGE_BUFFER);
}
if (output.toByteString().size() != FEW_WRITES * LARGE_BUFFER.length) {
throw new IllegalArgumentException();
}
output.close();
}
@Benchmark
public void testProtobufByteStringOutputStreamManyMixedWritesWithReuse(
ProtobufByteStringOutputStream state) throws Exception {
ByteString.Output output = state.output;
for (int i = 0; i < 9850; i++) {
output.write(1);
output.write(SMALL_BUFFER);
output.write(1);
output.write(LARGE_BUFFER);
output.write(1);
output.write(SMALL_BUFFER);
output.write(1);
}
if (output.toByteString().size()
!= (4 + 2 * SMALL_BUFFER.length + LARGE_BUFFER.length) * 9850) {
throw new IllegalArgumentException();
}
output.reset();
}
@Benchmark
public void testProtobufByteStringOutputStreamFewMixedWritesWithReuse(
ProtobufByteStringOutputStream state) throws Exception {
ByteString.Output output = state.output;
for (int i = 0; i < FEW_WRITES; i++) {
output.write(1);
output.write(SMALL_BUFFER);
output.write(1);
output.write(LARGE_BUFFER);
output.write(1);
output.write(SMALL_BUFFER);
output.write(1);
}
if (output.toByteString().size()
!= (4 + 2 * SMALL_BUFFER.length + LARGE_BUFFER.length) * FEW_WRITES) {
throw new IllegalArgumentException();
}
output.reset();
}
@Benchmark
public void testSdkCoreByteStringOutputStreamManyTinyWrites() throws Exception {
ByteStringOutputStream output = new ByteStringOutputStream();
for (int i = 0; i < MANY_WRITES; ++i) {
output.write(1);
}
if (output.toByteString().size() != MANY_WRITES) {
throw new IllegalArgumentException();
}
output.close();
}
@Benchmark
public void testSdkCoreByteStringOutputStreamManySmallWrites() throws Exception {
ByteStringOutputStream output = new ByteStringOutputStream();
for (int i = 0; i < MANY_WRITES; ++i) {
output.write(SMALL_BUFFER);
}
if (output.toByteString().size() != MANY_WRITES * SMALL_BUFFER.length) {
throw new IllegalArgumentException();
}
output.close();
}
@Benchmark
public void testSdkCoreByteStringOutputStreamManyLargeWrites() throws Exception {
ByteStringOutputStream output = new ByteStringOutputStream();
for (int i = 0; i < MANY_WRITES; ++i) {
output.write(LARGE_BUFFER);
}
if (output.toByteString().size() != MANY_WRITES * LARGE_BUFFER.length) {
throw new IllegalArgumentException();
}
output.close();
}
@Benchmark
public void testSdkCoreByteStringOutputStreamFewTinyWrites() throws Exception {
ByteStringOutputStream output = new ByteStringOutputStream();
for (int i = 0; i < FEW_WRITES; ++i) {
output.write(1);
}
if (output.toByteString().size() != FEW_WRITES) {
throw new IllegalArgumentException();
}
output.close();
}
@Benchmark
public void testSdkCoreByteStringOutputStreamFewSmallWrites() throws Exception {
ByteStringOutputStream output = new ByteStringOutputStream();
for (int i = 0; i < FEW_WRITES; ++i) {
output.write(SMALL_BUFFER);
}
if (output.toByteString().size() != FEW_WRITES * SMALL_BUFFER.length) {
throw new IllegalArgumentException();
}
output.close();
}
@Benchmark
public void testSdkCoreByteStringOutputStreamFewLargeWrites() throws Exception {
ByteStringOutputStream output = new ByteStringOutputStream();
for (int i = 0; i < FEW_WRITES; ++i) {
output.write(LARGE_BUFFER);
}
if (output.toByteString().size() != FEW_WRITES * LARGE_BUFFER.length) {
throw new IllegalArgumentException();
}
output.close();
}
@Benchmark
public void testSdkCoreByteStringOutputStreamManyMixedWritesWithReuse(
SdkCoreByteStringOutputStream state) throws Exception {
ByteStringOutputStream output = state.output;
for (int i = 0; i < 9850; i++) {
output.write(1);
output.write(SMALL_BUFFER);
output.write(1);
output.write(LARGE_BUFFER);
output.write(1);
output.write(SMALL_BUFFER);
output.write(1);
}
if (output.toByteStringAndReset().size()
!= (4 + 2 * SMALL_BUFFER.length + LARGE_BUFFER.length) * 9850) {
throw new IllegalArgumentException();
}
output.close();
}
@Benchmark
public void testSdkCoreByteStringOutputStreamFewMixedWritesWithReuse(
SdkCoreByteStringOutputStream state) throws Exception {
ByteStringOutputStream output = state.output;
for (int i = 0; i < FEW_WRITES; i++) {
output.write(1);
output.write(SMALL_BUFFER);
output.write(1);
output.write(LARGE_BUFFER);
output.write(1);
output.write(SMALL_BUFFER);
output.write(1);
}
if (output.toByteStringAndReset().size()
!= (4 + 2 * SMALL_BUFFER.length + LARGE_BUFFER.length) * FEW_WRITES) {
throw new IllegalArgumentException();
}
output.close();
}
/**
* These benchmarks below provide good details as to the cost of creating a new buffer vs copying
* a subset of the existing one and re-using the larger one.
*/
public static class NewVsCopy {
@State(Scope.Thread)
public static class ArrayCopyState {
@Param({
"512/1024", "640/1024", "768/1024", "896/1024",
"4096/8192", "5120/8192", "6144/8192", "7168/8192",
"20480/65536", "24576/65536", "28672/65536", "32768/65536",
"131072/262144", "163840/262144", "196608/262144", "229376/262144",
"524288/1048576", "655360/1048576", "786432/1048576", "917504/1048576"
})
String copyVsNew;
int copyThreshold;
int byteArraySize;
public byte[] src;
@Setup
public void setup() {
List<String> parts = Splitter.on('/').splitToList(copyVsNew);
copyThreshold = Integer.parseInt(parts.get(0));
byteArraySize = Integer.parseInt(parts.get(1));
src = new byte[byteArraySize];
}
}
@Benchmark
public void testCopyArray(ArrayCopyState state, Blackhole bh) {
byte[] dest = new byte[state.copyThreshold];
System.arraycopy(state.src, 0, dest, 0, state.copyThreshold);
bh.consume(UnsafeByteOperations.unsafeWrap(dest));
}
@State(Scope.Benchmark)
public static class ArrayNewState {
@Param({"1024", "8192", "65536", "262144", "1048576"})
int byteArraySize;
public byte[] src;
@Setup
public void setup() {
src = new byte[byteArraySize];
}
}
@Benchmark
public void testNewArray(ArrayNewState state, Blackhole bh) {
bh.consume(UnsafeByteOperations.unsafeWrap(state.src, 0, state.byteArraySize));
state.src = new byte[state.byteArraySize];
}
}
}