SOURABH GUPTA
SOURABH GUPTA

Reputation: 55

StringBuffer vs normal print

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);
    }
}

Upvotes: 0

Views: 278

Answers (3)

Stephen C
Stephen C

Reputation: 719709

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.

Upvotes: 1

Radu Mirea
Radu Mirea

Reputation: 420

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.

Upvotes: 0

HoRn
HoRn

Reputation: 1518

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

Upvotes: 0

Related Questions