uk.co.real_logic.aeron.tools.perf_tools.AeronLatencyUnderLoadPublisher.java Source code

Java tutorial

Introduction

Here is the source code for uk.co.real_logic.aeron.tools.perf_tools.AeronLatencyUnderLoadPublisher.java

Source

/*
 * Copyright 2015 Kaazing Corporation
 *
 * 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 uk.co.real_logic.aeron.tools.perf_tools;

import org.apache.commons.cli.*;
import uk.co.real_logic.aeron.Aeron;
import uk.co.real_logic.aeron.FragmentAssemblyAdapter;
import uk.co.real_logic.aeron.Publication;
import uk.co.real_logic.aeron.Subscription;
import uk.co.real_logic.aeron.logbuffer.BufferClaim;
import uk.co.real_logic.aeron.logbuffer.FragmentHandler;
import uk.co.real_logic.aeron.logbuffer.Header;
import uk.co.real_logic.aeron.tools.MessagesAtMessagesPerSecondInterval;
import uk.co.real_logic.aeron.tools.RateController;
import uk.co.real_logic.aeron.tools.RateControllerInterval;
import uk.co.real_logic.agrona.DirectBuffer;
import uk.co.real_logic.agrona.MutableDirectBuffer;
import uk.co.real_logic.agrona.concurrent.BusySpinIdleStrategy;
import uk.co.real_logic.agrona.concurrent.IdleStrategy;
import uk.co.real_logic.agrona.concurrent.UnsafeBuffer;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;

public class AeronLatencyUnderLoadPublisher implements RateController.Callback {
    private Publication pub = null;
    private Subscription sub = null;
    private CountDownLatch connectionLatch = null;
    private FragmentHandler fragmentHandler = null;
    private final int pubStreamId = 10;
    private final int subStreamId = 11;
    private String pubChannel = "udp://localhost:44444";
    private String reflectChannel = "udp://localhost:55555";
    private boolean running = true;
    private final int msgLen = 20;
    private UnsafeBuffer buffer = null;
    private final long timestamps[] = new long[41111100];
    private int msgCount = 0;
    private final BufferClaim bufferClaim;
    private int warmups = 0;
    private final double means[] = new double[7];

    public AeronLatencyUnderLoadPublisher(final String[] args) {
        try {
            parseArgs(args);
        } catch (final ParseException e) {
            throw new RuntimeException(e);
        }
        final Aeron.Context ctx = new Aeron.Context().newConnectionHandler(this::connectionHandler);
        fragmentHandler = new FragmentAssemblyAdapter(this::msgHandler);
        final Aeron aeron = Aeron.connect(ctx);
        System.out.println("Reflect: " + reflectChannel + " Pub: " + pubChannel);
        pub = aeron.addPublication(pubChannel, pubStreamId);
        sub = aeron.addSubscription(reflectChannel, subStreamId);
        connectionLatch = new CountDownLatch(1);
        final IdleStrategy idle = new BusySpinIdleStrategy();
        bufferClaim = new BufferClaim();

        final List<RateControllerInterval> intervals = new ArrayList<>();
        intervals.add(new MessagesAtMessagesPerSecondInterval(100, 10));
        intervals.add(new MessagesAtMessagesPerSecondInterval(1000, 100));
        intervals.add(new MessagesAtMessagesPerSecondInterval(10000, 1000));
        intervals.add(new MessagesAtMessagesPerSecondInterval(100000, 10000));
        intervals.add(new MessagesAtMessagesPerSecondInterval(1000000, 100000));
        intervals.add(new MessagesAtMessagesPerSecondInterval(10000000, 1000000));
        intervals.add(new MessagesAtMessagesPerSecondInterval(30000000, 3000000));
        buffer = new UnsafeBuffer(ByteBuffer.allocateDirect(msgLen));
        msgCount = 0;

        RateController rateController = null;
        try {
            rateController = new RateController(this, intervals);
        } catch (final Exception ex) {
            throw new RuntimeException(ex);
        }

        final Runnable task = () -> {
            while (running) {
                while (sub.poll(fragmentHandler, 1) <= 0 && running) {
                }
            }
            System.out.println("Done");
        };
        final Thread subThread = new Thread(task);
        subThread.start();

        try {
            connectionLatch.await();
        } catch (final InterruptedException e) {
            e.printStackTrace();
        }

        final int warmUpMsgs = 100000;
        for (int i = 0; i < warmUpMsgs; i++) {
            while (pub.tryClaim(buffer.capacity(), bufferClaim) < 0L) {
                idle.idle(1);
            }
            final MutableDirectBuffer buffer = bufferClaim.buffer();
            final int offset = bufferClaim.offset();
            buffer.putByte(offset, (byte) 'w');
            bufferClaim.commit();
        }
        try {
            Thread.sleep(1000);
        } catch (final InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("warmup msgs received: " + warmups);
        final int start = (int) System.currentTimeMillis();
        while (rateController.next()) {

        }
        final int total = (int) (System.currentTimeMillis() - start) / 1000;
        buffer.putByte(0, (byte) 'q');

        while (pub.offer(buffer, 0, buffer.capacity()) < 0L) {
            idle.idle(0);
        }

        System.out.println("Duration: " + total + " seconds");
        try {
            Thread.sleep(1000);
        } catch (final InterruptedException e) {
            e.printStackTrace();
        }

        running = false;

        try {
            subThread.join();
        } catch (final InterruptedException e) {
            e.printStackTrace();
        }

        aeron.close();

        try {
            computeStats();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public int onNext() {
        while (pub.tryClaim(buffer.capacity(), bufferClaim) < 0L) {
        }

        final MutableDirectBuffer buffer = bufferClaim.buffer();
        final int offset = bufferClaim.offset();
        buffer.putByte(offset, (byte) 'p');
        buffer.putInt(offset + 1, msgCount++);
        buffer.putLong(offset + 5, System.nanoTime());
        bufferClaim.commit();
        return msgLen;
    }

    private void connectionHandler(final String channel, final int streamId, final int sessionId,
            final long position, final String sourceIdentity) {
        System.out.println(channel + " " + streamId);
        if (channel.equals(reflectChannel) && subStreamId == streamId) {
            connectionLatch.countDown();
            System.out.println("Connected");
        }
    }

    private void msgHandler(final DirectBuffer buffer, final int offset, final int length, final Header header) {
        if (buffer.getByte(offset) == (byte) 'p') {
            timestamps[buffer.getInt(offset + 1)] = System.nanoTime() - buffer.getLong(offset + 5);
        } else {
            warmups++;
        }
    }

    private void parseArgs(final String[] args) throws ParseException {
        final Options options = new Options();
        options.addOption("c", "claim", false, "Use Try/Claim");
        options.addOption("", "pubChannel", true, "Primary publishing channel");
        options.addOption("", "reflectChannel", true, "Reflection channel");

        final CommandLineParser parser = new GnuParser();
        final CommandLine command = parser.parse(options, args);

        if (command.hasOption("pubChannel")) {
            pubChannel = command.getOptionValue("pubChannel", "udp://localhost:44444");
        }

        if (command.hasOption("reflectChannel")) {
            reflectChannel = command.getOptionValue("reflectChannel", "udp://localhost:55555");
        }
    }

    private void computeStats() throws IOException {
        means[0] = computeStats(0, 100, "10mps");
        means[1] = computeStats(100, 1100, "100mps");
        means[2] = computeStats(1100, 11000, "1Kmps");
        means[3] = computeStats(11000, 111000, "10Kmps");
        means[4] = computeStats(111000, 1111000, "100Kmps");
        means[5] = computeStats(1111000, 11111000, "1Mmps");
        means[6] = computeStats(11111000, 41111000, "3Mmps");

        generateScatterPlot();
    }

    private double computeStats(final int start, final int end, final String title) {
        double sum = 0.0;
        double min = Double.MAX_VALUE;
        double max = Double.MIN_VALUE;

        for (int i = start; i < end; i++) {
            final double ts = timestamps[i] / 1000.0;
            if (ts < 0) {
                System.exit(1);
            }
            sum += ts;
            if (ts < min) {
                min = ts;
            }
            if (ts > max) {
                max = ts;
            }
        }
        System.out.println("Mean latency for " + title + ": " + sum / (end - start));
        return sum / (end - start);
    }

    private void generateScatterPlot() throws IOException {
        final BufferedImage image = new BufferedImage(1800, 1000, BufferedImage.TYPE_INT_ARGB);
        final Graphics2D g2 = image.createGraphics();
        final FontMetrics fm = g2.getFontMetrics();
        final String filename = "throughputency.png";
        final File imageFile = new File(filename);

        final int height = 940;
        double min = Double.MAX_VALUE;
        double max = Double.MIN_VALUE;
        for (final long timestamp : timestamps) {
            final double ts = timestamp / 1000.0;
            if (ts < min) {
                min = ts;
            }
            if (ts > max) {
                max = ts;
            }
        }
        final double stepY = height / max;

        g2.setColor(Color.white);
        g2.fillRect(0, 0, 1800, 1000);
        g2.setColor(Color.black);
        g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
        g2.drawString("Latency ScatterPlot (microseconds)",
                900 - fm.stringWidth("Latency ScatterPlot (microseconds)") / 2, 20);
        g2.drawString("" + max, 10, 20);
        g2.drawLine(100, 20, 100, 960);
        g2.drawLine(100, 960, 1790, 960);
        int start = 0;
        int end = 100;
        final double width = 1690.0 / 7.0;
        g2.setColor(Color.red);
        plotSubset(g2, start, end, "10 msgs/sec", 100, width, stepY, means[0]);

        start = 100;
        end = 1100;
        g2.setColor(Color.green);
        plotSubset(g2, start, end, "100 msgs/sec", 100 + width, width, stepY, means[1]);

        start = 1100;
        end = 11100;
        g2.setColor(Color.blue);
        plotSubset(g2, start, end, "1K msgs/sec", 100 + width * 2, width, stepY, means[2]);

        start = 11100;
        end = 111100;
        g2.setColor(Color.cyan);
        plotSubset(g2, start, end, "10K msgs/sec", 100 + width * 3, width, stepY, means[3]);

        start = 111100;
        end = 1111100;
        g2.setColor(Color.magenta);
        plotSubset(g2, start, end, "100K msgs/sec", 100 + width * 4, width, stepY, means[4]);

        start = 1111100;
        end = 11111100;
        g2.setColor(Color.yellow);
        plotSubset(g2, start, end, "1M msgs/sec", 100 + width * 5, width, stepY, means[5]);

        start = 11111100;
        end = 41111100;
        g2.setColor(Color.orange);
        plotSubset(g2, start, end, "3M msgs/sec", 100 + width * 6, width, stepY, means[6]);

        ImageIO.write(image, "png", imageFile);
    }

    private void plotSubset(final Graphics2D g, final int start, final int end, final String title,
            final double startX, final double width, final double stepY, final double mean) {
        final FontMetrics fm = g.getFontMetrics();
        final Color color = g.getColor();
        g.setColor(Color.black);
        g.drawString(title, (int) (startX + width / 2 - fm.stringWidth(title) / 2), 975);
        final String tmp = String.format("Mean: %.3fus", mean);
        g.drawString(tmp, (int) (startX + width / 2 - fm.stringWidth(tmp) / 2), 990);
        g.setColor(color);
        final double stepX = width / (end - start);
        for (int i = start; i < end; i++) {
            final int posX = (int) (startX + stepX * (i - start));
            final int posY = 960 - ((int) (stepY * (timestamps[i] / 1000.0)) + 1);
            g.drawLine(posX, posY, posX, 960);
        }
    }

    public static void main(final String[] args) {
        new AeronLatencyUnderLoadPublisher(args);
    }
}