简体   繁体   中英

Java Program's Performance question

I have a java program with following logic:

i) Open a socket server and wait for messages from client

ii) Read messages received(fixed length records of about 233 bytes).

iii) on each message receive, call a process function.

iv) process function does follwing
 - add the record to the string builder.

 - if (length of stringbuilder > configured buffer size) {

   process this buffer

   } else {

      add the new record to buffer

   }

now when I try this program with 1 million records, each 233 bytes, it takes about 5 min 30 sec to finish. I want to reduce this time. Bulk of the time here is being wasted in this process function.

I was checking if I could get some advice on how to re-organize this process() to get better performance. My use case is to get the records and read them until they reach a configured buffer size ( like 50 Mb or 500 MB or 1 GB). Once they reach this size, process it and write to a file system.

Some back of envelope sanity checks:

  • You are sending 233*1M bytes=1.864Gb over a socket, this is will take different amounts of time depending on your bandwidth and NIC but to sketch some baseline figures if you got a 100Mb card you are looking at 20 seconds right there before any real network latency hit you. In reality this is likely to be much higher unless you are on localhost or on great hardware and connectivity.
  • Encoding 233 bytes into a String takes roughly 260 ns(on my machine) using new String(bytes[]) so you are looking at 1M*0.25us = 250 millis.

So off the bat bytes to String is not the issue, and I would also guess StringBuilder.append is not that bad(it copies the char array in). You will be churning allot of memory for all those strings and byte buffers and that may cause some slow down. To avoid the memory churn you can use Charset.newDecoder to get a decoder(keep and reuse) and write the ByteBuffer you get off the wire directly into a reusable CharBuffer. You mention some formatting of the strings, this can be costly when done wrong but unless you are doing something complex I wouldn't think that's the problem.

A more likely cause for your issue is network latency, you can test that theory by writing a dummy program which just reads the data and throws it away. Another likely suspect is the part of your code which writes to the file or to disk, but with no further info it's hard to help. Also, if you are new to profiling you can use hand rolled timing to verify your theories:

long start = System.nanotime();
process(data);
long took = System.nanotime() - start;

Sum up how long you spent in process and you got an idea of where the time went.

How many cores do you have on your system? You can create a threadpool with a number of threads equivalent to the number of cores on your system and create a runnable that calls process inside a thread within the pool. That might speed things up a bit for you.

We'll need more info on processing if you only have one core. Is your CPU pegged in your test? What OS, etc?

A couple of "micro-optimisations":

  1. Create your StringBuilder with the correct initial size(eg 1GB).
  2. Do not recreate the StringBuilder each time; reuse it by setting setLength(0)

But I'm not sure such micro-optimisations will have much impact. Maybe you could post more of your code?

  • Why is the data stored in the intermediate buffer? If all you are doing is writing it to a filesystem would it be better just to write one record at a time using a BufferedWriter?
  • Do the messages have to be processed in the order they arrived? If not you could use an ExecutorService to parallelize the processing.

I suspect that most of the 5:30 mins can be chalked up to network communication overheads of one form or another. I'd recommend the following (most important first):

  • If you are using UDP, switch to TCP. For something like this you are likely to get better throughput with a stream-based transport than a message-based transport.

  • On the client and server ends, make sure that you have wrapped the socket streams with buffered streams.

  • If the client and server are on the same host, use a loopback IP address (eg 127.0.0.1).

  • If the server-side processing is CPU intensive (and you have multiple cores), do the processing in a separate thread to the thread that reads the messages.

  • Consider using NIO for reading / writing the data.

  • Consider not converting the data to character form on the server side... though if it is really text, this would make processing difficult.


And before you do any of that, profile your client and server-side applications and see if that reveals any unexpected bottlenecks.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM