0

I am using below client thread for connecting to my NIO server.

    class RunnableDemo implements Runnable {
    private Thread t;
    private String threadName;

    InetAddress host = null;
    int port = 9090;

    RunnableDemo(String name) {
        threadName = name;
        System.err.println("Creating " + threadName);

    }

    public void run() {
        System.err.println("Running " + threadName);
        try {
            SocketChannel socketChannel = SocketChannel.open();

            socketChannel.configureBlocking(false);

            socketChannel.connect(new InetSocketAddress(host, port));

            while (!socketChannel.finishConnect())
                ;

            System.out.println("Thread " + threadName + " Connected");

            while (true) {
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                if (socketChannel.read(buffer) != 0) {
                    buffer.flip();
                    byte[] bytes = new byte[buffer.limit()];
                    buffer.get(bytes);
                    System.out.println(threadName+ ":" + new String(bytes));
                    buffer.clear();
                }
            }

        } catch (Exception e) {
            System.out.println("Thread " + threadName + " interrupted.");
            e.printStackTrace();
        }
        System.out.println("Thread " + threadName + " exiting.");
    }

    public void start() {
        System.out.println("Starting " + threadName);
        try {
            host = InetAddress.getByName("127.0.0.1");
            if (t == null) {
                t = new Thread(this, threadName);
                t.start();
            }
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
    }

}

This is my server side code. when I running the server side only the CPU is not more than 5% but when I run client for each thread cpu usage will raise about 20-30%

public class EchoServer {
    private static final int BUFFER_SIZE = 1024;

    private final static int DEFAULT_PORT = 9090;

    private long numMessages = 0;

    private long loopTime;

    private InetAddress hostAddress = null;

    private int port;

    private Selector selector;

    // The buffer into which we'll read data when it's available
    private ByteBuffer readBuffer = ByteBuffer.allocate(BUFFER_SIZE);

    int timestamp=0;

    public EchoServer() throws IOException {
        this(DEFAULT_PORT);
    }

    public EchoServer(int port) throws IOException {
        this.port = port;
        hostAddress = InetAddress.getByName("127.0.0.1");
        selector = initSelector();
        loop();
    }

    private Selector initSelector() throws IOException {
        Selector socketSelector = SelectorProvider.provider().openSelector();

        ServerSocketChannel serverChannel = ServerSocketChannel.open();
        serverChannel.configureBlocking(false);

        InetSocketAddress isa = new InetSocketAddress(hostAddress, port);
        serverChannel.socket().bind(isa);
        serverChannel.register(socketSelector, SelectionKey.OP_ACCEPT);
        return socketSelector;
    }

    private void loop() {
        for (;true;) {
            try {
                selector.select();
                Iterator<SelectionKey> selectedKeys = selector.selectedKeys()
                        .iterator();
                while (selectedKeys.hasNext()) {
                    SelectionKey key = selectedKeys.next();
                    selectedKeys.remove();
                    if (!key.isValid()) {
                        continue;
                    }
                     // Check what event is available and deal with it
                    if (key.isAcceptable()) {
                        accept(key);

                    } else if (key.isWritable()) {
                        write(key);
                    }
                }
                Thread.sleep(3000);
                timestamp+=3;
            } catch (Exception e) {
                e.printStackTrace();
                System.exit(1);
            }



        }
    }

    private void accept(SelectionKey key) throws IOException {

        ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();

        SocketChannel socketChannel = serverSocketChannel.accept();
        socketChannel.configureBlocking(false);
        socketChannel.setOption(StandardSocketOptions.SO_KEEPALIVE, true);
        socketChannel.setOption(StandardSocketOptions.TCP_NODELAY, true);

        socketChannel.register(selector, SelectionKey.OP_WRITE);

        System.out.println("Client is connected");
    }

    private void write(SelectionKey key) throws IOException {
        SocketChannel socketChannel = (SocketChannel) key.channel();
        ByteBuffer dummyResponse = ByteBuffer.wrap(("ok:" + String.valueOf(timestamp)) .getBytes("UTF-8"));

        socketChannel.write(dummyResponse);
        if (dummyResponse.remaining() > 0) {
            System.err.print("Filled UP");
        }
        System.out.println("Message Sent");
     //   key.interestOps(SelectionKey.OP_READ);
    }
}

Every thing works right. Client and server can see each other and communicate. In order to testing how much connection my code can accept I create several instances of above thread and here is the problem.

When I tracking the performance sector of my task panel(windows) by generating each instance of this thread the CPU usage of my PC (I am using a 2.6 core i5 CPU) raise with 30% and by generating 3 thread my cpu usage is about 100% !!!

I am wondering what is the problem with above code that takes 30% of my CPU.

Husein Behboudi Rad
  • 5,434
  • 11
  • 57
  • 115
  • @Hussein BehbudiRad Allocate your buffer outside the loop while and each time you want to use it with read method clear it. – Naruto Biju Mode Dec 25 '14 at 10:59
  • When your read method will reach the end of stream it will return -1 so inside your read loop you should test if `socketChannel.read(buffer) > 0` in order to avoid unnecessary works. – Naruto Biju Mode Dec 25 '14 at 11:06
  • your are right. I changed the '!=' to '>' and also moved the 'ByteBuffer' allocation before 'while' statement but no changes in cpu performance – Husein Behboudi Rad Dec 25 '14 at 11:10
  • can you post your server code also. I suspect your server is running on high CPU. – Naruto Biju Mode Dec 25 '14 at 11:26
  • I just posted the server code too. – Husein Behboudi Rad Dec 25 '14 at 11:49
  • your server is working fine, but the problem is your server work and sleep 3 seconds and your clients continue to read empty stream while your server is sleeping which lead to consume high cpu on your clients, the solution is to synchronize your clients to read from server then sleep for 1 second to lower their use of cpu. – Naruto Biju Mode Dec 25 '14 at 12:03
  • You should use a `Selector` for the client as well, this should solve all your issues. – TwoThe Dec 26 '14 at 12:44

2 Answers2

2

I can see two potential causes for high CPU load.

  1. You are using non-blocking I/O inappropriately, by (in effect) repeatedly polling the channel to complete the connect, and to read data. In this particular use-case, you would be better advised to used blocking I/O. The data throughput will be (pretty much) the same, and you won't waste CPU by polling.

    Generally speaking, non-blocking I/O is only a good idea when the thread has other things to do instead of blocking.

  2. Writing to System.out could also use significant CPU ... external to the JVM. If standard output goes to a typical console application that displays it on the screen, then the process of rendering and painting the text onto the screen ... and scrolling ... could use a fair amount of CPU.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • Actually I need to send some string text (only 4 character) to all of my connected clients. The clients do their job and also are connecting to the server. after each 3 second server sends a 4 character string and client log some thing based on this. Is it better that I use a blocking socket for this propose? – Husein Behboudi Rad Dec 25 '14 at 11:53
  • I commented the system.out.println and instead of printing just store it on a string variable but the cpu usage do not changed. – Husein Behboudi Rad Dec 25 '14 at 12:03
  • *"Is it better that I use a blocking socket for this propose?"* - YES. – Stephen C Dec 25 '14 at 12:04
  • OK. Thanks. In this mind I searched for some tutorials about blocking sockets. to find a good tutorial. do you now a good one too that can help me in my propose? – Husein Behboudi Rad Dec 25 '14 at 12:13
  • 1
    The javadocs work just fine for me. But in this case, it is simply a matter of *removing* code. 1) remove the `configureBlocking` call. 2) remove the `finishConnect` call / loop. – Stephen C Dec 25 '14 at 23:48
-2

Can't see anything particular that would cause undue stress but I would suggest that you include a short sleep in your loop to ensure you don't hog the cpu. This includes hogging the cup from your main thread (which could be a server expected to do the important job of listening on oncoming connections).

I would especially sleep where external activities are executed and latency is expected.

See also Thread.sleep.

Having done so, I would test throughout against your original while monitoring cpu, memory, load, etc.

wmorrison365
  • 5,995
  • 2
  • 27
  • 40
  • I have a sleep(3000) in my server side. it sends messages each 3 second. by this way is it necessary that I put thread.slepp in the client side too? – Husein Behboudi Rad Dec 25 '14 at 11:17
  • Well, not ordinarily unless your client is pumping out requests. Adding sleep should aid cpu but at cost of throughout. Your sysouts are going to be comparatively slow and so be waited on but wouldn't expect to e significant. – wmorrison365 Dec 25 '14 at 13:09
  • 1
    Minus 1 for inappropriate, avoidable and misleading use of Sleep(). – Martin James Dec 25 '14 at 15:11