Berin Loritsch a écrit :
> 
> Friends, going through the source code looking for
> places to optimize the critical path (i.e. the path
> taken when no error or log message is performed),
> I went through adding StringBuffers where there were
> three or more strings to concatenate.
> 
> I found an enigma.
> 
> In several places, where the code was already using
> a StringBuffer, I found code snippets like this:
> 
> buffer.append("<" + prefix + ":" + name + "/>");
> 
> This gets expanded into the much more complex code as
> follows:
> 
> buffer.append(
>     new StringBuffer(
>         new StringBuffer(
>             new StringBuffer(
>                 new StringBuffer("<").append(prefix).toString()
>             ).append(":").toString()
>         ).append(name).toString()
>     ).append("/>").toString()
> );
> 
> That means four additional StringBuffers were created
> behind the scenes.  This is ludicrous when we are already
> using StringBuffers--and wasteful of resources.
> 
> Instead, please write the code to explicitly take advantage
> of the StringBuffer when it is available:
> 
> buffer.append("<").append(prefix).append(":").append(name).append("/>");
> 
> This is more respectful of the environment, and is much quicker
> to execute on any JVM.
> 
> Please, be mindful in the future.

Berin,

Having once looked at the generated bytecode for string concatenation, I
came to the conclusion that replacing "+" on Strings by append() on
StringBuffers is really not worth it as far as performance is concerned,
apart of course in the above case which involves concatenation in
appends (and a strange compiler behaviour).

Take the following example. The two methods do exactly the same thing,
one with String concatenation, and the other one with
StringBuffer.append().

package proto;
public class TestSBuff {

    public void test1(int i, String msg) {
        System.out.println("The value "
            + "of i is "
            + i
            + " and the value of msg is '"
            + msg
            + "'");
    }
    
    public void test2(int i, String msg) {
        System.out.println(new StringBuffer("The value ")
            .append("of i is ")
            .append(i)
            .append(" and the value of msg is '")
            .append(msg)
            .append("'")
            .toString());
    }
}

Here's the resulting bytecode, output with javap -c :

Compiled from TestSBuff.java
public class proto.TestSBuff extends java.lang.Object {
    public proto.TestSBuff();
    public void test1(int, java.lang.String);
    public void test2(int, java.lang.String);
}

Method proto.TestSBuff()
   0 aload_0
   1 invokespecial #1 <Method java.lang.Object()>
   4 return

Method void test1(int, java.lang.String)
   0 getstatic #2 <Field java.io.PrintStream out>
   3 new #3 <Class java.lang.StringBuffer>
   6 dup
   7 invokespecial #4 <Method java.lang.StringBuffer()>
  10 ldc #5 <String "The value of i is ">
  12 invokevirtual #6 <Method java.lang.StringBuffer
append(java.lang.String)>
  15 iload_1
  16 invokevirtual #7 <Method java.lang.StringBuffer append(int)>
  19 ldc #8 <String " and the value of msg is '">
  21 invokevirtual #6 <Method java.lang.StringBuffer
append(java.lang.String)>
  24 aload_2
  25 invokevirtual #6 <Method java.lang.StringBuffer
append(java.lang.String)>
  28 ldc #9 <String "'">
  30 invokevirtual #6 <Method java.lang.StringBuffer
append(java.lang.String)>
  33 invokevirtual #10 <Method java.lang.String toString()>
  36 invokevirtual #11 <Method void println(java.lang.String)>
  39 return

Method void test2(int, java.lang.String)
   0 getstatic #2 <Field java.io.PrintStream out>
   3 new #3 <Class java.lang.StringBuffer>
   6 dup
   7 ldc #12 <String "The value ">
   9 invokespecial #13 <Method java.lang.StringBuffer(java.lang.String)>
  12 ldc #14 <String "of i is ">
  14 invokevirtual #6 <Method java.lang.StringBuffer
append(java.lang.String)>
  17 iload_1
  18 invokevirtual #7 <Method java.lang.StringBuffer append(int)>
  21 ldc #8 <String " and the value of msg is '">
  23 invokevirtual #6 <Method java.lang.StringBuffer
append(java.lang.String)>
  26 aload_2
  27 invokevirtual #6 <Method java.lang.StringBuffer
append(java.lang.String)>
  30 ldc #9 <String "'">
  32 invokevirtual #6 <Method java.lang.StringBuffer
append(java.lang.String)>
  35 invokevirtual #10 <Method java.lang.String toString()>
  38 invokevirtual #11 <Method void println(java.lang.String)>
  41 return

Two things are shown above :

* String concatenation is translated to StringBuffer.append()
instructions. The only difference with direct StringBuffer code is that
the default no-arg StringBuffer() constructor is called and the first
string is appended like others. I'm not sure this small benefit
justifies the decreased code readability (and the increased keyboard
typing ;)

* Constant String concatenation (like "The value " + "of i is ") isn't
translated to individual StringBuffer.append(), but to a single String
constant ("The value of i is "). In that case, performance is better
with String concatenation. This means some of the changes you made on
code where constant strings are concatenated to allow line-splitting
(see for example WildcardURIMatcherFactory) are in fact slowing down the
code !

The conclusion is that StringBuffer is only worth it when the
concatenation occurs in several statements, or iteratively such as in a
"for" loop. Otherwhise, single-statement String concatenations can be
considered equivalent to their StringBuffer translation, but produce
less readable code.

Sylvain.

-- 
Sylvain Wallez
Anyware Technologies - http://www.anyware-tech.com

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, email: [EMAIL PROTECTED]

Reply via email to