zipkin.benchmarks.CodecBenchmarks.java Source code

Java tutorial

Introduction

Here is the source code for zipkin.benchmarks.CodecBenchmarks.java

Source

/**
 * Copyright 2015-2016 The OpenZipkin Authors
 *
 * Licensed 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 zipkin.benchmarks;

import com.google.common.io.ByteStreams;
import com.twitter.zipkin.thriftjava.Annotation;
import com.twitter.zipkin.thriftjava.BinaryAnnotation;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import org.apache.thrift.TDeserializer;
import org.apache.thrift.TException;
import org.apache.thrift.TSerializer;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Threads;
import org.openjdk.jmh.annotations.Warmup;
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 zipkin.Codec;
import zipkin.Endpoint;
import zipkin.Span;

/**
 * This compares the speed of the bundled java codec with the approach used in the scala
 * implementation. Re-run this benchmark when changing internals of {@link zipkin.Codec}.
 *
 * <p>The {@link zipkin.Codec bundled java codec} aims to be both small in size (i.e. does not
 * significantly increase the size of zipkin's jar), and efficient. It may not always be fastest,
 * but we should try to keep it competitive.
 */
@Measurement(iterations = 5, time = 1)
@Warmup(iterations = 10, time = 1)
@Fork(3)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@State(Scope.Thread)
@Threads(1)
public class CodecBenchmarks {
    static final TBinaryProtocol.Factory TBINARY_PROTOCOL_FACTORY = new TBinaryProtocol.Factory();

    static final byte[] localSpanJson = read("/span-local.json");
    static final Span localSpan = Codec.JSON.readSpan(localSpanJson);
    static final byte[] localSpanThrift = Codec.THRIFT.writeSpan(localSpan);
    static final com.twitter.zipkin.thriftjava.Span localSpanLibThrift = deserialize(localSpanThrift);

    @Benchmark
    public Span readLocalSpan_json_zipkin() {
        return Codec.JSON.readSpan(localSpanJson);
    }

    @Benchmark
    public Span readLocalSpan_thrift_zipkin() {
        return Codec.THRIFT.readSpan(localSpanThrift);
    }

    @Benchmark
    public Span readLocalSpan_thrift_libthrift() {
        return toZipkinSpan(deserialize(localSpanThrift));
    }

    @Benchmark
    public byte[] writeLocalSpan_json_zipkin() {
        return Codec.JSON.writeSpan(localSpan);
    }

    @Benchmark
    public byte[] writeLocalSpan_thrift_zipkin() {
        return Codec.THRIFT.writeSpan(localSpan);
    }

    @Benchmark
    public byte[] writeLocalSpan_thrift_libthrift() throws TException {
        return serialize(localSpanLibThrift);
    }

    static final byte[] clientSpanJson = read("/span-client.json");
    static final Span clientSpan = Codec.JSON.readSpan(clientSpanJson);
    static final byte[] clientSpanThrift = Codec.THRIFT.writeSpan(clientSpan);
    static final com.twitter.zipkin.thriftjava.Span clientSpanLibThrift = deserialize(clientSpanThrift);

    @Benchmark
    public Span readClientSpan_json_zipkin() {
        return Codec.JSON.readSpan(clientSpanJson);
    }

    @Benchmark
    public Span readClientSpan_thrift_zipkin() {
        return Codec.THRIFT.readSpan(clientSpanThrift);
    }

    @Benchmark
    public Span readClientSpan_thrift_libthrift() {
        return toZipkinSpan(deserialize(clientSpanThrift));
    }

    @Benchmark
    public byte[] writeClientSpan_json_zipkin() {
        return Codec.JSON.writeSpan(clientSpan);
    }

    @Benchmark
    public byte[] writeClientSpan_thrift_zipkin() {
        return Codec.THRIFT.writeSpan(clientSpan);
    }

    @Benchmark
    public byte[] writeClientSpan_thrift_libthrift() throws TException {
        return serialize(clientSpanLibThrift);
    }

    static final byte[] rpcSpanJson = read("/span-rpc.json");
    static final Span rpcSpan = Codec.JSON.readSpan(rpcSpanJson);
    static final byte[] rpcSpanThrift = Codec.THRIFT.writeSpan(rpcSpan);
    static final com.twitter.zipkin.thriftjava.Span rpcSpanLibThrift = deserialize(rpcSpanThrift);

    @Benchmark
    public Span readRpcSpan_json_zipkin() {
        return Codec.JSON.readSpan(rpcSpanJson);
    }

    @Benchmark
    public Span readRpcSpan_thrift_zipkin() {
        return Codec.THRIFT.readSpan(rpcSpanThrift);
    }

    @Benchmark
    public Span readRpcSpan_thrift_libthrift() {
        return toZipkinSpan(deserialize(rpcSpanThrift));
    }

    @Benchmark
    public byte[] writeRpcSpan_json_zipkin() {
        return Codec.JSON.writeSpan(rpcSpan);
    }

