23

I know output to the console is a costly operation. In the interest of code readability sometimes it is nice to call a function to output text twice, rather than having a long string of text as an argument.

For example how much less efficient is it to have

System.out.println("Good morning.");
System.out.println("Please enter your name");

vs.

System.out.println("Good morning.\nPlease enter your name");

In the example the difference is only one call to println() but what if it's more?

On a related note, statements involving printing text can look strange while viewing the source code if the text to print is long. Assuming the text itself can't be made shorter, what can be done? Should this be a case where multiple println()calls be made? Someone once told me a line of code shouldn't be more than 80 characters (IIRC) so what would you do with

System.out.println("Good morning everyone. I am here today to present you with a very, very lengthy sentence in order to prove a point about how it looks strange amongst other code.");

Is the same true for languages such as C/C++ since each time data is written to an output stream a system call must be made and the process must go to kernel mode (which is very costly)?

6
  • Even though this is very little code, I have to say I have been wondering the same thing. Would be nice to determine the answer to this once and for all Commented Jul 1, 2014 at 0:08
  • @SimonAndréForsberg I'm not sure if it's applicable to Java because it runs on a virtual machine, but in lower level languages such as C/C++ I would imagine it would be costly as each time something writes to an output stream, a system call must be made.
    – Celeritas
    Commented Jul 1, 2014 at 0:10
  • There's this to consider too: stackoverflow.com/questions/21947452/…
    – h.j.k.
    Commented Jul 1, 2014 at 2:04
  • 2
    I have to say that I don't see the point here. When interacting with a user via terminal, I can't imagine any performance issues because usually there is not that much to print. And applications with an GUI or an webapp should write to a log-file (usually using a framework).
    – Andy
    Commented Jul 1, 2014 at 19:03
  • 2
    If you are saying good morning, you do it once or twice a day. Optimization is not a concern. If its something else, you need to profile to know if its a problem. The code I work on the logging slows the code to unusable unless you build a multi-line buffer and dump the text in one call.
    – mattnz
    Commented Jul 2, 2014 at 6:46

4 Answers 4

30

There are two 'forces' here, in tension: Performance vs. Readability.

Let's tackle the third problem first though, long lines:

System.out.println("Good morning everyone. I am here today to present you with a very, very lengthy sentence in order to prove a point about how it looks strange amongst other code.");

The best way to implement this and keep readibility, is to use string concatenation:

System.out.println("Good morning everyone. I am here today to present you "
                 + "with a very, very lengthy sentence in order to prove a "
                 + "point about how it looks strange amongst other code.");

The String-constant concatenation will happen at compile time, and will have no effect on performance at all. The lines are readable, and you can just move on.

Now, about the:

System.out.println("Good morning.");
System.out.println("Please enter your name");

vs.

System.out.println("Good morning.\nPlease enter your name");

The second option is significantly faster. I will suggest about 2X as fast.... why?

Because 90% (with a wide margin of error) of the work is not related to dumping the characters to the output, but is overhead needed to secure the output to write to it.

Synchronization

System.out is a PrintStream. All Java implementations that I know of, internally synchronize the PrintStream: See the code on GrepCode!.

What does this mean for your code?

It means that each time you call System.out.println(...) you are synchronizing your memory model, you are checking and waiting for a lock. Any other threads calling System.out will also be locked.

In single-threaded applications the impact of System.out.println() is often limited by the IO performance of your system, how fast can you write out to file. In multithreaded applications, the locking can be more of an issue than the IO.

Flushing

Each println is flushed. This causes the buffers to be cleared and triggers a Console-level write to the buffers. The amount of effort done here is implementation dependant, but, it is generally understood that the performance of the flush is only in small part related to the size of the buffer being flushed. There is a significant overhead related to the flush, where memory buffers are marked as dirty, the Virtual machine is performing IO, and so on. Incurring that overhead once, instead of twice, is an obvious optimization.

Some numbers

I put together the following little test:

public class ConsolePerf {

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            benchmark("Warm " + i);
        }
        benchmark("real");
    }

    private static void benchmark(String string) {
        benchString(string + "short", "This is a short String");
        benchString(string + "long", "This is a long String with a number of newlines\n"
                  + "in it, that should simulate\n"
                  + "printing some long sentences and log\n"
                  + "messages.");
        
    }
    
    private static final int REPS = 1000;

    private static void benchString(String name, String value) {
        long time = System.nanoTime();
        for (int i = 0; i < REPS; i++) {
            System.out.println(value);
        }
        double ms = (System.nanoTime() - time) / 1000000.0;
        System.err.printf("%s run in%n    %12.3fms%n    %12.3f lines per ms%n    %12.3f chars per ms%n",
                name, ms, REPS/ms, REPS * (value.length() + 1) / ms);
        
    }

    
}

