-1

I just copied a working example of data transfer from this forum and used it with little change in my program, but I can't see what's the problem with its speed. I tested the main example and it transfers some 1MB in less than 30ms. Both read and write speed is very good. But when I use it in my case the same amount of data is not transfered in less than 400ms! The writing remains effecient, but reading is somehow problematic.

Here the data is written. For now, I'm not trying to speed up the first part, i.e. the serialization of the object. My question is about the second part.

private static void writeObject(OutputStream out, Object obj) throws IOException {
    long t1 = System.currentTimeMillis();

    ByteArrayOutputStream bArr = new ByteArrayOutputStream();
    ObjectOutputStream ojs = new ObjectOutputStream(bArr);
    ojs.writeObject(obj);
    ojs.close();

    long t2 = System.currentTimeMillis();

    byte[] arr = bArr.toByteArray();
    int len = arr.length;
    for (int i = 0; i < arr.length; i += BUFFER_SIZE)
        out.write(arr, i, Math.min(len - i, BUFFER_SIZE));
    out.close();

    long t3 = System.currentTimeMillis();

    System.out.println(t3 - t2);
}

Well, this is not that bad! t3 - t2 prints some 30ms.

The problem is here, in readObject(), and not in its second part, where the object is deserialized, at least not for now, but the problem is in the first part, where t2 - t1 turns out to be more than 400ms, as I mentioned.

private static Object readObject(InputStream in) throws IOException, ClassNotFoundException {
    long t1 = System.currentTimeMillis();

    ByteArrayOutputStream bao = new ByteArrayOutputStream();
    byte[] buff = new byte[BUFFER_SIZE];
    int read;
    while ((read = in.read(buff)) != -1) {
        bao.write(buff, 0, read);
    }
    in.close();

    long t2 = System.currentTimeMillis();

    ByteArrayInputStream bArr = new ByteArrayInputStream(bao.toByteArray());
    Object o = new ObjectInputStream(new BufferedInputStream(bArr)).readObject();

    long t3 = System.currentTimeMillis();

    System.out.println(t2 - t1);
    return o;
}

And here is the main():

final static int BUFFER_SIZE = 64 * 1024;

public static void main(String[] args) throws Exception {
    final String largeFile1 = "store.aad";
    final Table t = (Table) new ObjectInputStream(new FileInputStream(largeFile1)).readObject();

    new Thread(new Runnable() {
        public void run() {
            try {
                ServerSocket serverSocket = new ServerSocket(12345);
                Socket clientSocket = serverSocket.accept();
                readObject(clientSocket.getInputStream());
            } catch (Exception e) {
            }
        }
    }).start();
    new Thread(new Runnable() {
        public void run() {
            try {
                Thread.sleep(1000);
                Socket socket = new Socket("localhost", 12345);
                OutputStream socketOutputStream = socket.getOutputStream();
                writeObject(socketOutputStream, t);
            } catch (Exception e) {
            }
        }
    }).start();
}

Where am I going wrong?!

Community
  • 1
  • 1
smr
  • 33
  • 6

4 Answers4

0

(An obligatory comment about the difficulty of getting Java benchmarks correct).

It seems that you are launching two threads, a reader thread and a writer thread. It is entirely feasible that things proceed in the following order:

  1. The reader thread starts.
  2. The reader thread records t1.
  3. The reader thread calls read(), but is blocked because no data is available yet.
  4. The writer thread starts.
  5. The writer thread sleeps for a second.
  6. The writer thread calls write().
  7. The writer thread exits.
  8. The reader thread's read() call returns.
  9. The reader thread t2, etc., and exits.

Now, if you are seeing ~400ms for t2 - t1, this is probably not what is happening: it seems probable that the writer thread's call to sleep() must be happening before t1 is recorded. But the short answer is that it seems unclear what t2 - t1 is measuring. In particular, it seems incorrect to expect it to measure simply the time read() takes doing work (as opposed to waiting for the data to read).

Community
  • 1
  • 1
Jae Heon Lee
  • 1,101
  • 6
  • 10
  • You're right! But the problem remains!! As you see in the code I wrote, the reading thread started 1 second after the writing one, because of the the `Thread.sleep()`. I just now extended it to 2 seconds, and it grew so better. Now it writes in less than 10ms but reads in more than 100ms. The main example, as I mentioned, does both in much less time. – smr Dec 06 '15 at 08:05
  • Moreover, I want to use the technic in a server/client program, where the clients sends some data and then waits for answer. if one second is insufficient, so the the problem is much greater when it waits for server's response. – smr Dec 06 '15 at 08:11
  • I am guessing that the `sleep()` is to serve as a makeshift synchronization mechanism, so that the reader thread's `accept()` is called before the writer thread's `Socket()`. I wonder what values you would get if you place another sleep between the reader thread's calls to `accept()` and `readObject()`, so that the writer thread (presumably) completes writing before reading starts. – Jae Heon Lee Dec 06 '15 at 08:25
  • Yes! That changed the story! But raised another question mark!! When I put the `sleep()` as you suggested, the reading time decreases to less the 10ms, but instead, it is the writing time that grows over than 100ms!! When I remove the `sleep()` the numbers are swapped!! Why?!! – smr Dec 06 '15 at 09:33
  • @user1389026 I suspect that the sleeps don't really affect the performance of these methods. In addition to the obvious mysteries of when the threads get scheduled, `read()` and `write()` are methods which take from or write to buffers. They can take time waiting for the buffers to become ready for their operations, without doing actual work of transferring data. – Jae Heon Lee Dec 06 '15 at 10:26
