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.
2 Answers 2
- 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.
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.
new ClassThatBuildsOnBuilder(builder);
Does this line do anything? \$\endgroup\$