    @Benchmark
    public byte[] writeRpcSpan_thrift_zipkin() {
        return Codec.THRIFT.writeSpan(rpcSpan);
    }

    @Benchmark
    public byte[] writeRpcSpan_thrift_libthrift() throws TException {
        return serialize(rpcSpanLibThrift);
    }

    static final byte[] rpcV6SpanJson = read("/span-rpc-ipv6.json");
    static final Span rpcV6Span = Codec.JSON.readSpan(rpcV6SpanJson);
    static final byte[] rpcV6SpanThrift = Codec.THRIFT.writeSpan(rpcV6Span);
    static final com.twitter.zipkin.thriftjava.Span rpcV6SpanLibThrift = deserialize(rpcV6SpanThrift);

    @Benchmark
    public Span readRpcV6Span_json_zipkin() {
        return Codec.JSON.readSpan(rpcV6SpanJson);
    }

    @Benchmark
    public Span readRpcV6Span_thrift_zipkin() {
        return Codec.THRIFT.readSpan(rpcV6SpanThrift);
    }

    @Benchmark
    public Span readRpcV6Span_thrift_libthrift() {
        return toZipkinSpan(deserialize(rpcV6SpanThrift));
    }

    @Benchmark
    public byte[] writeRpcV6Span_json_zipkin() {
        return Codec.JSON.writeSpan(rpcV6Span);
    }

    @Benchmark
    public byte[] writeRpcV6Span_thrift_zipkin() {
        return Codec.THRIFT.writeSpan(rpcV6Span);
    }

    @Benchmark
    public byte[] writeRpcV6Span_thrift_libthrift() throws TException {
        return serialize(rpcV6SpanLibThrift);
    }

    // Convenience main entry-point
    public static void main(String[] args) throws RunnerException {
        Options opt = new OptionsBuilder().include(".*" + CodecBenchmarks.class.getSimpleName() + ".*").build();

        new Runner(opt).run();
    }

    static byte[] read(String resource) {
        try {
            return ByteStreams.toByteArray(CodecBenchmarks.class.getResourceAsStream(resource));
        } catch (IOException e) {
            throw new IllegalStateException(e);
        }
    }

    private byte[] serialize(com.twitter.zipkin.thriftjava.Span thriftSpan) throws TException {
        // TSerializer isn't thread-safe
        return new TSerializer(TBINARY_PROTOCOL_FACTORY).serialize(thriftSpan);
    }

    private static com.twitter.zipkin.thriftjava.Span deserialize(byte[] serialized) {
        com.twitter.zipkin.thriftjava.Span result = new com.twitter.zipkin.thriftjava.Span();
        try {
            // TDeserializer isn't thread-safe
            new TDeserializer(TBINARY_PROTOCOL_FACTORY).deserialize(result, serialized);
        } catch (TException e) {
            throw new AssertionError(e);
        }
        return result;
    }

    /**
     * {@link zipkin.Span.Builder}, validates, doesn't return null for fields that aren't nullable,
     * uses immutable collections, etc. When comparing codec, make sure you copy-out as structs like
     * libthrift do no validation, which is cheaper, but not usable in zipkin.
     */
    private static Span toZipkinSpan(com.twitter.zipkin.thriftjava.Span libthriftSpan) {
        Span.Builder builder = Span.builder().traceId(libthriftSpan.trace_id).id(libthriftSpan.id)
                .parentId(libthriftSpan.isSetParent_id() ? libthriftSpan.parent_id : null).name(libthriftSpan.name)
                .timestamp(libthriftSpan.isSetTimestamp() ? libthriftSpan.timestamp : null)
                .duration(libthriftSpan.isSetDuration() ? libthriftSpan.duration : null)
                .debug(libthriftSpan.isSetDebug() ? libthriftSpan.debug : null);

        if (libthriftSpan.isSetAnnotations()) {
            for (Annotation a : libthriftSpan.annotations) {
                builder.addAnnotation(zipkin.Annotation.create(a.timestamp, a.value,
                        a.isSetHost()
                                ? Endpoint.builder().serviceName(a.host.service_name).ipv4(a.host.ipv4)
                                        .ipv6(a.host.getIpv6()).build()
                                : null));
            }
        }

        if (libthriftSpan.isSetBinary_annotations()) {
            for (BinaryAnnotation b : libthriftSpan.binary_annotations) {
                builder.addBinaryAnnotation(zipkin.BinaryAnnotation.create(b.key, b.getValue(),
                        zipkin.BinaryAnnotation.Type.fromValue(b.getAnnotation_type().getValue()),
                        b.isSetHost()
                                ? Endpoint.builder().serviceName(b.host.service_name).ipv4(b.host.ipv4)
                                        .ipv6(b.host.getIpv6()).build()
                                : null));
            }
        }
        return builder.build();
    }
}