0

If you want to read and write with buffered IO, you can use BufferedInputStream and BufferedOutputStream to read and write respectively. And, you could use a try-with-resources Statement to close. To write, something like

private static final int BUFFER_SIZE = 32 * 1024;
private static void writeObject(OutputStream out, Object obj) //
            throws IOException {
    try (ObjectOutputStream ojs = new ObjectOutputStream(//
                new BufferedOutputStream(out, BUFFER_SIZE));
        ojs.writeObject(obj);
    }
}

and to read like

private static Object readObject(InputStream in) throws IOException,//
            ClassNotFoundException {
    try (ObjectInputStream ois = new ObjectInputStream(//
                new BufferedInputStream(in, BUFFER_SIZE))) {
        return ois.readObject();
    }
}
Elliott Frisch
  • 198,278
  • 20
  • 158
  • 249
0

When you are performing a micro-benchmark I suggest you ignore all the results you get for at least the first 2 seconds of CPU time to give you JVM a chance to warmup.

I would write this without using sleep.

For the purpose of your test, writing the object is irrelevant. You just need to write a new byte[size] and see how long it takes.

For testing short latencies, I would use System.nanoTime()

I would start by writing a small message first and looking at the round trip time. i.e. client sends a packet to the server and the server sends it back again.

Last but not least, you will get better performance by using NIO which was added in Java 1.4 (2004)

Here is some code wrote earlier EchoServerMainand EchoClientMain which produces results like this.

On a E5-2650 v2 over loopback
Throughput was 2880.4 MB/s
Loop back echo latency was 5.8/6.2 9.6/19.4 23.2us for 50/90 99/99.9 99.99%tile

Note: These timing are the full round trip time to send the packet to the server and back again. The timings are in micro-seconds.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • Ok, you're right about the test. But the problem is with the real case! There I'm getting really low speeds! – smr Dec 06 '15 at 08:18
  • @user1389026 since you haven't warmed up the test, you are getting one low speed. If you only ever send one object, how fast does it need to be? – Peter Lawrey Dec 06 '15 at 08:19
  • @user1389026 BTW Java Serialization is standard but the slowest serialization you can get. There are alternatives which are faster and produce much smaller messages which take less time to send. In fact we have two, but just about anything would be better for speed and size. You could even compress the data to make it smaller. – Peter Lawrey Dec 06 '15 at 08:21
  • I know that the serialization part is ineffecient, but my #1 problem now is the connection speed, not on the localhost, where the latencies are negligible, but on a LAN. I don't know why it's getting sometimes about one second to send much less data than I'm testing here! While sharing data between the computers, i.e. in the Windows explorer is much faster! – smr Dec 06 '15 at 08:47
  • I would copy the contents of the file rather than translating the file into an object and back again, just in case this matters. I would use larger buffers at the TCP level and I wouldn't artificially break up the array. I would send it all at once. On the reading side, you should check the length you actually read() as this could be just one 1 byte. To make your life easier I suggest send the length first so that the reader knows exactly how big a buffer to use rather than use a growning ByteArrayOutputStream. – Peter Lawrey Dec 06 '15 at 08:54
  • I would have the receiver send back a message when it has received the data so you the sender know how long it really took and can time the end to end. – Peter Lawrey Dec 06 '15 at 08:56
  • Peter, what do you mean by "buffers at the TCP level"? – smr Dec 06 '15 at 09:26
  • The Socket send and receive buffers. – Peter Lawrey Dec 06 '15 at 22:02
  • 1
    When you write to a `java.net.Socket` you *can't* 'check to see how much data you wrote'. `write()` returns `void`. And it writes everything you supplied, or throws an `IOException.` – user207421 Dec 06 '15 at 23:05
0

I just copied a working example ...

No you didn't. You made up something completely different.

    int len = arr.length;
    for (int i = 0; i < arr.length; i += BUFFER_SIZE)
        out.write(arr, i, Math.min(len - i, BUFFER_SIZE));

This loop is complete nonsense. It can be replaced completely by

out.write(arr, 0, len);

However you're just adding latency and wasting space with all this.
private static void writeObject(OutputStream out, Object obj) throws IOException { long t1 = System.currentTimeMillis(); out.writeObject(obj); out.close(); long t2 = System.currentTimeMillis();

    System.out.println(t2-t1);
}

There is no point in the ByteArrayOutputStream, and writing more bytes than are in it to the real ObjectOutputStream is simply invalid. There's no point in benchmarking operations that should never sanely take place.

Why you're closing the ObjectOutputStream is another mystery. And presumably you have similar code at the receiving side: reapcle it all with ObjectInputStream.readObject().

user207421
  • 305,947
  • 44
  • 307
  • 483
  • I think you are misreading my code! Why do you read `out.write(arr, i, Math.min(len - i, BUFFER_SIZE))` as writing one byte at a time?! – smr Dec 06 '15 at 09:25
  • Oh! EJP! You're a true careless man, friend! Read my code again please, and see how I was using the `BUFFER_SIZE = 64 * 1024`... Anyway, thank you for your attention. – smr Dec 08 '15 at 03:30