The code is relatively simple, it repeatedly prints either a short, or a long string to output. The long String has multiple newlines in it. It measures how long it takes to print 1000 iterations of each.

If I run it at the unix (Linux) command-prompt, and redirect the STDOUT to /dev/null, and print the actual results to STDERR, I can do the following:

java -cp . ConsolePerf > /dev/null 2> ../errlog

The output (in errlog) looks like:

Warm 0short run in
           7.264ms
         137.667 lines per ms
        3166.345 chars per ms
Warm 0long run in
           1.661ms
         602.051 lines per ms
       74654.317 chars per ms
Warm 1short run in
           1.615ms
         619.327 lines per ms
       14244.511 chars per ms
Warm 1long run in
           2.524ms
         396.238 lines per ms
       49133.487 chars per ms
.......
Warm 99short run in
           1.159ms
         862.569 lines per ms
       19839.079 chars per ms
Warm 99long run in
           1.213ms
         824.393 lines per ms
      102224.706 chars per ms
realshort run in
           1.204ms
         830.520 lines per ms
       19101.959 chars per ms
reallong run in
           1.215ms
         823.160 lines per ms
      102071.811 chars per ms

What does this mean? Let me repeat the last 'stanza':

realshort run in
           1.204ms
         830.520 lines per ms
       19101.959 chars per ms
reallong run in
           1.215ms
         823.160 lines per ms
      102071.811 chars per ms

It means that, for all intents and purposes, even though the 'long' line is about 5-times longer, and contains multiple newlines, it takes just about as long to output as the short line.

The number of characters-per-second for the long run is 5 times as much, and the elapsed time is about the same.....

In other words, your performance scales relative to the number of printlns you have, not what they print.

Update: What happens if you redirect to a file, instead of to /dev/null?

realshort run in
           2.592ms
         385.815 lines per ms
        8873.755 chars per ms
reallong run in
           2.686ms
         372.306 lines per ms
       46165.955 chars per ms

It is a whole lot slower, but the proportions are about the same....

7
  • Added some performance numbers.
    – rolfl
    Commented Jul 1, 2014 at 2:43
  • You also have to consider the problem that "\n" may not be the right line terminator. println will automatically terminate the line with the right character(s), but sticking a \n into your string directly can cause issues. If you want to do it right, you might need to use string formatting or the line.separator system property. println is much cleaner. Commented Jul 1, 2014 at 3:56
  • 3
    This is all great analysis so +1 for sure, but I'd argue that once you are committed to console output, these minor performance differences fly out the window. If your program's algorithm runs faster than outputting the results (at this small level of output), you can print each character one-by-one and not notice the difference. Commented Jul 1, 2014 at 4:32
  • I believe this is a difference between Java and C/C++ that output is synchronized. I say this because I recall writing a multithreaded program and having issues with garbled output if different threads try to write to write to the console. Can anyone verify this?
    – Celeritas
    Commented Jul 1, 2014 at 5:09
  • 6
    It's important to also remember that none of that speed matters at all when put right next to function that waits on user input.
    – vmrob
    Commented Jul 1, 2014 at 8:41
2

I don't think having a bunch of printlns is a design issue at all. The way I see it is that this can clearly be done with static code analyzer if it is really a problem.

But it is not a problem because most people don't do IOs like this. When they really need to do lot of IOs, they use buffered ones (BufferedReader, BufferedWriter, etc..) when the input is buffered, you will see that the performance is similar enough, that you don't need to worry about having a bunch of println or few println.

So to answer the original question. I would say, not bad if you use println to print a few things out as most people would use println for.

1

In higher level languages like C and C++, this is less of a problem than in Java.

First of all, C and C++ define compile-time string concatenation, so you can so something like:

std::cout << "Good morning everyone. I am here today to present you with a very, "
    "very lengthy sentence in order to prove a point about how it looks strange "
    "amongst other code.";

In such a case, concatenating the string isn't just an optimization you can pretty much, usually (etc.) depend on the compiler to make. Rather, it's directly required by the C and C++ standards (phase 6 of translation: "Adjacent string literal tokens are concatenated.").

Though it's at the expense of a little extra complexity in the compiler and implementation, C and C++ do a bit more to hide the complexity of producing output efficiently from the programmer. Java is much more like assembly language--each call to System.out.println translates much more directly to a call to the underlying operating to write the data to the console. If you want buffering to improve efficiency, that has to be provided separately.

This means, for example, that in C++, rewriting the previous example, to something like this:

std::cout << "Good morning everyone. I am here today to present you with a very, ";
std::cout << "very lengthy sentence in order to prove a point about how it looks ";       
std::cout << "strange amongst other code.";

...would normally1 have almost no effect on efficiency. Each use of cout would simply deposit data into a buffer. That buffer would be flushed to the underlying stream when the buffer filled up, or the code tried to read input from the use (such as with std::cin).

iostreams also have a sync_with_stdio property that determines whether output from iostreams is synchronized with C-style input (e.g., getchar). By default sync_with_stdio is set to true, so if, for example, you write to std::cout, then read via getchar, the data you wrote to cout will be flushed when getchar is called. You can set sync_with_stdio to false to disable that (usually done to improve performance).

sync_with_stdio also controls a degree of synchronization between threads. If synchronization is turned on (the default) writing to an iostream from multiple threads can result in the data from the threads being interleaved, but prevents any race conditions. IOW, your program will run and produce output, but if more than one thread writes to a stream at a time, the arbitrary intermixing of the data from the different threads will typically render the output pretty useless.

If you turn off the synchronization, then synchronizing access from multiple threads becomes entirely your responsibility as well. Concurrent writes from multiple threads can/will lead to a data race, which means the code has undefined behavior.

Summary

C++ defaults to an attempt at balancing speed with safety. The result is fairly successful for single-threaded code, but less so for multi-threaded code. Multithreaded code typically needs to ensure that only one thread writes to a stream at a time to produce useful output.


1. It's possible to turn off buffering for a stream, but actually doing so is pretty unusual, and when/if somebody does it, it's probably for a fairly specific reason, such as ensuring that all output is captured immediately despite the effect on performance. In any case, this only happens if the code does it explicitly.

16
  • 13
    "In higher level languages like C and C++, this is less of a problem than in Java." - what? C and C++ are lower-level languages than Java. Also, you forgot your line terminators. Commented Jul 1, 2014 at 3:57
  • 1
    Throughout I point to the objective basis for Java being the lower level language. Not sure what line terminators you're talking about. Commented Jul 1, 2014 at 4:27
  • 2
    Java also does compile-time concatenation. For example, "2^31 - 1 = " + Integer.MAX_VALUE is stored as a single interned string (JLS Sec 3.10.5 and 15.28). Commented Jul 1, 2014 at 9:44
  • 2
    @200_success: Java doing the string concatenation at compile time seems to come down to §15.18.1: "The String object is newly created (§12.5) unless the expression is a compile-time constant expression (§15.28)." This seems to allow but not require that the concatenation be done at compile time. I.e., the result must be newly created if the inputs are not compile-time constants, but no requirement is made in either direction if they are compile time constants. To require compile-time concatenation, you'd have to read its (implied) "if" as really meaning "if and only if". Commented Jul 1, 2014 at 14:13
  • 2
    @Phoshi: Try with resources isn't even vaguely similar to RAII. RAII allows the class to manage the resources, but try with resources requires the client code to manage the resources. The features (abstractions, more accurately) that one has and the other lacks are entirely relevant--in fact, they're exactly what makes one language higher level than another. Commented Jul 2, 2014 at 13:04
1

While the performance is not really an issue here, the bad readability of a bunch of println statements points to a missing design aspect.

Why do we write a sequence of many println statements? If it were just one fixed text block, like a --help text in a console command, it would be much better to have it as a separate ressource and to read it in and write it to the screen on request.

But usually it is a mixture of dynamic and static parts. Let's say we have some bare order data on the one hand, and some fixed static text parts on the other hand, and these things have to be mixed together to form an order confirmation sheet. Again, also in this case, it is better to have a separate ressource text file: The ressource would be a template, containing some kind of symbols (placeholders), which are replaced at runtime by the actual order data.

The separation of the programming language from the natural language has many advantages - among these is internationalization: You might have to translate the text if you want to become multilangual with your software. Also, why should a compile step necessary if you only want to have a textual correction, say fix some misspelling.

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.