Package com.higherfrequencytrading.hiccup

Source Code of com.higherfrequencytrading.hiccup.TcpHiccupMain$Reader

/*
* Copyright 2013 Peter Lawrey
*
* 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 com.higherfrequencytrading.hiccup;

import com.higherfrequencytrading.chronicle.tools.IOTools;

import java.io.IOException;
import java.net.BindException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.ClosedByInterruptException;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
* @author peter.lawrey
*/
public class TcpHiccupMain {
    // total messages to send.
    static final int WARMUP = Integer.getInteger("warmup", 12000);
    // total messages to send.
    static int RUNS = Integer.getInteger("runs", 1000000);
    // per milli-second. (note: every message is sent twice)
    static int RATE = Integer.getInteger("rate", 25);
    // busy waiting
    static boolean BUSY = Boolean.getBoolean("busy");
    // number of tests
    static final int TESTS = Integer.getInteger("tests", 3);

    public static void main(String... args) throws IOException {
        String hostname = "localhost";
        int port = 65432;

        switch (args.length) {
            case 0:
                for (int i = 0; i < TESTS; i++) {
                    for (int busy = 0; busy <= 1; busy++) {
                        BUSY = busy != 0;

                        Thread thread = new Thread(new Acceptor(port + i));
                        thread.setDaemon(true);
                        thread.start();
                        new Sender(hostname, port + i).run();
                        thread.interrupt();
                    }
                }
                break;

            case 1:
                port = Integer.parseInt(args[0]);
                new Acceptor(port).run();
                // only run one.
                return;

            case 2:
                hostname = args[0];
                port = Integer.parseInt(args[1]);
                for (int rate : new int[]{5, 10, 25}) {
                    RATE = rate;
                    RUNS = rate * 10000;
                    for (int i = 0; i < TESTS; i++) {
                        new Sender(hostname, port).run();
                    }
                }
                break;
        }
    }


    static class Acceptor implements Runnable {
        private final ServerSocketChannel ssc;

        public Acceptor(int port) throws IOException {
            ssc = ServerSocketChannel.open();
            ssc.socket().setReuseAddress(true);
            for (int i = 0; i < 3; i++)
                try {
                    ssc.socket().bind(new InetSocketAddress(port));
                    break;
                } catch (BindException be) {
                    System.err.println("Failed to bind, retrying... ");
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        throw new IOException(e);
                    }
                }
        }

        @Override
        public void run() {
            try {
                ByteBuffer bb = ByteBuffer.allocateDirect(64 * 1024);
                System.err.println("Accepting connect on " + ssc.socket().getLocalPort());
                while (!Thread.interrupted()) {
                    SocketChannel sc = ssc.accept();
                    if (BUSY)
                        sc.configureBlocking(false);

                    String ra = sc.socket().getInetAddress().toString();
                    System.err.println("... connect to " + ra);
                    try {
                        sc.socket().setTcpNoDelay(true);
                        while (sc.read(bb) >= 0) {
                            bb.flip();
//                            System.out.println("e "+bb.remaining());
                            IOTools.writeAllOrEOF(sc, bb);
                            bb.clear();
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    } finally {
                        sc.close();
                    }
                }
            } catch (ClosedByInterruptException ignored) {
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    ssc.close();
                } catch (IOException ignored) {
                }
            }
        }
    }

    static class Sender implements Runnable {
        private final SocketChannel sc;
        private final ExecutorService reader = Executors.newSingleThreadExecutor();
        private final Future<?> future;

        public Sender(String hostname, int port) throws IOException {
            sc = SocketChannel.open(new InetSocketAddress(hostname, port));
            if (BUSY)
                sc.configureBlocking(false);
            sc.socket().setTcpNoDelay(true);
            future = reader.submit(new Reader(sc));
        }

        @Override
        public void run() {
            try {
                ByteBuffer time = ByteBuffer.allocateDirect(12).order(ByteOrder.nativeOrder());
                long start = System.nanoTime();
                for (int i = 1; i <= WARMUP + RUNS; i++) {
                    long next = start + i * 1000000L / RATE;
                    do {
                        /* busy wait */
                    } while (System.nanoTime() < next);
                    time.clear();
                    // when it should have been sent, not when it was.
                    time.putInt(i);
                    time.putLong(next);
                    time.flip();
//                    System.out.println("w " + time.getInt(0) + " " + time.remaining());
                    IOTools.writeAll(sc, time);
                }
                future.get();

            } catch (Throwable e) {
                e.printStackTrace();
                System.exit(-1);
            }
            reader.shutdown();
        }
    }

    static class Reader implements Runnable {
        private final SocketChannel sc;

        public Reader(SocketChannel sc) {
            this.sc = sc;
        }

        @Override
        public void run() {
            System.err.println("... starting reader.");
            Histogram warmup = new Histogram(1000, 100, 7);
            Histogram histo = new Histogram(1000, 100, 7);

            ByteBuffer time = ByteBuffer.allocateDirect(4096).order(ByteOrder.nativeOrder());
            try {
                for (int i = 1; i <= WARMUP + RUNS; i++) {
                    while (time.position() < 12)
                        sc.read(time);
                    time.flip();
                    int sendI = time.getInt();
//                    System.out.println("r " + sendI + " " + time.remaining());
                    if (sendI != i)
                        throw new AssertionError("sendI=" + sendI + ", i=" + i);
                    long next = time.getLong();
                    long took = System.nanoTime() - next;
                    if (i >= WARMUP)
                        histo.sample(took);
                    else
                        warmup.sample(took);
                    time.compact();
                }

                StringBuilder heading = new StringBuilder("runs\trate\twarmup\tbusy");
                StringBuilder values = new StringBuilder(RUNS + "\t" + RATE + "\t" + WARMUP + "\t" + BUSY);
                for (double perc : new double[]{50, 90, 99, 99.9, 99.99, 99.999}) {
                    double oneIn = 1.0 / (1 - perc / 100);
                    heading.append("\t").append(perc).append("%");
                    long value = histo.percentile(perc);
                    values.append("\t").append(inNanos(value));
                    if (RUNS <= oneIn * oneIn)
                        break;
                }
                heading.append("\tworst");
                long worst = histo.percentile(99.9999);
                values.append("\t").append(inNanos(worst)).append("\tmicro-seconds");
                System.out.println(heading);
                System.out.println(values);
            } catch (Throwable e) {
                e.printStackTrace();
                System.exit(-1);

            } finally {
                try {
                    System.err.println("... disconnected from " + sc.socket().getInetAddress());
                    sc.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        private String inNanos(long value) {
            return (value < 1e4) ? "" + value / 1e3 :
                    (value < 1e6) ? "" + value / 1000 :
                            (value < 1e7) ? value / 1e6 + "e3" :
                                    (value < 1e9) ? value / 1000000 + "e3" :
                                            value / 1e9 + "e6";
        }
    }
}
TOP

Related Classes of com.higherfrequencytrading.hiccup.TcpHiccupMain$Reader

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.