0

Why printing the array items in a loop taking more time as compared to appending all array items into StringBuffer and then print?

In this, I printed array items in the loop.

public static void main (String[] args) {
    Scanner scan=new Scanner(System.in);
    int count=scan.nextInt();
    for (int i=0;i<count;i++) {
        int arrlen=scan.nextInt();
        int rotate=scan.nextInt();
        int revarr[]=new int[arrlen];
        for (int j=0;j<arrlen;j++) {
            revarr[(arrlen-rotate+j)%arrlen]=scan.nextInt();
        }
        for (int j:revarr) {
            System.out.print(j+" ");
        }   
        System.out.println();
    }
}

In this, I appended array items into StringBuffer and then printed.

public static void main (String[] args) {
    Scanner scan=new Scanner(System.in);
    int count=scan.nextInt();
    for (int i=0;i<count;i++) {
        int arrlen=scan.nextInt();
        int rotate=scan.nextInt();
        int revarr[]=new int[arrlen];
        for(int j=0;j<arrlen;j++){
            revarr[(arrlen-rotate+j)%arrlen]=scan.nextInt();
        }
        StringBuffer s = new StringBuffer();
        for (int j:revarr){
            s.append(j+" ");
        }   
        System.out.println(s);
    }
}
5
  • How long are those strings in your case? How many iterations of the loop are there? Are we talking about tens of iteration or milions of iterations? Commented Apr 20, 2020 at 8:39
  • Because each print call is synchronized and invokes the underlying OS. --- FYI: Your code would be even faster if you used StringBuilder. Commented Apr 20, 2020 at 8:40
  • It is how many times you are using the output stream. in case of the StringBuffer you are calling only one time, but in the other case you are calling it many times and each contribute a decent time linit. Commented Apr 20, 2020 at 8:42
  • Calling print each time, that is like going shopping to buy 5 packages of toilet paper. But you only grab one package, walk to the cashier, pay, move it to the parking lot. Then you walk back, grab another one, pay it, move it to the car, repeat. Compared to: you taking a shopping cart, and putting 5 packages of toilet paper into the cart, moving to the cashier, paying once, moving everything at once to the car. Are you really surprised that doing two things completely different takes different amounts of time? In other words: the real point is to question your assumptions here ... Commented Apr 20, 2020 at 9:22
  • "Because each print call ... invokes the underlying OS" - That is not exactly true. There will only be a call to the operating when System.out's buffer fills, or println is called. Commented Apr 20, 2020 at 12:24

3 Answers 3

1

The probable reason is to do with the implementation of the PrintStream that System.out is set to (by default).

In Java 11, the method that creates the PrintStream is as follows:

private static PrintStream newPrintStream(FileOutputStream fos, String enc) {
   if (enc != null) {
        try {
            return new PrintStream(new BufferedOutputStream(fos, 128), true, enc);
        } catch (UnsupportedEncodingException uee) {}
    }
    return new PrintStream(new BufferedOutputStream(fos, 128), true);
}

The key line is this:

return new PrintStream(new BufferedOutputStream(fos, 128), true, enc);

Firstly, it is creating a BufferedOutputStream with a buffer size of 128 bytes. That is small. If you are doing small writes to System.out, there will be a buffer flush at least every 128 byte written.

Secondly, the true parameter is enabling autoflushing. The javadoc describes this as follows:

"If true, the output buffer will be flushed whenever a byte array is written, one of the println methods is invoked, or a newline character or byte ('\n') is written"

Again, this means that there is more flushing.

Why does this make a difference?

Well flushing involves performing a fwrite syscall to write characters out. Syscalls are relatively expensive. According to some numbers I have seen, the syscall overhead of a fwrite syscall is in the order of 60 to 350 ns. That is not huge, but if you do that a few times per loop iteration compared with once per loop iteration, and you repeat that for long enough, the difference could be significant.

There could also be overheads that depend on what System.out is connected to. For example, if you are writing to a console, then lots of small writes could slow down the console application.


Another explanation is possible. The benchmark code that you have shown doesn't take any account of possible JVM warmup effects. For example, it could be that one example is triggering JIT compilation while the other isn't. The overheads of a JIT compilation could result in the former taking longer than the latter.

Sign up to request clarification or add additional context in comments.

Comments

0

Printing into System.out is relatively a rather long operation, and adding to StringBuffer is quick. StringBuffer is intended to concatenate Strings. However, StringBuffer is obsolete. Use StringBuilder instead

1 Comment

"was" being the operative word. No one should be using StringBuffer anymore.
0

It is very common that input and output operations take a long time in Java. Each time you call print or println it generates an additional overhead that is much more time consuming than the actual printing, that is why it's actually much faster to print it all in one batch.
This is the method used by print and println:

    private void write(String s) {
        try {
            synchronized(this) {
                this.ensureOpen();
                this.textOut.write(s);
                this.textOut.flushBuffer();
                this.charOut.flushBuffer();
                if (this.autoFlush && s.indexOf(10) >= 0) {
                    this.out.flush();
                }
            }
        } catch (InterruptedIOException var5) {
            Thread.currentThread().interrupt();
        } catch (IOException var6) {
            this.trouble = true;
        }

    }

As you can see, it is a synchronized operation. Synchronization in Java hinders the performance as it takes a lot of time to acquire and release locks, and the code in the synchronized block is not optimized by the compiler.

You could get an even faster time if you use StringBuilder instead of StringBuffer, as it is not synchronized and you'll have a smaller overhead.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.