Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PrintWriter vs PrintStream vs OutputStreamWriter timecosts

As you know we have several tools in java for writing data into streams.
In this sample code I have compared them by runtime.
Can somebody explain it exactly? Thanks.
Here is the code:

import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.io.PrintWriter;

public class IOtests
{

public static void main(String[] args) throws Exception
{
    char[] chars = new char[100];
    byte[] bytes = new byte[100];
    for (int i = 0; i < 100; i++)
    {
        chars[i] = (char) i;
        bytes[i] = (byte) i;
    }
    OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(
            "output.txt"));
    long a = System.currentTimeMillis();
    for (int i = 0; i < 100000; i++)
        for (char j : chars)
            out.write(j);
    System.out.println("OutputStreamWriter writing characters: "
            + (System.currentTimeMillis() - a));
    out = new OutputStreamWriter(new FileOutputStream("output.txt"));
    a = System.currentTimeMillis();
    for (int i = 0; i < 100000; i++)
        for (byte j : bytes)
            out.write(j);
    System.out.println("OutputStreamWriter writing bytes: "
            + (System.currentTimeMillis() - a));
    PrintStream out1 = new PrintStream("output.txt");
    a = System.currentTimeMillis();
    for (int i = 0; i < 100000; i++)
        for (char j : chars)
            out1.write(j);
    System.out.println("PrintStream writing characters: "
            + (System.currentTimeMillis() - a));
    out1 = new PrintStream("output.txt");
    a = System.currentTimeMillis();
    for (int i = 0; i < 100000; i++)
        for (byte j : bytes)
            out1.write(j);
    System.out.println("PrintStream writing bytes: "
            + (System.currentTimeMillis() - a));
    PrintWriter out2 = new PrintWriter("output.txt");
    a = System.currentTimeMillis();
    for (int i = 0; i < 100000; i++)
        for (char j : chars)
            out2.write(j);
    System.out.println("PrintWriter writing characters: "
            + (System.currentTimeMillis() - a));
    out1 = new PrintStream("output.txt");
    a = System.currentTimeMillis();
    for (int i = 0; i < 100000; i++)
        for (byte j : bytes)
            out2.write(j);
    System.out.println("PrintWriter writing bytes: "
            + (System.currentTimeMillis() - a));
}

}

Results:

OutputStreamWriter writing characters: 4141
OutputStreamWriter writing bytes: 3546
PrintStream writing characters: 86516
PrintStream writing bytes: 70484
PrintWriter writing characters: 938
PrintWriter writing bytes: 2484

Note that all times are in milliseconds.

like image 661
Alireza Mohamadi Avatar asked Aug 25 '13 16:08

Alireza Mohamadi


People also ask

What is the difference between PrintStream and PrintWriter?

The primary difference is that PrintStream writes raw bytes in the machine's native character format, and PrintWriter converts bytes to recognized encoding schemes. Thus, files created with PrintWriter are more compatible across different platforms than files created with PrintStream .

Is PrintWriter faster than PrintStream?

PrintStreams can allow more flexibility with encoding. I'm guessing that some system encodings are used, but I'm not sure. PrintWriter is also about twice as fast for printing text.


1 Answers

I've reduced your question to its essence:

public class Test {
  static byte[] bytes = new byte[10_000_000];
  static {
    for (int i = 0; i < bytes.length; i++) bytes[i] = (byte) (i%100+32);
  }
  public static void main(String[] args) throws Exception {
    writer(true);
    writer(false);
    stream(true);
    stream(false);
  }

  static void writer(boolean flush) throws IOException {
    Writer out = new FileWriter("output.txt");
    long a = System.currentTimeMillis();
    for (byte j : bytes) {
      out.write(j);
      if (flush) out.flush();
    }
    out.close();
    System.out.println("FileWriter with" + (flush? "":"out") + " flushing: " +
        (System.currentTimeMillis() - a));
  }
  static void stream(boolean flush) throws IOException {
    OutputStream out = new FileOutputStream("output.txt");
    long a = System.currentTimeMillis();
    for (byte j : bytes) {
      out.write(j);
      if (flush) out.flush();
    }
    out.close();
    System.out.println("FileOutputStream with" + (flush? "":"out") + " flushing: " +
        (System.currentTimeMillis() - a));
  }
}

Notes:

  • properly closing the resources when done;
  • double loop replaced by single loop, but a larger array;
  • avoid writing control characters to evade autoflush behavior;
  • only using byte array since you are testing only one method in all cases: write(int). Therefore it makes no difference whether you are using bytes or chars;
  • removed everything except a FileWriter and a FileOutputStream because all other cases boil down to these two;
  • testing both writer and output stream in two modes: flush after each write, and don't flush at all until close.

Now, when you run this, you'll get output like the following:

FileWriter with flushing: 28235
FileWriter without flushing: 828
FileOutputStream with flushing: 23984
FileOutputStream without flushing: 23641

So, what's the lesson?

  • all writers are buffered because internally they delegate to StreamEncoder which is itself buffered;
  • FileOutputStream is not buffered;
  • non-buffered writing byte-by-byte is very slow.

Good practices demand that you always do buffered writing: either using buffered sinks, or maintaining an explicit buffer on your side.

like image 191
Marko Topolnik Avatar answered Sep 28 '22 15:09

Marko Topolnik