1
\$\begingroup\$

I need to cut the time it takes to print a large string by ~50%.

I went from using s1 + s2 to using StringBuilder, and instead of making several calls to System.out.print for each line, I create one large string by appending newlines to the StringBuilder, and then print that large string just once with System.out.

But my code is still way too slow (this is for a homework assignment and the code is not accepted by the automated hand-in system because it takes too long).

When the StringBuilder is printed, the string has ~100k lines

The relevant part of my code looks like this:

public class Main {
    public static void main (String[] args){
        MyClass builder = new MyClass();
        new ClassThatBuildsOnBuilder(builder);
        System.out.println(builder.str.toString());
    }
}
public class MyClass {
    private double[] coordinates = new double[]{0, 0, 0, 0};

    public StringBuilder str = new StringBuilder();
    private DecimalFormat formatter = new DecimalFormat("#0.0000");

    public MyClass(){

    }

    public void changeValues(double[] coordinates){
        this.coordinates = coordinates;
        updateRow();
    }

    private void updateRow(){
        for (double d : coordinates){
            str.append(formatter.format(d));
            str.append(" ");
        }
        str.append("\n");
    }

Can this even be optimized further? I feel like this should be very close to a lower bound.

\$\endgroup\$
  • \$\begingroup\$ add language tags? \$\endgroup\$ – ShapeOfMatter May 15 at 18:27
  • \$\begingroup\$ new ClassThatBuildsOnBuilder(builder); Does this line do anything? \$\endgroup\$ – dustytrash May 15 at 18:52
  • \$\begingroup\$ If you know the number of rows and the lenght of each row, you can calculate the final size of StringBuilder and specify it in the constructor to avoid unnecessary reallocations and array copies. Or at least you can estimate it to avoid most of them. \$\endgroup\$ – TorbenPutkonen May 16 at 6:36
4
\$\begingroup\$
  • Are you sure the bottleneck is the printing, as opposed to whatever new ClassThatBuildsOnBuilder(builder); is doing?
  • Is coordinates always going to have a width of four?
  • It appears you're building the whole string in memory, and then printing all your output at once at the end. While printing each line to standard-out individually may be slower, it also may take better advantages of the built-in output buffering. (Printing each word individually as you describe having originally done probably isn't a great idea.

Would something like this work?

  private void printRow(){
    System.out.format("#%.3f #%.3f #%.3f #%.3f%n",
      coordinates[0],
      coordinates[1],
      coordinates[2],
      coordinates[3]);

You could also try a Formatter, which would let you use a StringBuilder as you already are, but also use printf syntax.

Finally, I don't know how to take manual control of System.out's buffering behavior, but it's probably possible and may help.

\$\endgroup\$
0
\$\begingroup\$

I don't think buffering is the problem or solution. Creating a gigantic buffer probably hurts if the recipient has, say, a 4k buffer for reading the results. That would mean the recipient can't start working before your code has finished completely, making the process completely serial instead of parallel. Instead you should try creating a smaller buffer (with the initial size defined in the constructor, so it doesn't do unnecessary reallocations) and flush it regularly.

This is a bit of shooting in the dark as we don't know how the evaluator works.

In Java creating temporary Strings in tight loops is often a performance killer. Find the APIs that let you format numbers straight into a stream or with minimal temporary strings. DecimalFormat can write directly to a StringBuffer. Try using that method instead.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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